class DistributedObjectGlobalAI(DistributedObjectAI): notify = directNotify.newCategory('DistributedObjectGlobalAI') doNotDeallocateChannel = 1 isGlobalDistObj = 1 def __init__(self, air): DistributedObjectAI.__init__(self, air) def announceGenerate(self): DistributedObjectAI.announceGenerate(self) try: if not self.doNotListenToChannel: self.air.registerForChannel(self.doId) except AttributeError: self.air.registerForChannel(self.doId) return False def delete(self): DistributedObjectAI.delete(self) try: if not self.doNotListenToChannel: self.air.unregisterForChannel(self.doId) except AttributeError: self.air.unregisterForChannel(self.doId)
class Factory: """This class manages a list of object types and their corresponding constructors. Objects may be created on-demand from their type. Object types may be any hashable piece of unique data (such as a string). This class is intended to be derived from. Subclasses should call self._registerTypes to set up type constructors.""" notify = directNotify.newCategory('Factory') def __init__(self): self._type2ctor = {} def create(self, type, *args, **kwArgs): return self._type2ctor[type](*args, **kwArgs) def _registerType(self, type, ctor): if self._type2ctor.has_key(type): self.notify.debug('replacing %s ctor %s with %s' % (type, self._type2ctor[type], ctor)) self._type2ctor[type] = ctor def _registerTypes(self, type2ctor): for type, ctor in type2ctor.items(): self._registerType(type, ctor) def nullCtor(self, *args, **kwArgs): return None
class DistributedObjectGlobal(DistributedObject): """ The Distributed Object Global class is the base class for global network based (i.e. distributed) objects. """ notify = directNotify.newCategory("DistributedObjectGlobal") # A few objects will set neverDisable to 1... Examples are # localToon, and anything that lives in the UberZone. This # keeps them from being disabled when you change zones, # even to the quiet zone. neverDisable = 1 def __init__(self, cr): assert self.notify.debugStateCall(self) DistributedObject.__init__(self, cr) self.parentId = 0 self.zoneId = 0
def checkForGarbageLeaks(): gc.collect() numGarbage = len(gc.garbage) if (numGarbage > 0 and (not config.GetBool('disable-garbage-logging', 1))): if (numGarbage != _CFGLGlobals.LastNumGarbage): print gr = GarbageReport('found garbage', threaded=False, collect=False) print _CFGLGlobals.LastNumGarbage = numGarbage _CFGLGlobals.LastNumCycles = gr.getNumCycles() messenger.send(GarbageCycleCountAnnounceEvent, [gr.getDesc2numDict()]) gr.destroy() notify = directNotify.newCategory("GarbageDetect") if config.GetBool('allow-garbage-cycles', 1): func = notify.warning else: func = notify.error func('%s garbage cycles found, see info above' % _CFGLGlobals.LastNumCycles) return numGarbage
class DistributedObjectOV(DistributedObjectBase): """ Implementation of the 'owner view' (OV) of a distributed object; """ notify = directNotify.newCategory("DistributedObjectOV") def __init__(self, cr): assert self.notify.debugStateCall(self) try: self.DistributedObjectOV_initialized except: self.DistributedObjectOV_initialized = 1 DistributedObjectBase.__init__(self, cr) # Keep track of our state as a distributed object. This # is only trustworthy if the inheriting class properly # calls up the chain for disable() and generate(). self.activeState = ESNew if __debug__: def status(self, indent=0): """ print out "doId(parentId, zoneId) className" and conditionally show generated, disabled """ spaces = ' ' * (indent + 2) try: print "%s%s:" % (' ' * indent, self.__class__.__name__) print "%sfrom DistributedObjectOV doId:%s, parent:%s, zone:%s" % ( spaces, self.doId, self.parentId, self.zoneId), flags = [] if self.activeState == ESGenerated: flags.append("generated") if self.activeState < ESGenerating: flags.append("disabled") if len(flags): print "(%s)" % (" ".join(flags), ), print except Exception, e: print "%serror printing status" % (spaces, ), e
def detectLeaks(self): if not __dev__: return # call this after the DirectObject instance has been destroyed # if it's leaking, will notify user # make sure we're not still listening for messenger events events = messenger.getAllAccepting(self) # make sure we're not leaking tasks # TODO: include tasks that were added directly to the taskMgr tasks = [] if hasattr(self, '_taskList'): tasks = [task.name for task in self._taskList.values()] if len(events) or len(tasks): estr = choice(len(events), 'listening to events: %s' % events, '') andStr = choice(len(events) and len(tasks), ' and ', '') tstr = choice(len(tasks), '%srunning tasks: %s' % (andStr, tasks), '') notify = directNotify.newCategory('LeakDetect') func = choice(getRepository()._crashOnProactiveLeakDetect, self.notify.error, self.notify.warning) func('destroyed %s instance is still %s%s' % (self.__class__.__name__, estr, tstr))
import direct from panda3d.pandac import HttpRequest from panda3d.direct.directnotify.DirectNotifyGlobal import directNotify from panda3d.direct.task.TaskManagerGlobal import taskMgr from panda3d.direct.task import Task from LandingPage import LandingPage notify = directNotify.newCategory('WebRequestDispatcher') class WebRequest(object): """ Pointer to a single web request (maps to an open HTTP socket). An instance of this class maps to a single client waiting for a response. connection is an instance of libdirect.HttpRequest """ def __init__(self, connection): self.connection = connection def getURI(self): return self.connection.GetRequestURL() def getRequestType(self): return self.connection.GetRequestType() def dictFromGET(self): result = {} for pair in self.connection.GetRequestOptionString().split('&'): arg = pair.split('=', 1) if len(arg) > 1:
class GravityWalker(DirectObject.DirectObject): notify = directNotify.newCategory("GravityWalker") wantDebugIndicator = base.config.GetBool('want-avatar-physics-indicator', 0) wantFloorSphere = base.config.GetBool('want-floor-sphere', 0) earlyEventSphere = base.config.GetBool('early-event-sphere', 0) DiagonalFactor = math.sqrt(2.) / 2. # special methods def __init__(self, gravity=64.348, standableGround=0.707, hardLandingForce=16.0): assert self.notify.debugStateCall(self) DirectObject.DirectObject.__init__(self) self.__gravity = gravity self.__standableGround = standableGround self.__hardLandingForce = hardLandingForce self.mayJump = 1 self.jumpDelayTask = None self.controlsTask = None self.indicatorTask = None self.falling = 0 self.needToDeltaPos = 0 self.physVelocityIndicator = None self.avatarControlForwardSpeed = 0 self.avatarControlJumpForce = 0 self.avatarControlReverseSpeed = 0 self.avatarControlRotateSpeed = 0 self.getAirborneHeight = None self.priorParent = Vec3(0) self.__oldPosDelta = Vec3(0) self.__oldDt = 0 self.moving = 0 self.speed = 0.0 self.rotationSpeed = 0.0 self.slideSpeed = 0.0 self.vel = Vec3(0.0) self.collisionsActive = 0 self.isAirborne = 0 self.highMark = 0 """ def spawnTest(self): assert self.notify.debugStateCall(self) if not self.wantDebugIndicator: return from panda3d.pandac import * from panda3d.direct.interval.IntervalGlobal import * from toontown.coghq import MovingPlatform if hasattr(self, "platform"): # Remove the prior instantiation: self.moveIval.pause() del self.moveIval self.platform.destroy() del self.platform self.platform2.destroy() del self.platform2 model = loader.loadModel('phase_9/models/cogHQ/platform1') fakeId = id(self) self.platform = MovingPlatform.MovingPlatform() self.platform.setupCopyModel(fakeId, model, 'platformcollision') self.platformRoot = render.attachNewNode("GravityWalker-spawnTest-%s"%fakeId) self.platformRoot.setPos(base.localAvatar, Vec3(0.0, 0.0, 1.0)) self.platformRoot.setHpr(base.localAvatar, Vec3.zero()) self.platform.reparentTo(self.platformRoot) self.platform2 = MovingPlatform.MovingPlatform() self.platform2.setupCopyModel(1+fakeId, model, 'platformcollision') self.platform2Root = render.attachNewNode("GravityWalker-spawnTest2-%s"%fakeId) self.platform2Root.setPos(base.localAvatar, Vec3(-16.0, 30.0, 1.0)) self.platform2Root.setHpr(base.localAvatar, Vec3.zero()) self.platform2.reparentTo(self.platform2Root) duration = 5 self.moveIval = Parallel( Sequence( WaitInterval(0.3), LerpPosInterval(self.platform, duration, Vec3(0.0, 30.0, 0.0), name='platformOut%s' % fakeId, fluid = 1), WaitInterval(0.3), LerpPosInterval(self.platform, duration, Vec3(0.0, 0.0, 0.0), name='platformBack%s' % fakeId, fluid = 1), WaitInterval(0.3), LerpPosInterval(self.platform, duration, Vec3(0.0, 0.0, 30.0), name='platformUp%s' % fakeId, fluid = 1), WaitInterval(0.3), LerpPosInterval(self.platform, duration, Vec3(0.0, 0.0, 0.0), name='platformDown%s' % fakeId, fluid = 1), ), Sequence( WaitInterval(0.3), LerpPosInterval(self.platform2, duration, Vec3(0.0, -30.0, 0.0), name='platform2Out%s' % fakeId, fluid = 1), WaitInterval(0.3), LerpPosInterval(self.platform2, duration, Vec3(0.0, 30.0, 30.0), name='platform2Back%s' % fakeId, fluid = 1), WaitInterval(0.3), LerpPosInterval(self.platform2, duration, Vec3(0.0, -30.0, 0.0), name='platform2Up%s' % fakeId, fluid = 1), WaitInterval(0.3), LerpPosInterval(self.platform2, duration, Vec3(0.0, 0.0, 0.0), name='platformDown%s' % fakeId, fluid = 1), ), name='platformIval%s' % fakeId, ) self.moveIval.loop() """ def setWalkSpeed(self, forward, jump, reverse, rotate): assert self.notify.debugStateCall(self) self.avatarControlForwardSpeed = forward self.avatarControlJumpForce = jump self.avatarControlReverseSpeed = reverse self.avatarControlRotateSpeed = rotate def getSpeeds(self): #assert self.debugPrint("getSpeeds()") return (self.speed, self.rotationSpeed, self.slideSpeed) def getIsAirborne(self): return self.isAirborne def setAvatar(self, avatar): self.avatar = avatar if avatar is not None: pass # setup the avatar def setupRay(self, bitmask, floorOffset, reach): assert self.notify.debugStateCall(self) # This is a ray cast from your head down to detect floor polygons. # This ray start is arbitrarily high in the air. Feel free to use # a higher or lower value depending on whether you want an avatar # that is outside of the world to step up to the floor when they # get under valid floor: cRay = CollisionRay(0.0, 0.0, CollisionHandlerRayStart, 0.0, 0.0, -1.0) cRayNode = CollisionNode('GW.cRayNode') cRayNode.addSolid(cRay) self.cRayNodePath = self.avatarNodePath.attachNewNode(cRayNode) cRayNode.setFromCollideMask(bitmask) cRayNode.setIntoCollideMask(BitMask32.allOff()) # set up floor collision mechanism self.lifter = CollisionHandlerGravity() self.lifter.setGravity(self.__gravity) self.lifter.addInPattern("enter%in") self.lifter.addOutPattern("exit%in") self.lifter.setOffset(floorOffset) self.lifter.setReach(reach) # Limit our rate-of-fall with the lifter. # If this is too low, we actually "fall" off steep stairs # and float above them as we go down. I increased this # from 8.0 to 16.0 to prevent this #self.lifter.setMaxVelocity(16.0) self.lifter.addCollider(self.cRayNodePath, self.avatarNodePath) def setupWallSphere(self, bitmask, avatarRadius): """ Set up the collision sphere """ assert self.notify.debugStateCall(self) # This is a sphere on the ground to detect collisions with # walls, but not the floor. self.avatarRadius = avatarRadius cSphere = CollisionSphere(0.0, 0.0, avatarRadius, avatarRadius) cSphereNode = CollisionNode('GW.cWallSphereNode') cSphereNode.addSolid(cSphere) cSphereNodePath = self.avatarNodePath.attachNewNode(cSphereNode) cSphereNode.setFromCollideMask(bitmask) cSphereNode.setIntoCollideMask(BitMask32.allOff()) # set up collision mechanism if config.GetBool('want-fluid-pusher', 0): self.pusher = CollisionHandlerFluidPusher() else: self.pusher = CollisionHandlerPusher() self.pusher.addCollider(cSphereNodePath, self.avatarNodePath) self.cWallSphereNodePath = cSphereNodePath def setupEventSphere(self, bitmask, avatarRadius): """ Set up the collision sphere """ assert self.notify.debugStateCall(self) # This is a sphere a little larger than the wall sphere to # trigger events. self.avatarRadius = avatarRadius cSphere = CollisionSphere(0.0, 0.0, avatarRadius - 0.1, avatarRadius * 1.04) # Mark it intangible just to emphasize its non-physical purpose. cSphere.setTangible(0) cSphereNode = CollisionNode('GW.cEventSphereNode') cSphereNode.addSolid(cSphere) cSphereNodePath = self.avatarNodePath.attachNewNode(cSphereNode) cSphereNode.setFromCollideMask(bitmask) cSphereNode.setIntoCollideMask(BitMask32.allOff()) # set up collision mechanism self.event = CollisionHandlerEvent() self.event.addInPattern("enter%in") self.event.addOutPattern("exit%in") self.cEventSphereNodePath = cSphereNodePath def setupFloorSphere(self, bitmask, avatarRadius): """ Set up the collision sphere """ assert self.notify.debugStateCall(self) # This is a tiny sphere concentric with the wallSphere to keep # us from slipping through floors. self.avatarRadius = avatarRadius cSphere = CollisionSphere(0.0, 0.0, avatarRadius, 0.01) cSphereNode = CollisionNode('GW.cFloorSphereNode') cSphereNode.addSolid(cSphere) cSphereNodePath = self.avatarNodePath.attachNewNode(cSphereNode) cSphereNode.setFromCollideMask(bitmask) cSphereNode.setIntoCollideMask(BitMask32.allOff()) # set up collision mechanism self.pusherFloorhandler = CollisionHandlerPusher() self.pusherFloor.addCollider(cSphereNodePath, self.avatarNodePath) self.cFloorSphereNodePath = cSphereNodePath def setWallBitMask(self, bitMask): self.wallBitmask = bitMask def setFloorBitMask(self, bitMask): self.floorBitmask = bitMask def swapFloorBitMask(self, oldMask, newMask): self.floorBitmask = self.floorBitmask & ~oldMask self.floorBitmask |= newMask if self.cRayNodePath and not self.cRayNodePath.isEmpty(): self.cRayNodePath.node().setFromCollideMask(self.floorBitmask) def setGravity(self, gravity): self.__gravity = gravity self.lifter.setGravity(self.__gravity) def getGravity(self, gravity): return self.__gravity def initializeCollisions(self, collisionTraverser, avatarNodePath, avatarRadius=1.4, floorOffset=1.0, reach=1.0): """ floorOffset is how high the avatar can reach. I.e. if the avatar walks under a ledge that is <= floorOffset above the ground (a double floor situation), the avatar will step up on to the ledge (instantly). Set up the avatar collisions """ assert self.notify.debugStateCall(self) assert not avatarNodePath.isEmpty() self.avatarNodePath = avatarNodePath self.cTrav = collisionTraverser self.setupRay(self.floorBitmask, floorOffset, reach) self.setupWallSphere(self.wallBitmask, avatarRadius) self.setupEventSphere(self.wallBitmask, avatarRadius) if self.wantFloorSphere: self.setupFloorSphere(self.floorBitmask, avatarRadius) self.setCollisionsActive(1) def setTag(self, key, value): self.cEventSphereNodePath.setTag(key, value) def setAirborneHeightFunc(self, unused_parameter): assert self.notify.debugStateCall(self) self.getAirborneHeight = self.lifter.getAirborneHeight def getAirborneHeight(self): assert self.notify.debugStateCall(self) self.lifter.getAirborneHeight() def setAvatarPhysicsIndicator(self, indicator): """ indicator is a NodePath """ assert self.notify.debugStateCall(self) self.cWallSphereNodePath.show() def deleteCollisions(self): assert self.notify.debugStateCall(self) del self.cTrav self.cWallSphereNodePath.removeNode() del self.cWallSphereNodePath if self.wantFloorSphere: self.cFloorSphereNodePath.removeNode() del self.cFloorSphereNodePath del self.pusher # del self.pusherFloor del self.event del self.lifter del self.getAirborneHeight def setCollisionsActive(self, active=1): assert self.notify.debugStateCall(self) if self.collisionsActive != active: self.collisionsActive = active # Each time we change the collision geometry, make one # more pass to ensure we aren't standing in a wall. self.oneTimeCollide() # make sure we have a shadow traverser base.initShadowTrav() if active: if 1: # Please let skyler or drose know if this is causing a problem # This is a bit of a hack fix: self.avatarNodePath.setP(0.0) self.avatarNodePath.setR(0.0) self.cTrav.addCollider(self.cWallSphereNodePath, self.pusher) if self.wantFloorSphere: self.cTrav.addCollider(self.cFloorSphereNodePath, self.pusherFloor) # Add the lifter to the shadow traverser, which runs after # our traverser. This prevents the "fall through wall and # off ledge" bug. The problem was that we couldn't control # which collided first, the wall pusher or the lifter, if # they're in the same collision traverser. If the lifter # collided first, we'd start falling before getting pushed # back behind the wall. base.shadowTrav.addCollider(self.cRayNodePath, self.lifter) if self.earlyEventSphere: # If we want to trigger the events at the same # time as we intersect walls (e.g. Toontown, for # backward compatibility issues), add the event # sphere to the main traverser. This allows us to # hit door triggers that are just slightly behind # the door itself. self.cTrav.addCollider(self.cEventSphereNodePath, self.event) else: # Normally, we'd rather trigger the events after # the pusher has had a chance to fix up our # position, so we never trigger things that are # behind other polygons. base.shadowTrav.addCollider(self.cEventSphereNodePath, self.event) else: if hasattr(self, 'cTrav'): self.cTrav.removeCollider(self.cWallSphereNodePath) if self.wantFloorSphere: self.cTrav.removeCollider(self.cFloorSphereNodePath) self.cTrav.removeCollider(self.cEventSphereNodePath) base.shadowTrav.removeCollider(self.cEventSphereNodePath) base.shadowTrav.removeCollider(self.cRayNodePath) def getCollisionsActive(self): assert self.debugPrint("getCollisionsActive() returning=%s" % (self.collisionsActive, )) return self.collisionsActive def placeOnFloor(self): """ Make a reasonable effor to place the avatar on the ground. For example, this is useful when switching away from the current walker. """ assert self.notify.debugStateCall(self) self.oneTimeCollide() self.avatarNodePath.setZ(self.avatarNodePath.getZ() - self.lifter.getAirborneHeight()) def oneTimeCollide(self): """ Makes one quick collision pass for the avatar, for instance as a one-time straighten-things-up operation after collisions have been disabled. """ assert self.notify.debugStateCall(self) if not hasattr(self, 'cWallSphereNodePath'): return self.isAirborne = 0 self.mayJump = 1 tempCTrav = CollisionTraverser("oneTimeCollide") tempCTrav.addCollider(self.cWallSphereNodePath, self.pusher) if self.wantFloorSphere: tempCTrav.addCollider(self.cFloorSphereNodePath, self.event) tempCTrav.addCollider(self.cRayNodePath, self.lifter) tempCTrav.traverse(render) def setMayJump(self, task): """ This function's use is internal to this class (maybe I'll add the __ someday). Anyway, if you want to enable or disable jumping in a general way see the ControlManager (don't use this). """ assert self.notify.debugStateCall(self) self.mayJump = 1 return Task.done def startJumpDelay(self, delay): assert self.notify.debugStateCall(self) if self.jumpDelayTask: self.jumpDelayTask.remove() self.mayJump = 0 self.jumpDelayTask = taskMgr.doMethodLater(delay, self.setMayJump, "jumpDelay-%s" % id(self)) def addBlastForce(self, vector): self.lifter.addVelocity(vector.length()) def displayDebugInfo(self): """ For debug use. """ onScreenDebug.add("w controls", "GravityWalker") onScreenDebug.add("w airborneHeight", self.lifter.getAirborneHeight()) onScreenDebug.add("w falling", self.falling) onScreenDebug.add("w isOnGround", self.lifter.isOnGround()) #onScreenDebug.add("w gravity", self.lifter.getGravity()) #onScreenDebug.add("w jumpForce", self.avatarControlJumpForce) onScreenDebug.add("w contact normal", self.lifter.getContactNormal().pPrintValues()) onScreenDebug.add("w mayJump", self.mayJump) onScreenDebug.add("w impact", self.lifter.getImpactVelocity()) onScreenDebug.add("w velocity", self.lifter.getVelocity()) onScreenDebug.add("w isAirborne", self.isAirborne) onScreenDebug.add("w hasContact", self.lifter.hasContact()) def handleAvatarControls(self, task): """ Check on the arrow keys and update the avatar. """ # get the button states: run = inputState.isSet("run") forward = inputState.isSet("forward") reverse = inputState.isSet("reverse") turnLeft = inputState.isSet("turnLeft") turnRight = inputState.isSet("turnRight") slideLeft = inputState.isSet("slideLeft") slideRight = inputState.isSet("slideRight") jump = inputState.isSet("jump") # Check for Auto-Run if 'localAvatar' in __builtins__: if base.localAvatar and base.localAvatar.getAutoRun(): forward = 1 reverse = 0 # Determine what the speeds are based on the buttons: self.speed = (forward and self.avatarControlForwardSpeed or reverse and -self.avatarControlReverseSpeed) # Slide speed is a scaled down version of forward speed # Note: you can multiply a factor in here if you want slide to # be slower than normal walk/run. Let's try full speed. #self.slideSpeed=(slideLeft and -self.avatarControlForwardSpeed*0.75 or # slideRight and self.avatarControlForwardSpeed*0.75) self.slideSpeed = ( reverse and slideLeft and -self.avatarControlReverseSpeed * 0.75 or reverse and slideRight and self.avatarControlReverseSpeed * 0.75 or slideLeft and -self.avatarControlForwardSpeed * 0.75 or slideRight and self.avatarControlForwardSpeed * 0.75) self.rotationSpeed = not (slideLeft or slideRight) and ( (turnLeft and self.avatarControlRotateSpeed) or (turnRight and -self.avatarControlRotateSpeed)) if self.speed and self.slideSpeed: self.speed *= GravityWalker.DiagonalFactor self.slideSpeed *= GravityWalker.DiagonalFactor debugRunning = inputState.isSet("debugRunning") if (debugRunning): self.speed *= base.debugRunningMultiplier self.slideSpeed *= base.debugRunningMultiplier self.rotationSpeed *= 1.25 if self.needToDeltaPos: self.setPriorParentVector() self.needToDeltaPos = 0 if self.wantDebugIndicator: self.displayDebugInfo() if self.lifter.isOnGround(): if self.isAirborne: self.isAirborne = 0 assert self.debugPrint("isAirborne 0 due to isOnGround() true") impact = self.lifter.getImpactVelocity() if impact < -30.0: messenger.send("jumpHardLand") self.startJumpDelay(0.3) else: messenger.send("jumpLand") if impact < -5.0: self.startJumpDelay(0.2) # else, ignore the little potholes. assert self.isAirborne == 0 self.priorParent = Vec3.zero() if jump and self.mayJump: # The jump button is down and we're close # enough to the ground to jump. self.lifter.addVelocity(self.avatarControlJumpForce) messenger.send("jumpStart") self.isAirborne = 1 assert self.debugPrint("isAirborne 1 due to jump") else: if self.isAirborne == 0: assert self.debugPrint( "isAirborne 1 due to isOnGround() false") self.isAirborne = 1 self.__oldPosDelta = self.avatarNodePath.getPosDelta(render) # How far did we move based on the amount of time elapsed? self.__oldDt = ClockObject.getGlobalClock().getDt() dt = self.__oldDt # Check to see if we're moving at all: self.moving = self.speed or self.slideSpeed or self.rotationSpeed or ( self.priorParent != Vec3.zero()) if self.moving: distance = dt * self.speed slideDistance = dt * self.slideSpeed rotation = dt * self.rotationSpeed # Take a step in the direction of our previous heading. if distance or slideDistance or self.priorParent != Vec3.zero(): # rotMat is the rotation matrix corresponding to # our previous heading. rotMat = Mat3.rotateMatNormaxis(self.avatarNodePath.getH(), Vec3.up()) if self.isAirborne: forward = Vec3.forward() else: contact = self.lifter.getContactNormal() forward = contact.cross(Vec3.right()) # Consider commenting out this normalize. If you do so # then going up and down slops is a touch slower and # steeper terrain can cut the movement in half. Without # the normalize the movement is slowed by the cosine of # the slope (i.e. it is multiplied by the sign as a # side effect of the cross product above). forward.normalize() self.vel = Vec3(forward * distance) if slideDistance: if self.isAirborne: right = Vec3.right() else: right = forward.cross(contact) # See note above for forward.normalize() right.normalize() self.vel = Vec3(self.vel + (right * slideDistance)) self.vel = Vec3(rotMat.xform(self.vel)) step = self.vel + (self.priorParent * dt) self.avatarNodePath.setFluidPos( Point3(self.avatarNodePath.getPos() + step)) self.avatarNodePath.setH(self.avatarNodePath.getH() + rotation) else: self.vel.set(0.0, 0.0, 0.0) if self.moving or jump: messenger.send("avatarMoving") return Task.cont def doDeltaPos(self): assert self.notify.debugStateCall(self) self.needToDeltaPos = 1 def setPriorParentVector(self): assert self.notify.debugStateCall(self) if __debug__: onScreenDebug.add("__oldDt", "% 10.4f" % self.__oldDt) onScreenDebug.add("self.__oldPosDelta", self.__oldPosDelta.pPrintValues()) # avoid divide by zero crash - grw if self.__oldDt == 0: velocity = 0 else: velocity = self.__oldPosDelta * (1.0 / self.__oldDt) self.priorParent = Vec3(velocity) if __debug__: if self.wantDebugIndicator: onScreenDebug.add("priorParent", self.priorParent.pPrintValues()) def reset(self): assert self.notify.debugStateCall(self) self.lifter.setVelocity(0.0) self.priorParent = Vec3.zero() def getVelocity(self): return self.vel def enableAvatarControls(self): """ Activate the arrow keys, etc. """ assert self.notify.debugStateCall(self) assert self.collisionsActive #*#if __debug__: #*# self.accept("control-f3", self.spawnTest) #*# # remove any old if self.controlsTask: self.controlsTask.remove() # spawn the new task taskName = "AvatarControls-%s" % (id(self), ) self.controlsTask = taskMgr.add(self.handleAvatarControls, taskName, 25) self.isAirborne = 0 self.mayJump = 1 if self.physVelocityIndicator: if self.indicatorTask: self.indicatorTask.remove() self.indicatorTask = taskMgr.add( self.avatarPhysicsIndicator, "AvatarControlsIndicator-%s" % (id(self), ), 35) def disableAvatarControls(self): """ Ignore the arrow keys, etc. """ assert self.notify.debugStateCall(self) if self.controlsTask: self.controlsTask.remove() self.controlsTask = None if self.indicatorTask: self.indicatorTask.remove() self.indicatorTask = None if self.jumpDelayTask: self.jumpDelayTask.remove() self.jumpDelayTask = None if __debug__: self.ignore("control-f3") #*# def flushEventHandlers(self): if hasattr(self, 'cTrav'): self.pusher.flush() if self.wantFloorSphere: self.floorPusher.flush() self.event.flush() self.lifter.flush() # not currently defined or needed if __debug__: def debugPrint(self, message): """for debugging""" return self.notify.debug(str(id(self)) + ' ' + message)
class MessengerLeakDetector(Job): # check for objects that are only referenced by the messenger # and would otherwise be garbage collected notify = directNotify.newCategory("MessengerLeakDetector") def __init__(self, name): Job.__init__(self, name) self.setPriority(Job.Priorities.Normal * 2) jobMgr.add(self) def run(self): # set of ids of objects that we know are always attached to builtin; # if an object is attached to one of these, it's attached to builtin # this cuts down on the amount of searching that needs to be done builtinIds = set() builtinIds.add(id(__builtin__.__dict__)) try: builtinIds.add(id(base)) builtinIds.add(id(base.cr)) builtinIds.add(id(base.cr.doId2do)) except: pass try: builtinIds.add(id(simbase)) builtinIds.add(id(simbase.air)) builtinIds.add(id(simbase.air.doId2do)) except: pass try: builtinIds.add(id(uber)) builtinIds.add(id(uber.air)) builtinIds.add(id(uber.air.doId2do)) except: pass while True: yield None objects = messenger._Messenger__objectEvents.keys() assert self.notify.debug('%s objects in the messenger' % len(objects)) for object in objects: yield None assert self.notify.debug('---> new object: %s' % itype(object)) # try to find a path to builtin that doesn't involve the messenger # lists of objects for breadth-first search # iterate through one list while populating other list objList1 = [] objList2 = [] curObjList = objList1 nextObjList = objList2 visitedObjIds = set() # add the id of the object, and the messenger containers so that # the search for builtin will stop at the messenger; we're looking # for any path to builtin that don't involve the messenger visitedObjIds.add(id(object)) visitedObjIds.add(id(messenger._Messenger__objectEvents)) visitedObjIds.add(id(messenger._Messenger__callbacks)) nextObjList.append(object) foundBuiltin = False # breadth-first search, go until you run out of new objects or you find __builtin__ while len(nextObjList): if foundBuiltin: break # swap the lists, prepare for the next pass curObjList = nextObjList nextObjList = [] assert self.notify.debug( 'next search iteration, num objects: %s' % len(curObjList)) for curObj in curObjList: if foundBuiltin: break yield None referrers = gc.get_referrers(curObj) assert self.notify.debug( 'curObj: %s @ %s, %s referrers, repr=%s' % (itype(curObj), hex(id(curObj)), len(referrers), fastRepr(curObj, maxLen=2))) for referrer in referrers: #assert self.notify.debug('referrer: %s' % itype(curObj)) yield None refId = id(referrer) # don't go in a loop if refId in visitedObjIds: #assert self.notify.debug('already visited') continue # don't self-reference if referrer is curObjList or referrer is nextObjList: continue if refId in builtinIds: # not a leak, there is a path to builtin that does not involve the messenger #assert self.notify.debug('object has another path to __builtin__, it\'s not a messenger leak') foundBuiltin = True break else: visitedObjIds.add(refId) nextObjList.append(referrer) if not foundBuiltin: self.notify.warning( '%s is referenced only by the messenger' % (itype(object)))
class ShipPilot2(PhysicsWalker): notify = directNotify.newCategory("PhysicsWalker") wantDebugIndicator = base.config.GetBool('want-avatar-physics-indicator', 0) useBowSternSpheres = 1 useOneSphere = 0 useDSSolid = 0 useLifter = 0 useHeightRay = 0 MAX_STRAIGHT_SAIL_BONUS = 4.0 STRAIGHT_SAIL_BONUS_TIME = 10.0 # special methods def __init__(self, gravity=-32.1740, standableGround=0.707, hardLandingForce=16.0): assert self.debugPrint( "PhysicsWalker(gravity=%s, standableGround=%s)" % (gravity, standableGround)) PhysicsWalker.__init__(self, gravity, standableGround, hardLandingForce) self.__gravity = gravity self.__standableGround = standableGround self.__hardLandingForce = hardLandingForce self.needToDeltaPos = 0 self.physVelocityIndicator = None self.avatarControlForwardSpeed = 0 self.avatarControlJumpForce = 0 self.avatarControlReverseSpeed = 0 self.avatarControlRotateSpeed = 0 self.__oldAirborneHeight = None self.getAirborneHeight = None self.__oldContact = None self.__oldPosDelta = Vec3(0) self.__oldDt = 0 self.__speed = 0.0 self.__rotationSpeed = 0.0 self.__slideSpeed = 0.0 self.__vel = Vec3(0.0) self.collisionsActive = 0 self.currentTurning = 0.0 self.isAirborne = 0 self.highMark = 0 self.ship = None # Keeps track of the ship sailing in a straight heading # for long periods of time. We slowly up the ship's max # acceleration as this increases. self.straightHeading = 0 def setWalkSpeed(self, forward, jump, reverse, rotate): assert self.debugPrint("setWalkSpeed()") self.avatarControlForwardSpeed = forward self.avatarControlJumpForce = 0.0 self.avatarControlReverseSpeed = reverse self.avatarControlRotateSpeed = rotate def getSpeeds(self): #assert self.debugPrint("getSpeeds()") return (self.__speed, self.__rotationSpeed) def setAvatar(self, ship): if ship is None: base.controlForce.clearPhysicsObject() self.takedownPhysics() self.ship = None else: base.controlForce.setPhysicsObject(ship.node().getPhysicsObject()) #self.setupShip() self.setupPhysics(ship) self.ship = ship """ #*# Debug: if not hasattr(ship, "acceleration"): self.ship.acceleration = 60 self.ship.maxSpeed = 14 self.ship.reverseAcceleration = 30 self.ship.maxReverseSpeed = 2 self.ship.turnRate = 3 self.ship.maxTurn = 30 self.ship.anchorDrag = .9 self.ship.hullDrag = .9 """ def setupRay(self, floorBitmask, floorOffset): # This is a ray cast from your head down to detect floor polygons # A toon is about 4.0 feet high, so start it there self.cRay = CollisionRay(0.0, 0.0, CollisionHandlerRayStart, 0.0, 0.0, -1.0) cRayNode = CollisionNode('PW.cRayNode') cRayNode.addSolid(self.cRay) self.cRayNodePath = self.avatarNodePath.attachNewNode(cRayNode) self.cRayBitMask = floorBitmask cRayNode.setFromCollideMask(self.cRayBitMask) cRayNode.setIntoCollideMask(BitMask32.allOff()) if 0 or self.useLifter: # set up floor collision mechanism self.lifter = CollisionHandlerFloor() self.lifter.setInPattern("enter%in") self.lifter.setOutPattern("exit%in") self.lifter.setOffset(floorOffset) # Limit our rate-of-fall with the lifter. # If this is too low, we actually "fall" off steep stairs # and float above them as we go down. I increased this # from 8.0 to 16.0 to prevent this #self.lifter.setMaxVelocity(16.0) #self.bobNodePath = self.avatarNodePath.attachNewNode("bob") #self.lifter.addCollider(self.cRayNodePath, self.cRayNodePath) self.lifter.addCollider(self.cRayNodePath, self.avatarNodePath) else: # useCollisionHandlerQueue self.cRayQueue = CollisionHandlerQueue() self.cTrav.addCollider(self.cRayNodePath, self.cRayQueue) def determineHeight(self): """ returns the height of the avatar above the ground. If there is no floor below the avatar, 0.0 is returned. aka get airborne height. """ if self.useLifter: height = self.avatarNodePath.getPos(self.cRayNodePath) # If the shadow where not pointed strait down, we would need to # get magnitude of the vector. Since it is strait down, we'll # just get the z: #spammy --> assert self.debugPrint( #spammy --> "getAirborneHeight() returning %s"%(height.getZ(),)) assert onScreenDebug.add("height", height.getZ()) return height.getZ() - self.floorOffset else: # useCollisionHandlerQueue """ returns the height of the avatar above the ground. If there is no floor below the avatar, 0.0 is returned. aka get airborne height. """ height = 0.0 #*#self.cRayTrav.traverse(render) if self.cRayQueue.getNumEntries() != 0: # ...we have a floor. # Choose the highest of the possibly several floors we're over: self.cRayQueue.sortEntries() floorPoint = self.cRayQueue.getEntry( 0).getFromIntersectionPoint() height = -floorPoint.getZ() self.cRayQueue.clearEntries() if __debug__: onScreenDebug.add("height", height) return height def setupSphere(self, bitmask, avatarRadius): """ Set up the collision sphere """ # This is a sphere on the ground to detect barrier collisions if 0: self.avatarRadius = avatarRadius self.cSphere = CollisionTube(Point3(0.0, 0.0, 0.0), Point3(0.0, 40.0, 0.0), avatarRadius) cSphereNode = CollisionNode('SP.cSphereNode') cSphereNode.addSolid(self.cSphere) self.cSphereNodePath = self.avatarNodePath.attachNewNode( cSphereNode) self.cSphereBitMask = bitmask else: # Middle sphere: self.avatarRadius = avatarRadius #self.cSphere = CollisionSphere(0.0, -5.0, 0.0, avatarRadius) #cSphereNode = CollisionNode('SP.cSphereNode') #cSphereNode.addSolid(self.cSphere) #self.cSphereNodePath = self.avatarNodePath.attachNewNode(cSphereNode) self.cSphereBitMask = bitmask #cSphereNode.setFromCollideMask(self.cSphereBitMask) #cSphereNode.setIntoCollideMask(BitMask32.allOff()) # set up collision mechanism self.pusher = PhysicsCollisionHandler() self.pusher.setInPattern("enter%in") self.pusher.setOutPattern("exit%in") #self.pusher.addCollider(self.cSphereNodePath, self.avatarNodePath) if self.useBowSternSpheres: # Front sphere: self.cBowSphere = CollisionSphere(0.0, self.frontSphereOffset, -5.0, avatarRadius) cBowSphereNode = CollisionNode('SP.cBowSphereNode') cBowSphereNode.addSolid(self.cBowSphere) self.cBowSphereNodePath = self.avatarNodePath.attachNewNode( cBowSphereNode) #self.cBowSphereNodePath.show() cBowSphereNode.setFromCollideMask(self.cSphereBitMask) cBowSphereNode.setIntoCollideMask(BitMask32.allOff()) self.cBowSphereNode = cBowSphereNode self.pusher.addCollider(self.cBowSphereNodePath, self.avatarNodePath) # Back sphere: self.cSternSphere = CollisionSphere(0.0, self.backSphereOffset, -5.0, avatarRadius) cSternSphereNode = CollisionNode('SP.cSternSphereNode') cSternSphereNode.addSolid(self.cSternSphere) self.cSternSphereNodePath = self.avatarNodePath.attachNewNode( cSternSphereNode) #self.cSternSphereNodePath.show() self.cSternSphereBitMask = bitmask cSternSphereNode.setFromCollideMask(self.cSphereBitMask) cSternSphereNode.setIntoCollideMask(BitMask32.allOff()) self.pusher.addCollider(self.cSternSphereNodePath, self.avatarNodePath) # hide other things on my ship that these spheres might collide # with and which I dont need anyways... shipCollWall = self.avatarNodePath.transNode.find( "**/collision_hull") if not shipCollWall.isEmpty(): shipCollWall.stash() elif self.useOneSphere: # Front sphere: self.cSphere = CollisionSphere(0.0, 0.0, -5.0, avatarRadius) cSphereNode = CollisionNode('SP.cSphereNode') cSphereNode.addSolid(self.cSphere) self.cSphereNodePath = self.avatarNodePath.attachNewNode( cSphereNode) if 1: self.cSphereNodePath.show() self.cSphereNodePath.showBounds() cSphereNode.setFromCollideMask(self.cSphereBitMask) cSphereNode.setIntoCollideMask(BitMask32.allOff()) self.cSphereNode = cSphereNode self.pusher.addCollider(self.cSphereNodePath, self.avatarNodePath) # hide other things on my ship that these spheres might collide # with and which I dont need anyways... shipCollWall = self.avatarNodePath.transNode.find( "**/collision_hull") if not shipCollWall.isEmpty(): shipCollWall.stash() elif self.useDSSolid: # DSSolid: self.cHull = CollisionDSSolid( Point3(-160, 0, 0), 200, Point3(160, 0, 0), 200, Plane(Vec3(0, 0, 1), Point3(0, 0, 50)), Plane(Vec3(0, -1, 0), Point3(0, -90, 0))) cHullNode = CollisionNode('SP.cHullNode') cHullNode.addSolid(self.cHull) self.cHullNodePath = self.avatarNodePath.attachNewNode(cHullNode) self.cHullNodePath.show() cHullNode.setFromCollideMask(bitmask) cHullNode.setIntoCollideMask(BitMask32.allOff()) self.cHullNode = cHullNode self.pusher.addCollider(self.cHullNodePath, self.avatarNodePath) # hide other things on my ship that these spheres might collide # with and which I dont need anyways... shipCollWall = self.avatarNodePath.transNode.find( "**/collision_hull") if not shipCollWall.isEmpty(): shipCollWall.stash() def takedownPhysics(self): assert self.debugPrint("takedownPhysics()") if 0: if hasattr(self, "phys"): for i in self.nodes: i.removeNode() del self.phys if self.ship != None: self.ship.worldVelocity = Vec3.zero() def setupPhysics(self, avatarNodePath): assert self.debugPrint("setupPhysics()") if avatarNodePath is None: return assert not avatarNodePath.isEmpty() self.takedownPhysics() self.nodes = [] self.actorNode = avatarNodePath.node() if 0: fn = ForceNode("ship priorParent") fnp = NodePath(fn) fnp.reparentTo(render) self.nodes.append(fnp) priorParent = LinearVectorForce(0.0, 0.0, 0.0) fn.addForce(priorParent) self.phys.addLinearForce(priorParent) self.priorParentNp = fnp self.priorParent = priorParent self.avatarNodePath = avatarNodePath #self.actorNode.getPhysicsObject().resetPosition(self.avatarNodePath.getPos()) #self.actorNode.updateTransform() self.setupSphere(self.wallBitmask | self.floorBitmask, self.avatarRadius) assert not avatarNodePath.isEmpty() self.setCollisionsActive(1) def setWallBitMask(self, bitMask): self.wallBitmask = bitMask def setFloorBitMask(self, bitMask): self.floorBitmask = bitMask def initializeCollisions(self, collisionTraverser, avatarRadius=1.4, floorOffset=1.0, reach=1.0, width=30.0, length=105.0, height=45.0): """ width is feet from port to starboard. length is feet from aft to bow. height is feet from bildge to deck (i.e. not including mast height). Set up the avatar collisions """ assert self.debugPrint("initializeCollisions()") self.cTrav = collisionTraverser self.avatarRadius = avatarRadius self.floorOffset = floorOffset self.reach = reach if self.useBowSternSpheres: self.frontSphereOffset = length * 0.3 self.backSphereOffset = -length * 0.7 self.width = width self.length = length self.height = height def deleteCollisions(self): assert self.debugPrint("deleteCollisions()") del self.cTrav if self.useHeightRay: del self.cRayQueue self.cRayNodePath.removeNode() del self.cRayNodePath if hasattr(self, "cSphere"): del self.cSphere self.cSphereNodePath.removeNode() del self.cSphereNodePath del self.pusher self.getAirborneHeight = None def setTag(self, key, value): if not hasattr(self, "collisionTags"): self.collisionTags = {} self.collisionTags[key] = value def setAirborneHeightFunc(self, getAirborneHeight): self.getAirborneHeight = getAirborneHeight def setAvatarPhysicsIndicator(self, indicator): """ indicator is a NodePath """ assert self.debugPrint("setAvatarPhysicsIndicator()") self.cSphereNodePath.show() if indicator: # Indicator Node: change = render.attachNewNode("change") #change.setPos(Vec3(1.0, 1.0, 1.0)) #change.setHpr(0.0, 0.0, 0.0) change.setScale(0.1) #change.setColor(Vec4(1.0, 1.0, 1.0, 1.0)) indicator.reparentTo(change) indicatorNode = render.attachNewNode("physVelocityIndicator") #indicatorNode.setScale(0.1) #indicatorNode.setP(90.0) indicatorNode.setPos(self.avatarNodePath, 0.0, 0.0, 6.0) indicatorNode.setColor(0.0, 0.0, 1.0, 1.0) change.reparentTo(indicatorNode) self.physVelocityIndicator = indicatorNode # Contact Node: contactIndicatorNode = render.attachNewNode("physContactIndicator") contactIndicatorNode.setScale(0.25) contactIndicatorNode.setP(90.0) contactIndicatorNode.setPos(self.avatarNodePath, 0.0, 0.0, 5.0) contactIndicatorNode.setColor(1.0, 0.0, 0.0, 1.0) indicator.instanceTo(contactIndicatorNode) self.physContactIndicator = contactIndicatorNode else: print "failed load of physics indicator" def avatarPhysicsIndicator(self, task): #assert self.debugPrint("avatarPhysicsIndicator()") # Velocity: self.physVelocityIndicator.setPos(self.avatarNodePath, 0.0, 0.0, 6.0) physObject = self.actorNode.getPhysicsObject() a = physObject.getVelocity() self.physVelocityIndicator.setScale(math.sqrt(a.length())) a += self.physVelocityIndicator.getPos() self.physVelocityIndicator.lookAt(Point3(a)) # Contact: contact = self.actorNode.getContactVector() if contact == Vec3.zero(): self.physContactIndicator.hide() else: self.physContactIndicator.show() self.physContactIndicator.setPos(self.avatarNodePath, 0.0, 0.0, 5.0) #contact=self.actorNode.getContactVector() point = Point3(contact + self.physContactIndicator.getPos()) self.physContactIndicator.lookAt(point) return Task.cont def setCollisionsActive(self, active=1): assert self.debugPrint("collisionsActive(active=%s)" % (active, )) if self.collisionsActive != active: self.collisionsActive = active if active: if self.useBowSternSpheres: self.cTrav.addCollider(self.cBowSphereNodePath, self.pusher) self.cTrav.addCollider(self.cSternSphereNodePath, self.pusher) elif self.useOneSphere: self.cTrav.addCollider(self.cSphereNodePath, self.pusher) elif self.useDSSolid: self.cTrav.addCollider(self.cHullNodePath, self.pusher) if self.useHeightRay: if self.useLifter: self.cTrav.addCollider(self.cRayNodePath, self.lifter) else: self.cTrav.addCollider(self.cRayNodePath, self.cRayQueue) else: if self.useBowSternSpheres: self.cTrav.removeCollider(self.cBowSphereNodePath) self.cTrav.removeCollider(self.cSternSphereNodePath) elif self.useOneSphere: self.cTrav.removeCollider(self.cSphereNodePath) elif self.useDSSolid: self.cTrav.removeCollider(self.cHullNodePath) if self.useHeightRay: self.cTrav.removeCollider(self.cRayNodePath) # Now that we have disabled collisions, make one more pass # right now to ensure we aren't standing in a wall. self.oneTimeCollide() def getCollisionsActive(self): assert self.debugPrint("getCollisionsActive() returning=%s" % (self.collisionsActive, )) return self.collisionsActive def placeOnFloor(self): """ Make a reasonable effort to place the avatar on the ground. For example, this is useful when switching away from the current walker. """ self.oneTimeCollide() self.avatarNodePath.setZ(self.avatarNodePath.getZ() - self.getAirborneHeight()) def oneTimeCollide(self): """ Makes one quick collision pass for the avatar, for instance as a one-time straighten-things-up operation after collisions have been disabled. """ assert self.debugPrint("oneTimeCollide()") tempCTrav = CollisionTraverser("oneTimeCollide") if self.useHeightRay: if self.useLifter: tempCTrav.addCollider(self.cRayNodePath, self.lifter) else: tempCTrav.addCollider(self.cRayNodePath, self.cRayQueue) tempCTrav.traverse(render) def addBlastForce(self, vector): pass def displayDebugInfo(self): """ For debug use. """ onScreenDebug.add("w controls", "ShipPilot") onScreenDebug.add("w ship", self.ship) onScreenDebug.add("w isAirborne", self.isAirborne) onScreenDebug.add( "posDelta1", self.avatarNodePath.getPosDelta(render).pPrintValues()) physObject = self.actorNode.getPhysicsObject() if 0: onScreenDebug.add( "w posDelta3", render.getRelativeVector( self.avatarNodePath, self.avatarNodePath.getPosDelta(render)).pPrintValues()) if 0: onScreenDebug.add("w priorParent", self.priorParent.getLocalVector().pPrintValues()) onScreenDebug.add("w physObject pos", physObject.getPosition().pPrintValues()) onScreenDebug.add( "w physObject hpr", physObject.getOrientation().getHpr().pPrintValues()) onScreenDebug.add("w physObject orien", physObject.getOrientation().pPrintValues()) if 1: physObject = physObject.getVelocity() onScreenDebug.add("w physObject vec", physObject.pPrintValues()) onScreenDebug.add("w physObject len", "% 10.4f" % physObject.length()) onScreenDebug.add( "orientation", self.actorNode.getPhysicsObject().getOrientation(). pPrintValues()) if 0: momentumForce = self.momentumForce.getLocalVector() onScreenDebug.add("w momentumForce vec", momentumForce.pPrintValues()) onScreenDebug.add("w momentumForce len", "% 10.4f" % momentumForce.length()) ## if 1: ## keel = self.keel.getLocalVector() ## onScreenDebug.add("w keel vec", ## keel.pPrintValues()) if 0: onScreenDebug.add( "posDelta4", self.priorParentNp.getRelativeVector( render, self.avatarNodePath.getPosDelta(render)).pPrintValues()) if 0: onScreenDebug.add("w priorParent", self.priorParent.getLocalVector().pPrintValues()) if 0: onScreenDebug.add( "w priorParent po", self.priorParent.getVector(physObject).pPrintValues()) if 1: onScreenDebug.add("w contact", self.actorNode.getContactVector().pPrintValues()) #onScreenDebug.add("airborneHeight", "% 10.4f"%( # self.getAirborneHeight(),)) def handleAvatarControls(self, task): """ Check on the arrow keys and update the avatar. """ if __debug__: if self.wantDebugIndicator: onScreenDebug.append( "localAvatar pos = %s\n" % (base.localAvatar.getPos().pPrintValues(), )) onScreenDebug.append( "localAvatar hpr = %s\n" % (base.localAvatar.getHpr().pPrintValues(), )) #assert self.debugPrint("handleAvatarControls(task=%s)"%(task,)) physObject = self.actorNode.getPhysicsObject() contact = self.actorNode.getContactVector() # get the button states: forward = inputState.isSet("forward") reverse = inputState.isSet("reverse") turnLeft = inputState.isSet("slideLeft") or inputState.isSet( "turnLeft") turnRight = inputState.isSet("slideRight") or inputState.isSet( "turnRight") slide = inputState.isSet("slide") slideLeft = 0 slideRight = 0 jump = inputState.isSet("jump") # Determine what the speeds are based on the buttons: # Check for Auto-Sailing if self.ship.getIsAutoSailing(): forward = 1 reverse = 0 # How far did we move based on the amount of time elapsed? dt = ClockObject.getGlobalClock().getDt() if reverse or turnLeft or turnRight: # Reset Straight Sailing Bonus self.straightHeading = 0 # Straight Sailing Acceleration Bonus straightSailDt += dt straightSailBonus = 0.0 if straightSailDt > self.STRAIGHT_SAIL_BONUS_TIME / 2.0: straightSailBonus = (straightSailDt - (self.STRAIGHT_SAIL_BONUS_TIME / 2.0)) / self.STRAIGHT_SAIL_BONUS_TIME / 2.0 straightSailBonus *= self.MAX_STRAIGHT_SAIL_BONUS straightSailBonus += 1.0 print "##################" print straightSailBonus # this was causing the boat to get stuck moving forward or back if 0: if not hasattr(self, "sailsDeployed"): self.sailsDeployed = 0.0 if forward and reverse: # Way anchor: self.__speed = 0.0 physObject.setVelocity(Vec3.zero()) elif forward: self.sailsDeployed += 0.25 if self.sailsDeployed > 1.0: self.sailsDeployed = 1.0 elif reverse: self.sailsDeployed -= 0.25 if self.sailsDeployed < -1.0: self.sailsDeployed = -1.0 self.__speed = self.ship.acceleration * self.sailsDeployed else: self.__speed=(forward and self.ship.acceleration) or \ (reverse and -self.ship.reverseAcceleration) #self.__speed=(forward and min(dt*(self.__speed + self.ship.acceleration), self.ship.maxSpeed) or # reverse and min(dt*(self.__speed - self.ship.reverseAcceleration), self.ship.maxReverseSpeed)) avatarSlideSpeed = self.ship.acceleration * 0.5 #self.__slideSpeed=slide and ( # (turnLeft and -avatarSlideSpeed) or # (turnRight and avatarSlideSpeed)) self.__slideSpeed = (forward or reverse) and ( (slideLeft and -avatarSlideSpeed) or (slideRight and avatarSlideSpeed)) self.__rotationSpeed = not slide and ( (turnLeft and self.ship.turnRate) or (turnRight and -self.ship.turnRate)) # Add in Straight Sailing Multiplier self.__speed *= straightSailBonus # Enable debug turbo mode maxSpeed = self.ship.maxSpeed * straightSailBonus debugRunning = inputState.isSet("debugRunning") if debugRunning or base.localAvatar.getTurbo(): self.__speed *= 4.0 self.__slideSpeed *= 4.0 self.__rotationSpeed *= 1.25 maxSpeed = self.ship.maxSpeed * 4.0 #self.__speed*=4.0 #self.__slideSpeed*=4.0 #self.__rotationSpeed*=1.25 #maxSpeed = self.ship.maxSpeed * 4.0 #*# self.currentTurning += self.__rotationSpeed if self.currentTurning > self.ship.maxTurn: self.currentTurning = self.ship.maxTurn elif self.currentTurning < -self.ship.maxTurn: self.currentTurning = -self.ship.maxTurn if turnLeft or turnRight: mult = .9 elif forward or reverse: mult = .82 else: mult = .8 self.currentTurning *= mult if self.currentTurning < 0.001 and self.currentTurning > -0.001: self.currentTurning = 0.0 self.__rotationSpeed = self.currentTurning if self.wantDebugIndicator: self.displayDebugInfo() if self.needToDeltaPos: self.setPriorParentVector() self.needToDeltaPos = 0 airborneHeight = self.getAirborneHeight() if airborneHeight > self.highMark: self.highMark = airborneHeight if __debug__: onScreenDebug.add("highMark", "% 10.4f" % (self.highMark, )) #if airborneHeight < 0.1: #contact!=Vec3.zero(): if 1: if (airborneHeight > self.avatarRadius * 0.5 or physObject.getVelocity().getZ() > 0.0): # Check stair angles before changing this. # The avatar is airborne (maybe a lot or a tiny amount). self.isAirborne = 1 else: # The avatar is very close to the ground (close # enough to be considered on the ground). if self.isAirborne and physObject.getVelocity().getZ() <= 0.0: # the avatar has landed. contactLength = contact.length() if contactLength > self.__hardLandingForce: messenger.send("shipJumpHardLand") else: messenger.send("shipJumpLand") #self.priorParent.setVector(Vec3.zero()) self.isAirborne = 0 elif jump: #self.__jumpButton=0 messenger.send("shipJumpStart") if 0: # Jump away from walls and with with the slope normal. jumpVec = Vec3(contact + Vec3.up()) #jumpVec=Vec3(rotAvatarToPhys.xform(jumpVec)) jumpVec.normalize() else: # Jump straight up, even if next to a wall. jumpVec = Vec3.up() jumpVec *= self.avatarControlJumpForce physObject.addImpulse(Vec3(jumpVec)) self.isAirborne = 1 # Avoid double impulse before fully airborne. else: self.isAirborne = 0 if __debug__: onScreenDebug.add("isAirborne", "%d" % (self.isAirborne, )) else: if contact != Vec3.zero(): # The avatar has touched something (but might # not be on the ground). contactLength = contact.length() contact.normalize() angle = contact.dot(Vec3.up()) if angle > self.__standableGround: # ...avatar is on standable ground. if self.__oldContact == Vec3.zero(): #if self.__oldAirborneHeight > 0.1: #self.__oldContact==Vec3.zero(): # ...avatar was airborne. self.jumpCount -= 1 if contactLength > self.__hardLandingForce: messenger.send("jumpHardLand") else: messenger.send("jumpLand") elif jump: self.jumpCount += 1 #self.__jumpButton=0 messenger.send("jumpStart") jump = Vec3(contact + Vec3.up()) #jump=Vec3(rotAvatarToPhys.xform(jump)) jump.normalize() jump *= self.avatarControlJumpForce physObject.addImpulse(Vec3(jump)) if contact != self.__oldContact: # We must copy the vector to preserve it: self.__oldContact = Vec3(contact) self.__oldAirborneHeight = airborneHeight #------------------------------ #debugTempH=self.avatarNodePath.getH() if __debug__: q1 = self.avatarNodePath.getQuat() q2 = physObject.getOrientation() q1.normalize() q2.normalize() assert q1.isSameDirection(q2) or (q1.getHpr() == q2.getHpr()) assert self.avatarNodePath.getPos().almostEqual( physObject.getPosition(), 0.0001) #------------------------------ # Check to see if we're moving at all: physVel = physObject.getVelocity() physVelLen = physVel.length() if (physVelLen != 0. or self.__speed or self.__slideSpeed or self.__rotationSpeed): distance = dt * self.__speed goForward = True if (distance < 0): goForward = False slideDistance = dt * self.__slideSpeed rotation = dt * self.__rotationSpeed # update pos: # Take a step in the direction of our previous heading. self.__vel = Vec3(Vec3.forward() * distance + Vec3.right() * slideDistance) # rotMat is the rotation matrix corresponding to # our previous heading. rotMat = Mat3.rotateMatNormaxis(self.avatarNodePath.getH(), Vec3.up()) step = rotMat.xform(self.__vel) #newVector = self.acForce.getLocalVector()+Vec3(step) newVector = Vec3(step) #newVector=Vec3(rotMat.xform(newVector)) #maxLen = maxSpeed if (goForward): maxLen = self.ship.acceleration else: maxLen = self.ship.reverseAcceleration if newVector.length() > maxLen: newVector.normalize() newVector *= maxLen if __debug__: onScreenDebug.add("newVector", newVector) onScreenDebug.add("newVector length", newVector.length()) base.controlForce.setVector(newVector) assert base.controlForce.getLocalVector() == newVector assert base.controlForce.getPhysicsObject() assert base.controlForce.getPhysicsObject() == physObject #momentum = self.momentumForce.getLocalVector() #momentum *= 0.9 #self.momentumForce.setVector(momentum) # update hpr: o = physObject.getOrientation() r = LRotationf() r.setHpr(Vec3(rotation, 0.0, 0.0)) physObject.setOrientation(o * r) # sync the change: self.actorNode.updateTransform() #assert self.avatarNodePath.getH()==debugTempH-rotation messenger.send("avatarMoving") else: # even if there are no active inputs, we still might be moving assert physObject.getVelocity().length() == 0. #base.controlForce.setVector(Vec3.zero()) goForward = True #*# speed = physVel if (goForward): if physVelLen > maxSpeed: speed.normalize() speed *= maxSpeed else: if physVelLen > self.ship.maxReverseSpeed: speed.normalize() speed *= self.ship.maxReverseSpeed #speed *= 1.0 - dt * 0.05 # modify based on sail damage speed *= self.ship.Sp speed /= self.ship.maxSp ## physObject.setVelocity(speed) #rotMat=Mat3.rotateMatNormaxis(self.avatarNodePath.getH(), Vec3.up()) #speed=rotMat.xform(speed) # The momentumForce makes it feel like we are sliding on ice -- Joe # f = Vec3(self.__vel) # f.normalize() # self.momentumForce.setVector(Vec3(f*(speed.length()*0.9))) if __debug__: q1 = self.avatarNodePath.getQuat() q2 = physObject.getOrientation() q1.normalize() q2.normalize() assert q1.isSameDirection(q2) or q1.getHpr() == q2.getHpr() assert self.avatarNodePath.getPos().almostEqual( physObject.getPosition(), 0.0001) # Clear the contact vector so we can # tell if we contact something next frame self.actorNode.setContactVector(Vec3.zero()) self.__oldPosDelta = self.avatarNodePath.getPosDelta(render) self.__oldDt = dt assert hasattr(self.ship, 'worldVelocity') self.ship.worldVelocity = self.__oldPosDelta * (1 / self.__oldDt) if self.wantDebugIndicator: onScreenDebug.add("w __oldPosDelta vec", self.__oldPosDelta.pPrintValues()) onScreenDebug.add("w __oldPosDelta len", "% 10.4f" % self.__oldPosDelta.length()) onScreenDebug.add("w __oldDt", "% 10.4f" % self.__oldDt) onScreenDebug.add("w worldVelocity vec", self.ship.worldVelocity.pPrintValues()) onScreenDebug.add("w worldVelocity len", "% 10.4f" % self.ship.worldVelocity.length()) # if hasattr(self.ship, 'sailBillow'): # self.ship.sailBillow = self.sailsDeployed if hasattr(self.ship, 'currentTurning'): self.ship.currentTurning = self.currentTurning return Task.cont def doDeltaPos(self): assert self.debugPrint("doDeltaPos()") self.needToDeltaPos = 1 def setPriorParentVector(self): assert self.debugPrint("doDeltaPos()") #print "self.__oldDt", self.__oldDt, "self.__oldPosDelta", self.__oldPosDelta if __debug__: onScreenDebug.add("__oldDt", "% 10.4f" % self.__oldDt) onScreenDebug.add("self.__oldPosDelta", self.__oldPosDelta.pPrintValues()) velocity = self.__oldPosDelta * (1 / self.__oldDt ) #*4.0 # *4.0 is a hack assert self.debugPrint(" __oldPosDelta=%s" % (self.__oldPosDelta, )) assert self.debugPrint(" velocity=%s" % (velocity, )) self.priorParent.setVector(Vec3(velocity)) if __debug__: if self.wantDebugIndicator: onScreenDebug.add("velocity", velocity.pPrintValues()) def reset(self): assert self.debugPrint("reset()") self.actorNode.getPhysicsObject().resetPosition( self.avatarNodePath.getPos()) self.priorParent.setVector(Vec3.zero()) self.highMark = 0 self.actorNode.setContactVector(Vec3.zero()) if __debug__: contact = self.actorNode.getContactVector() onScreenDebug.add( "priorParent po", self.priorParent.getVector( self.actorNode.getPhysicsObject()).pPrintValues()) onScreenDebug.add("highMark", "% 10.4f" % (self.highMark, )) onScreenDebug.add("contact", contact.pPrintValues()) def getVelocity(self): return self.__vel def enableAvatarControls(self): """ Activate the arrow keys, etc. """ assert self.debugPrint("enableAvatarControls()") assert self.collisionsActive if __debug__: #self.accept("control-f3", self.spawnTest) #*# self.accept("f3", self.reset) # for debugging only. taskName = "AvatarControls-%s" % (id(self), ) # remove any old taskMgr.remove(taskName) # spawn the new task taskMgr.add(self.handleAvatarControls, taskName, 25) if self.physVelocityIndicator: taskMgr.add(self.avatarPhysicsIndicator, "AvatarControlsIndicator%s" % (id(self), ), 35) def disableAvatarControls(self): """ Ignore the arrow keys, etc. """ assert self.debugPrint("disableAvatarControls()") taskName = "AvatarControls-%s" % (id(self), ) taskMgr.remove(taskName) taskName = "AvatarControlsIndicator%s" % (id(self), ) taskMgr.remove(taskName) if __debug__: self.ignore("control-f3") #*# self.ignore("f3") if __debug__: def setupAvatarPhysicsIndicator(self): if self.wantDebugIndicator: indicator = loader.loadModel('phase_5/models/props/dagger') #self.walkControls.setAvatarPhysicsIndicator(indicator) def debugPrint(self, message): """for debugging""" return self.notify.debug(str(id(self)) + ' ' + message)
class JobManager: """ Similar to the taskMgr but designed for tasks that are CPU-intensive and/or not time-critical. Jobs run in a fixed timeslice that the JobManager is allotted each frame. """ notify = directNotify.newCategory("JobManager") # there's one task for the JobManager, all jobs run in this task TaskName = 'jobManager' def __init__(self, timeslice=None): # how long do we run per frame self._timeslice = timeslice # store the jobs in these structures to allow fast lookup by various keys # priority -> jobId -> job self._pri2jobId2job = {} # priority -> chronological list of jobIds self._pri2jobIds = {} # jobId -> priority self._jobId2pri = {} # how many timeslices to give each job; this is used to efficiently implement # the relative job priorities self._jobId2timeslices = {} # how much time did the job use beyond the allotted timeslice, used to balance # out CPU usage self._jobId2overflowTime = {} self._useOverflowTime = None # this is a generator that we use to give high-priority jobs more timeslices, # it yields jobIds in a sequence that includes high-priority jobIds more often # than low-priority self._jobIdGenerator = None self._highestPriority = Job.Priorities.Normal def destroy(self): taskMgr.remove(JobManager.TaskName) del self._pri2jobId2job def add(self, job): pri = job.getPriority() jobId = job._getJobId() # store the job in the main table self._pri2jobId2job.setdefault(pri, {}) self._pri2jobId2job[pri][jobId] = job # and also store a direct mapping from the job's ID to its priority self._jobId2pri[jobId] = pri # add the jobId onto the end of the list of jobIds for this priority self._pri2jobIds.setdefault(pri, []) self._pri2jobIds[pri].append(jobId) # record the job's relative timeslice count self._jobId2timeslices[jobId] = pri # init the overflow time tracking self._jobId2overflowTime[jobId] = 0. # reset the jobId round-robin self._jobIdGenerator = None if len(self._jobId2pri) == 1: taskMgr.add(self._process, JobManager.TaskName) self._highestPriority = pri elif pri > self._highestPriority: self._highestPriority = pri self.notify.debug('added job: %s' % job.getJobName()) def remove(self, job): jobId = job._getJobId() # look up the job's priority pri = self._jobId2pri.pop(jobId) # TODO: this removal is a linear search self._pri2jobIds[pri].remove(jobId) # remove the job from the main table del self._pri2jobId2job[pri][jobId] # clean up the job's generator, if any job._cleanupGenerator() # remove the job's timeslice count self._jobId2timeslices.pop(jobId) # remove the overflow time self._jobId2overflowTime.pop(jobId) if len(self._pri2jobId2job[pri]) == 0: del self._pri2jobId2job[pri] if pri == self._highestPriority: if len(self._jobId2pri) > 0: # calculate a new highest priority # TODO: this is not very fast priorities = self._getSortedPriorities() self._highestPriority = priorities[-1] else: taskMgr.remove(JobManager.TaskName) self._highestPriority = 0 self.notify.debug('removed job: %s' % job.getJobName()) def finish(self, job): # run this job, right now, until it finishes assert self.notify.debugCall() jobId = job._getJobId() # look up the job's priority pri = self._jobId2pri[jobId] # grab the job job = self._pri2jobId2job[pri][jobId] gen = job._getGenerator() if __debug__: job._pstats.start() job.resume() while True: try: result = gen.next() except StopIteration: # Job didn't yield Job.Done, it ran off the end and returned # treat it as if it returned Job.Done self.notify.warning('job %s never yielded Job.Done' % job) result = Job.Done if result is Job.Done: job.suspend() self.remove(job) job._setFinished() messenger.send(job.getFinishedEvent()) # job is done. break if __debug__: job._pstats.stop() # how long should we run per frame? @staticmethod def getDefaultTimeslice(): # run for 1/2 millisecond per frame by default # config is in milliseconds, this func returns value in seconds return getBase().config.GetFloat('job-manager-timeslice-ms', .5) / 1000. def getTimeslice(self): if self._timeslice: return self._timeslice return self.getDefaultTimeslice() def setTimeslice(self, timeslice): self._timeslice = timeslice def _getSortedPriorities(self): # returns all job priorities in ascending order priorities = self._pri2jobId2job.keys() priorities.sort() return priorities def _process(self, task=None): if self._useOverflowTime is None: self._useOverflowTime = config.GetBool('job-use-overflow-time', 1) if len(self._pri2jobId2job): #assert self.notify.debugCall() # figure out how long we can run endT = globalClock.getRealTime() + (self.getTimeslice() * .9) while True: if self._jobIdGenerator is None: # round-robin the jobs, giving high-priority jobs more timeslices self._jobIdGenerator = flywheel( self._jobId2timeslices.keys(), countFunc = lambda jobId: self._jobId2timeslices[jobId]) try: # grab the next jobId in the sequence jobId = self._jobIdGenerator.next() except StopIteration: self._jobIdGenerator = None continue # OK, we've selected a job to run pri = self._jobId2pri.get(jobId) if pri is None: # this job is no longer present continue # check if there's overflow time that we need to make up for if self._useOverflowTime: overflowTime = self._jobId2overflowTime[jobId] timeLeft = endT - globalClock.getRealTime() if overflowTime >= timeLeft: self._jobId2overflowTime[jobId] = max(0., overflowTime-timeLeft) # don't run any more jobs this frame, this makes up # for the extra overflow time that was used before break job = self._pri2jobId2job[pri][jobId] gen = job._getGenerator() if __debug__: job._pstats.start() job.resume() while globalClock.getRealTime() < endT: try: result = gen.next() except StopIteration: # Job didn't yield Job.Done, it ran off the end and returned # treat it as if it returned Job.Done self.notify.warning('job %s never yielded Job.Done' % job) result = Job.Done if result is Job.Sleep: job.suspend() if __debug__: job._pstats.stop() # grab the next job if there's time left break elif result is Job.Done: job.suspend() self.remove(job) job._setFinished() if __debug__: job._pstats.stop() messenger.send(job.getFinishedEvent()) # grab the next job if there's time left break else: # we've run out of time #assert self.notify.debug('timeslice end: %s, %s' % (endT, globalClock.getRealTime())) job.suspend() overflowTime = globalClock.getRealTime() - endT if overflowTime > self.getTimeslice(): self._jobId2overflowTime[jobId] += overflowTime if __debug__: job._pstats.stop() break if len(self._pri2jobId2job) == 0: # there's nothing left to do, all the jobs are done! break return task.cont def __repr__(self): s = '=======================================================' s += '\nJobManager: active jobs in descending order of priority' s += '\n=======================================================' pris = self._getSortedPriorities() if len(pris) == 0: s += '\n no jobs running' else: pris.reverse() for pri in pris: jobId2job = self._pri2jobId2job[pri] # run through the jobs at this priority in the order that they will run for jobId in self._pri2jobIds[pri]: job = jobId2job[jobId] s += '\n%5d: %s (jobId %s)' % (pri, job.getJobName(), jobId) s += '\n' return s
class State(DirectObject): notify = directNotify.newCategory("State") # this 'constant' can be used to specify that the state # can transition to any other state Any = 'ANY' # Keep a list of State objects currently in memory for # Control-C-Control-V redefining. These are just weakrefs so they # should not cause any leaks. if __debug__: import weakref States = weakref.WeakKeyDictionary() @classmethod def replaceMethod(self, oldFunction, newFunction): import new import types count = 0 for state in self.States: # Note: you can only replace methods currently enterFunc = state.getEnterFunc() exitFunc = state.getExitFunc() # print 'testing: ', state, enterFunc, exitFunc, oldFunction if type(enterFunc) == types.MethodType: if (enterFunc.im_func == oldFunction): # print 'found: ', enterFunc, oldFunction state.setEnterFunc( new.instancemethod(newFunction, enterFunc.im_self, enterFunc.im_class)) count += 1 if type(exitFunc) == types.MethodType: if (exitFunc.im_func == oldFunction): # print 'found: ', exitFunc, oldFunction state.setExitFunc( new.instancemethod(newFunction, exitFunc.im_self, exitFunc.im_class)) count += 1 return count def __init__(self, name, enterFunc=None, exitFunc=None, transitions=Any, inspectorPos=[]): """__init__(self, string, func, func, string[], inspectorPos = []) State constructor: takes name, enter func, exit func, and a list of states it can transition to (or State.Any).""" self.__name = name self.__enterFunc = enterFunc self.__exitFunc = exitFunc self.__transitions = transitions self.__FSMList = [] if __debug__: self.setInspectorPos(inspectorPos) # For redefining self.States[self] = 1 # setters and getters def getName(self): return (self.__name) def setName(self, stateName): self.__name = stateName def getEnterFunc(self): return (self.__enterFunc) def setEnterFunc(self, stateEnterFunc): self.__enterFunc = stateEnterFunc def getExitFunc(self): return (self.__exitFunc) def setExitFunc(self, stateExitFunc): self.__exitFunc = stateExitFunc def transitionsToAny(self): """ returns true if State defines transitions to any other state """ return self.__transitions is State.Any def getTransitions(self): """ warning -- if the state transitions to any other state, returns an empty list (falsely implying that the state has no transitions) see State.transitionsToAny() """ if self.transitionsToAny(): return [] return self.__transitions def isTransitionDefined(self, otherState): if self.transitionsToAny(): return 1 # if we're given a state object, get its name instead if type(otherState) != type(''): otherState = otherState.getName() return (otherState in self.__transitions) def setTransitions(self, stateTransitions): """setTransitions(self, string[])""" self.__transitions = stateTransitions def addTransition(self, transition): """addTransitions(self, string)""" if not self.transitionsToAny(): self.__transitions.append(transition) else: State.notify.warning( 'attempted to add transition %s to state that ' 'transitions to any state') if __debug__: def getInspectorPos(self): """getInspectorPos(self)""" return (self.__inspectorPos) def setInspectorPos(self, inspectorPos): """setInspectorPos(self, [x, y])""" self.__inspectorPos = inspectorPos # support for HFSMs def getChildren(self): """ Return the list of child FSMs """ return (self.__FSMList) def setChildren(self, FSMList): """setChildren(self, ClassicFSM[]) Set the children to given list of FSMs """ self.__FSMList = FSMList def addChild(self, ClassicFSM): """ Add the given ClassicFSM to list of child FSMs """ self.__FSMList.append(ClassicFSM) def removeChild(self, ClassicFSM): """ Remove the given ClassicFSM from list of child FSMs """ if ClassicFSM in self.__FSMList: self.__FSMList.remove(ClassicFSM) def hasChildren(self): """ Return true if state has child FSMs """ return len(self.__FSMList) > 0 def __enterChildren(self, argList): """ Enter all child FSMs """ for fsm in self.__FSMList: # Check to see if the child fsm is already in a state # if it is, politely request the initial state if fsm.getCurrentState(): # made this 'conditional_request()' instead of 'request()' to avoid warning when # loading minigames where rules->frameworkInit transition doesnt exist and you # dont want to add it since it results in hanging the game fsm.conditional_request((fsm.getInitialState()).getName()) # If it has no current state, I assume this means it # has never entered the initial state, so enter it # explicitly else: fsm.enterInitialState() def __exitChildren(self, argList): """ Exit all child FSMs """ for fsm in self.__FSMList: fsm.request((fsm.getFinalState()).getName()) # basic State functionality def enter(self, argList=[]): """ Call the enter function for this state """ # enter child FSMs first. It is assumed these have a start # state that is safe to enter self.__enterChildren(argList) if (self.__enterFunc != None): apply(self.__enterFunc, argList) def exit(self, argList=[]): """ Call the exit function for this state """ # first exit child FSMs self.__exitChildren(argList) # call exit function if it exists if (self.__exitFunc != None): apply(self.__exitFunc, argList) def __str__(self): return "State: name = %s, enter = %s, exit = %s, trans = %s, children = %s" %\ (self.__name, self.__enterFunc, self.__exitFunc, self.__transitions, self.__FSMList)
class LandingPage: notify = directNotify.newCategory("LandingPage") def __init__(self): self.headerTemplate = LandingPageHTML.header self.footerTemplate = LandingPageHTML.footer self.menu = {} self.uriToTitle = {} self.quickStats = [[],{}] self.addQuickStat("Pages Served", 0, 0) self.favicon = self.readFavIcon() def readFavIcon(self): vfs = VirtualFileSystem.getGlobalPtr() filename = Filename('favicon.ico') searchPath = DSearchPath() searchPath.appendDirectory(Filename('.')) searchPath.appendDirectory(Filename('etc')) searchPath.appendDirectory(Filename.fromOsSpecific(os.path.expandvars('$DIRECT/src/http'))) searchPath.appendDirectory(Filename.fromOsSpecific(os.path.expandvars('direct/src/http'))) searchPath.appendDirectory(Filename.fromOsSpecific(os.path.expandvars('direct/http'))) found = vfs.resolveFilename(filename,searchPath) if not found: raise "Couldn't find direct/http/favicon.ico" return vfs.readFile(filename, 1) def addTab(self, title, uri): self.menu[title] = uri self.uriToTitle[uri] = title def getMenu(self, activeTab): return LandingPageHTML.getTabs(self.menu,activeTab) def getHeader(self, activeTab = "Main"): s = self.headerTemplate % {'titlestring' : LandingPageHTML.title, 'menustring' : self.getMenu(activeTab)} return s def getFooter(self): return self.footerTemplate % {'contact' : LandingPageHTML.contactInfo} def getServicesPage(self, uriToHandler): output = "" uriList = uriToHandler.keys() uriList.sort() autoList = [] if "/" in uriList: uriList.remove("/") autoList.append("/") if "/services" in uriList: uriList.remove("/services") autoList.append("/services") if "/default.css" in uriList: uriList.remove("/default.css") autoList.append("/default.css") if "/favicon.ico" in uriList: uriList.remove("/favicon.ico") autoList.append("/favicon.ico") output += LandingPageHTML.getURITable(title="Application",uriList=uriList,uriToHandler=uriToHandler) output += LandingPageHTML.getURITable(title="Admin",uriList=autoList,uriToHandler=uriToHandler) return output def populateMainPage(self, body): LandingPageHTML.mainPageBody = body def setTitle(self, title): if LandingPageHTML.title == LandingPageHTML.defaultTitle: LandingPageHTML.title = title else: LandingPageHTML.title = LandingPageHTML.title + " + " + title def setDescription(self,desc): if LandingPageHTML.description == LandingPageHTML.defaultDesc: LandingPageHTML.description = desc else: LandingPageHTML.description = LandingPageHTML.description + "</P>\n<P>" + desc def setContactInfo(self,info): LandingPageHTML.contactInfo = info def getDescription(self): return LandingPageHTML.description def getQuickStatsTable(self): return LandingPageHTML.getQuickStatsTable(self.quickStats) def getMainPage(self): return LandingPageHTML.mainPageBody % {"description" : self.getDescription(), "quickstats" : self.getQuickStatsTable()} def getStyleSheet(self): return LandingPageHTML.stylesheet def getFavIcon(self): return self.favicon def skin(self, body, uri): title = self.uriToTitle.get(uri,"Services") return self.getHeader(title) + body + self.getFooter() def addQuickStat(self,item,value,position): if item in self.quickStats[1]: assert self.notify.warning("Ignoring duplicate addition of quickstat %s." % item) return self.quickStats[0].insert(position,item) self.quickStats[1][item] = value def updateQuickStat(self,item,value): assert item in self.quickStats[1] self.quickStats[1][item] = value def incrementQuickStat(self,item): assert item in self.quickStats[1] self.quickStats[1][item] += 1
class GarbageReport(Job): """Detects leaked Python objects (via gc.collect()) and reports on garbage items, garbage-to-garbage references, and garbage cycles. If you just want to dump the report to the log, use GarbageLogger.""" notify = directNotify.newCategory("GarbageReport") NotGarbage = 'NG' def __init__(self, name, log=True, verbose=False, fullReport=False, findCycles=True, threaded=False, doneCallback=None, autoDestroy=False, priority=None, safeMode=False, delOnly=False, collect=True): # if autoDestroy is True, GarbageReport will self-destroy after logging # if false, caller is responsible for calling destroy() # if threaded is True, processing will be performed over multiple frames # if collect is False, we assume that the caller just did a collect and the results # are still in gc.garbage Job.__init__(self, name) # stick the arguments onto a ScratchPad so we can delete them all at once self._args = ScratchPad(name=name, log=log, verbose=verbose, fullReport=fullReport, findCycles=findCycles, doneCallback=doneCallback, autoDestroy=autoDestroy, safeMode=safeMode, delOnly=delOnly, collect=collect) if priority is not None: self.setPriority(priority) jobMgr.add(self) if not threaded: jobMgr.finish(self) def run(self): # do the garbage collection oldFlags = gc.get_debug() if self._args.delOnly: # do a collect without SAVEALL, to identify the instances that are involved in # cycles with instances that define __del__ # cycles that do not involve any instances that define __del__ are cleaned up # automatically by Python, but they also appear in gc.garbage when SAVEALL is set gc.set_debug(0) if self._args.collect: gc.collect() garbageInstances = gc.garbage[:] del gc.garbage[:] # only yield if there's more time-consuming work to do, # if there's no garbage, give instant feedback if len(garbageInstances) > 0: yield None # don't repr the garbage list if we don't have to if self.notify.getDebug(): self.notify.debug('garbageInstances == %s' % fastRepr(garbageInstances)) self.numGarbageInstances = len(garbageInstances) # grab the ids of the garbage instances (objects with __del__) self.garbageInstanceIds = set() for i in xrange(len(garbageInstances)): self.garbageInstanceIds.add(id(garbageInstances[i])) if not (i % 20): yield None # then release the list of instances so that it doesn't interfere with the gc.collect() below del garbageInstances else: self.garbageInstanceIds = set() # do a SAVEALL pass so that we have all of the objects involved in legitimate garbage cycles # without SAVEALL, gc.garbage only contains objects with __del__ methods gc.set_debug(gc.DEBUG_SAVEALL) if self._args.collect: gc.collect() self.garbage = gc.garbage[:] del gc.garbage[:] # only yield if there's more time-consuming work to do, # if there's no garbage, give instant feedback if len(self.garbage) > 0: yield None # don't repr the garbage list if we don't have to if self.notify.getDebug(): self.notify.debug('self.garbage == %s' % fastRepr(self.garbage)) gc.set_debug(oldFlags) self.numGarbage = len(self.garbage) # only yield if there's more time-consuming work to do, # if there's no garbage, give instant feedback if self.numGarbage > 0: yield None if self._args.verbose: self.notify.info('found %s garbage items' % self.numGarbage) """ spammy # print the types of the garbage first, in case the repr of an object # causes a crash if self.numGarbage > 0: self.notify.info('TYPES ONLY (this is only needed if a crash occurs before GarbageReport finishes):') for result in printNumberedTypesGen(self.garbage): yield None """ # Py obj id -> garbage list index self._id2index = {} self.referrersByReference = {} self.referrersByNumber = {} self.referentsByReference = {} self.referentsByNumber = {} self.cycles = [] self.cyclesBySyntax = [] self.uniqueCycleSets = set() self.cycleIds = set() # make the id->index table to speed up the next steps for i in xrange(self.numGarbage): self._id2index[id(self.garbage[i])] = i if not (i % 20): yield None # grab the referrers (pointing to garbage) if self._args.fullReport and (self.numGarbage != 0): if self._args.verbose: self.notify.info('getting referrers...') for i in xrange(self.numGarbage): yield None for result in self._getReferrers(self.garbage[i]): yield None byNum, byRef = result self.referrersByNumber[i] = byNum self.referrersByReference[i] = byRef # grab the referents (pointed to by garbage) if self.numGarbage > 0: if self._args.verbose: self.notify.info('getting referents...') for i in xrange(self.numGarbage): yield None for result in self._getReferents(self.garbage[i]): yield None byNum, byRef = result self.referentsByNumber[i] = byNum self.referentsByReference[i] = byRef # find the cycles if self._args.findCycles and self.numGarbage > 0: if self._args.verbose: self.notify.info('calculating cycles...') for i in xrange(self.numGarbage): yield None for newCycles in self._getCycles(i, self.uniqueCycleSets): yield None self.cycles.extend(newCycles) # create a representation of the cycle in human-readable form newCyclesBySyntax = [] for cycle in newCycles: cycleBySyntax = '' objs = [] # leave off the last index, it's a repeat of the first index for index in cycle[:-1]: objs.append(self.garbage[index]) yield None # make the list repeat so we can safely iterate off the end numObjs = len(objs) - 1 objs.extend(objs) # state variables for our loop below numToSkip = 0 objAlreadyRepresented = False # if cycle starts off with an instance dict, start with the instance instead startIndex = 0 # + 1 to include a reference back to the first object endIndex = numObjs + 1 if type(objs[-1]) is types.InstanceType and type( objs[0]) is types.DictType: startIndex -= 1 endIndex -= 1 for index in xrange(startIndex, endIndex): if numToSkip: numToSkip -= 1 continue obj = objs[index] if type(obj) is types.InstanceType: if not objAlreadyRepresented: cycleBySyntax += '%s' % obj.__class__.__name__ cycleBySyntax += '.' # skip past the instance dict and get the member obj numToSkip += 1 member = objs[index + 2] for key, value in obj.__dict__.iteritems(): if value is member: break yield None else: key = '<unknown member name>' cycleBySyntax += '%s' % key objAlreadyRepresented = True elif type(obj) is types.DictType: cycleBySyntax += '{' # get object referred to by dict val = objs[index + 1] for key, value in obj.iteritems(): if value is val: break yield None else: key = '<unknown key>' cycleBySyntax += '%s}' % fastRepr(key) objAlreadyRepresented = True elif type(obj) in (types.TupleType, types.ListType): brackets = { types.TupleType: '()', types.ListType: '[]', }[type(obj)] # get object being referenced by container nextObj = objs[index + 1] cycleBySyntax += brackets[0] for index in xrange(len(obj)): if obj[index] is nextObj: index = str(index) break yield None else: index = '<unknown index>' cycleBySyntax += '%s%s' % (index, brackets[1]) objAlreadyRepresented = True else: cycleBySyntax += '%s --> ' % itype(obj) objAlreadyRepresented = False newCyclesBySyntax.append(cycleBySyntax) yield None self.cyclesBySyntax.extend(newCyclesBySyntax) # if we're not doing a full report, add this cycle's IDs to the master set if not self._args.fullReport: for cycle in newCycles: yield None self.cycleIds.update(set(cycle)) self.numCycles = len(self.cycles) if self._args.findCycles: s = [ '===== GarbageReport: \'%s\' (%s %s) =====' % (self._args.name, self.numCycles, choice(self.numCycles == 1, 'cycle', 'cycles')) ] else: s = ['===== GarbageReport: \'%s\' =====' % (self._args.name)] if self.numGarbage > 0: # make a list of the ids we will actually be printing if self._args.fullReport: garbageIndices = range(self.numGarbage) else: garbageIndices = list(self.cycleIds) garbageIndices.sort() numGarbage = len(garbageIndices) # log each individual item with a number in front of it if not self._args.fullReport: abbrev = '(abbreviated) ' else: abbrev = '' s.append('===== Garbage Items %s=====' % abbrev) digits = 0 n = numGarbage while n > 0: yield None digits += 1 n /= 10 digits = digits format = '%0' + '%s' % digits + 'i:%s \t%s' for i in xrange(numGarbage): yield None idx = garbageIndices[i] if self._args.safeMode: # in safe mode, don't try to repr any of the objects objStr = repr(itype(self.garbage[idx])) else: objStr = fastRepr(self.garbage[idx]) maxLen = 5000 if len(objStr) > maxLen: snip = '<SNIP>' objStr = '%s%s' % (objStr[:(maxLen - len(snip))], snip) s.append(format % (idx, itype(self.garbage[idx]), objStr)) # also log the types of the objects s.append('===== Garbage Item Types %s=====' % abbrev) for i in xrange(numGarbage): yield None idx = garbageIndices[i] objStr = str(deeptype(self.garbage[idx])) maxLen = 5000 if len(objStr) > maxLen: snip = '<SNIP>' objStr = '%s%s' % (objStr[:(maxLen - len(snip))], snip) s.append(format % (idx, itype(self.garbage[idx]), objStr)) if self._args.findCycles: s.append('===== Garbage Cycles (Garbage Item Numbers) =====') ac = AlphabetCounter() for i in xrange(self.numCycles): yield None s.append('%s:%s' % (ac.next(), self.cycles[i])) if self._args.findCycles: s.append('===== Garbage Cycles (Python Syntax) =====') ac = AlphabetCounter() for i in xrange(len(self.cyclesBySyntax)): yield None s.append('%s:%s' % (ac.next(), self.cyclesBySyntax[i])) if self._args.fullReport: format = '%0' + '%s' % digits + 'i:%s' s.append( '===== Referrers By Number (what is referring to garbage item?) =====' ) for i in xrange(numGarbage): yield None s.append(format % (i, self.referrersByNumber[i])) s.append( '===== Referents By Number (what is garbage item referring to?) =====' ) for i in xrange(numGarbage): yield None s.append(format % (i, self.referentsByNumber[i])) s.append( '===== Referrers (what is referring to garbage item?) =====' ) for i in xrange(numGarbage): yield None s.append(format % (i, self.referrersByReference[i])) s.append( '===== Referents (what is garbage item referring to?) =====' ) for i in xrange(numGarbage): yield None s.append(format % (i, self.referentsByReference[i])) self._report = s if self._args.log: self.printingBegin() for i in xrange(len(self._report)): if self.numGarbage > 0: yield None self.notify.info(self._report[i]) self.printingEnd() yield Job.Done def finished(self): if self._args.doneCallback: self._args.doneCallback(self) if self._args.autoDestroy: self.destroy() def destroy(self): #print 'GarbageReport.destroy' del self._args del self.garbage # don't get rid of these, we might need them #del self.numGarbage #del self.numCycles del self.referrersByReference del self.referrersByNumber del self.referentsByReference del self.referentsByNumber if hasattr(self, 'cycles'): del self.cycles del self._report if hasattr(self, '_reportStr'): del self._reportStr Job.destroy(self) def getNumCycles(self): # if the job hasn't run yet, we don't have a numCycles yet return self.numCycles def getDesc2numDict(self): # dict of python-syntax leak -> number of that type of leak desc2num = {} for cycleBySyntax in self.cyclesBySyntax: desc2num.setdefault(cycleBySyntax, 0) desc2num[cycleBySyntax] += 1 return desc2num def getGarbage(self): return self.garbage def getReport(self): if not hasattr(self, '_reportStr'): self._reportStr = '' for str in self._report: self._reportStr += '\n' + str return self._reportStr def _getReferrers(self, obj): # referrers (pointing to garbage) # returns two lists, first by index into gc.garbage, second by # direct reference yield None byRef = gc.get_referrers(obj) yield None # look to see if each referrer is another garbage item byNum = [] for i in xrange(len(byRef)): if not (i % 20): yield None referrer = byRef[i] num = self._id2index.get(id(referrer), None) byNum.append(num) yield byNum, byRef def _getReferents(self, obj): # referents (pointed to by garbage) # returns two lists, first by index into gc.garbage, second by # direct reference yield None byRef = gc.get_referents(obj) yield None # look to see if each referent is another garbage item byNum = [] for i in xrange(len(byRef)): if not (i % 20): yield None referent = byRef[i] num = self._id2index.get(id(referent), None) byNum.append(num) yield byNum, byRef def _getNormalizedCycle(self, cycle): # returns a representation of a cycle (list of indices) that will be # reliably derived from a unique cycle regardless of ordering # this lets us detect duplicate cycles that appear different because of # which element appears first if len(cycle) == 0: return cycle min = 1 << 30 minIndex = None for i in xrange(len(cycle)): elem = cycle[i] if elem < min: min = elem minIndex = i return cycle[minIndex:] + cycle[:minIndex] def _getCycles(self, index, uniqueCycleSets=None): # detect garbage cycles for a particular item of garbage assert self.notify.debugCall() # returns list of lists, sublists are garbage reference cycles cycles = [] # this lets us eliminate duplicate cycles if uniqueCycleSets is None: uniqueCycleSets = set() stateStack = Stack() rootId = index # check if the root object is one of the garbage instances (has __del__) objId = id(self.garbage[rootId]) numDelInstances = choice(objId in self.garbageInstanceIds, 1, 0) stateStack.push(([rootId], rootId, numDelInstances, 0)) while True: yield None if len(stateStack) == 0: break candidateCycle, curId, numDelInstances, resumeIndex = stateStack.pop( ) if self.notify.getDebug(): if self._args.delOnly: print 'restart: %s root=%s cur=%s numDelInstances=%s resume=%s' % ( candidateCycle, rootId, curId, numDelInstances, resumeIndex) else: print 'restart: %s root=%s cur=%s resume=%s' % ( candidateCycle, rootId, curId, resumeIndex) for index in xrange(resumeIndex, len(self.referentsByNumber[curId])): yield None refId = self.referentsByNumber[curId][index] if self.notify.getDebug(): print ' : %s -> %s' % (curId, refId) if refId == rootId: # we found a cycle! mark it down and move on to the next refId normCandidateCycle = self._getNormalizedCycle( candidateCycle) normCandidateCycleTuple = tuple(normCandidateCycle) if not normCandidateCycleTuple in uniqueCycleSets: # cycles with no instances that define __del__ will be # cleaned up by Python if (not self._args.delOnly) or numDelInstances >= 1: if self.notify.getDebug(): print ' FOUND: ', normCandidateCycle + [ normCandidateCycle[0], ] cycles.append(normCandidateCycle + [ normCandidateCycle[0], ]) uniqueCycleSets.add(normCandidateCycleTuple) elif refId in candidateCycle: pass elif refId is not None: # check if this object is one of the garbage instances (has __del__) objId = id(self.garbage[refId]) numDelInstances += choice(objId in self.garbageInstanceIds, 1, 0) # this refId does not complete a cycle. Mark down # where we are in this list of referents, then # start looking through the referents of the new refId stateStack.push((list(candidateCycle), curId, numDelInstances, index + 1)) stateStack.push((list(candidateCycle) + [refId], refId, numDelInstances, 0)) break yield cycles
class ObjectReport: """report on every Python object in the current process""" notify = directNotify.newCategory('ObjectReport') def __init__(self, name, log=True): gr = GarbageReport.GarbageReport('ObjectReport\'s GarbageReport: %s' % name, log=log) gr.destroy() del gr self._name = name self._pool = ObjectPool.ObjectPool(self._getObjectList()) #ExclusiveObjectPool.addExclObjs(self, self._pool, self._name) if log: self.notify.info('===== ObjectReport: \'%s\' =====\n%s' % (self._name, self.typeFreqStr())) def destroy(self): #ExclusiveObjectPool.removeExclObjs(self, self._pool, self._name) self._pool.destroy() del self._pool del self._name def typeFreqStr(self): return self._pool.typeFreqStr() def diff(self, other): return self._pool.diff(other._pool) def getObjectPool(self): return self._pool def _getObjectList(self): if hasattr(sys, 'getobjects'): return sys.getobjects(0) else: gc.collect() # grab gc's object list gc_objects = gc.get_objects() # use get_referents to find everything else objects = gc_objects objects.append(__builtin__.__dict__) nextObjList = gc_objects found = set() found.add(id(objects)) found.add(id(found)) found.add(id(gc_objects)) for obj in objects: found.add(id(obj)) # repeatedly call get_referents until we can't find any more objects while len(nextObjList): curObjList = nextObjList nextObjList = [] for obj in curObjList: refs = gc.get_referents(obj) for ref in refs: if id(ref) not in found: found.add(id(ref)) objects.append(ref) nextObjList.append(ref) return objects """
from panda3d.pandac import ConfigConfigureGetConfigConfigShowbase as config from panda3d.direct.directnotify.DirectNotifyGlobal import directNotify from panda3d.direct.showbase.PythonUtil import fastRepr from exceptions import Exception import sys import types import traceback notify = directNotify.newCategory("ExceptionVarDump") reentry = 0 def _varDump__init__(self, *args, **kArgs): global reentry if reentry > 0: return reentry += 1 # frame zero is this frame f = 1 self._savedExcString = None self._savedStackFrames = [] while True: try: frame = sys._getframe(f) except ValueError, e: break else: f += 1 self._savedStackFrames.append(frame) self._moved__init__(*args, **kArgs)
class ClassicFSM(DirectObject): """ Finite State Machine class. This module and class exist only for backward compatibility with existing code. New code should use the FSM class instead. """ # create ClassicFSM DirectNotify category notify = directNotify.newCategory("ClassicFSM") # special methods # these are flags that tell the ClassicFSM what to do when an # undefined transition is requested: ALLOW = 0 # print a warning, and do the transition DISALLOW = 1 # silently ignore the request (don't do the transition) DISALLOW_VERBOSE = 2 # print a warning, and don't do the transition ERROR = 3 # print an error message and raise an exception def __init__(self, name, states=[], initialStateName=None, finalStateName=None, onUndefTransition=DISALLOW_VERBOSE): """__init__(self, string, State[], string, string, int) ClassicFSM constructor: takes name, list of states, initial state and final state as: fsm = ClassicFSM.ClassicFSM('stopLight', [State.State('red', enterRed, exitRed, ['green']), State.State('yellow', enterYellow, exitYellow, ['red']), State.State('green', enterGreen, exitGreen, ['yellow'])], 'red', 'red') each state's last argument, a list of allowed state transitions, is optional; if left out (or explicitly specified to be State.State.Any) then any transition from the state is 'defined' and allowed 'onUndefTransition' flag determines behavior when undefined transition is requested; see flag definitions above """ self.setName(name) self.setStates(states) self.setInitialState(initialStateName) self.setFinalState(finalStateName) self.onUndefTransition = onUndefTransition # Flag to see if we are inspecting self.inspecting = 0 # We do not enter the initial state to separate # construction from activation self.__currentState = None # We set this while we are modifying the state. No one else # should recursively attempt to modify the state while we are # doing this. self.__internalStateInFlux = 0 if __debug__: global _debugFsms _debugFsms[name] = weakref.ref(self) # I know this isn't how __repr__ is supposed to be used, but it # is nice and convenient. def __repr__(self): return self.__str__() def __str__(self): """ Print out something useful about the fsm """ currentState = self.getCurrentState() if currentState: str = ("ClassicFSM " + self.getName() + ' in state "' + currentState.getName() + '"') else: str = ("ClassicFSM " + self.getName() + ' not in any state') return str def enterInitialState(self, argList=[]): assert not self.__internalStateInFlux if self.__currentState == self.__initialState: return assert self.__currentState == None self.__internalStateInFlux = 1 self.__enter(self.__initialState, argList) assert not self.__internalStateInFlux # setters and getters def getName(self): return (self.__name) def setName(self, name): self.__name = name def getStates(self): return self.__states.values() def setStates(self, states): """setStates(self, State[])""" # Make a dictionary from stateName -> state self.__states = {} for state in states: self.__states[state.getName()] = state def addState(self, state): self.__states[state.getName()] = state def getInitialState(self): return (self.__initialState) def setInitialState(self, initialStateName): self.__initialState = self.getStateNamed(initialStateName) def getFinalState(self): return (self.__finalState) def setFinalState(self, finalStateName): self.__finalState = self.getStateNamed(finalStateName) def requestFinalState(self): self.request(self.__finalState.getName()) def getCurrentState(self): return (self.__currentState) # lookup funcs def getStateNamed(self, stateName): """ Return the state with given name if found, issue warning otherwise """ state = self.__states.get(stateName) if state: return state else: ClassicFSM.notify.warning( "[%s]: getStateNamed: %s, no such state" % (self.__name, stateName)) def hasStateNamed(self, stateName): """ Return True if stateName is a valid state, False otherwise. """ result = False state = self.__states.get(stateName) if state: result = True return result # basic ClassicFSM functionality def __exitCurrent(self, argList): """ Exit the current state """ assert self.__internalStateInFlux assert ClassicFSM.notify.debug( "[%s]: exiting %s" % (self.__name, self.__currentState.getName())) self.__currentState.exit(argList) # Only send the state change event if we are inspecting it # If this event turns out to be generally useful, we can # turn it on all the time, but for now nobody else is using it if self.inspecting: messenger.send(self.getName() + '_' + self.__currentState.getName() + '_exited') self.__currentState = None def __enter(self, aState, argList=[]): """ Enter a given state, if it exists """ assert self.__internalStateInFlux stateName = aState.getName() if (stateName in self.__states): assert ClassicFSM.notify.debug("[%s]: entering %s" % (self.__name, stateName)) self.__currentState = aState # Only send the state change event if we are inspecting it # If this event turns out to be generally useful, we can # turn it on all the time, but for now nobody else is using it if self.inspecting: messenger.send(self.getName() + '_' + stateName + '_entered') # Once we begin entering the new state, we're allow to # recursively request a transition to another state. # Indicate this by marking our internal state no longer in # flux. self.__internalStateInFlux = 0 aState.enter(argList) else: # notify.error is going to raise an exception; reset the # flux flag first self.__internalStateInFlux = 0 ClassicFSM.notify.error("[%s]: enter: no such state" % (self.__name)) def __transition(self, aState, enterArgList=[], exitArgList=[]): """ Exit currentState and enter given one """ assert not self.__internalStateInFlux self.__internalStateInFlux = 1 self.__exitCurrent(exitArgList) self.__enter(aState, enterArgList) assert not self.__internalStateInFlux def request(self, aStateName, enterArgList=[], exitArgList=[], force=0): """ Attempt transition from currentState to given one. Return true is transition exists to given state, false otherwise. """ # If you trigger this assertion failure, you must have # recursively requested a state transition from within the # exitState() function for the previous state. This is not # supported because we're not fully transitioned into the new # state yet. assert not self.__internalStateInFlux if not self.__currentState: # Make this a warning for now ClassicFSM.notify.warning( "[%s]: request: never entered initial state" % (self.__name)) self.__currentState = self.__initialState if isinstance(aStateName, types.StringType): aState = self.getStateNamed(aStateName) else: # Allow the caller to pass in a state in itself, not just # the name of a state. aState = aStateName aStateName = aState.getName() if aState == None: ClassicFSM.notify.error("[%s]: request: %s, no such state" % (self.__name, aStateName)) # is the transition defined? if it isn't, should we allow it? transitionDefined = self.__currentState.isTransitionDefined(aStateName) transitionAllowed = transitionDefined if self.onUndefTransition == ClassicFSM.ALLOW: transitionAllowed = 1 if not transitionDefined: # the transition is not defined, but we're going to do it # anyway. print a warning. ClassicFSM.notify.warning( "[%s]: performing undefined transition from %s to %s" % (self.__name, self.__currentState.getName(), aStateName)) if transitionAllowed or force: self.__transition(aState, enterArgList, exitArgList) return 1 # We can implicitly always transition to our final state. elif (aStateName == self.__finalState.getName()): if (self.__currentState == self.__finalState): # Do not do the transition if we are already in the # final state assert ClassicFSM.notify.debug( "[%s]: already in final state: %s" % (self.__name, aStateName)) return 1 else: # Force a transition to allow for cleanup assert ClassicFSM.notify.debug( "[%s]: implicit transition to final state: %s" % (self.__name, aStateName)) self.__transition(aState, enterArgList, exitArgList) return 1 # are we already in this state? elif (aStateName == self.__currentState.getName()): assert ClassicFSM.notify.debug( "[%s]: already in state %s and no self transition" % (self.__name, aStateName)) return 0 else: msg = ("[%s]: no transition exists from %s to %s" % (self.__name, self.__currentState.getName(), aStateName)) if self.onUndefTransition == ClassicFSM.ERROR: ClassicFSM.notify.error(msg) elif self.onUndefTransition == ClassicFSM.DISALLOW_VERBOSE: ClassicFSM.notify.warning(msg) return 0 def forceTransition(self, aStateName, enterArgList=[], exitArgList=[]): """ force a transition -- for debugging ONLY """ self.request(aStateName, enterArgList, exitArgList, force=1) def conditional_request(self, aStateName, enterArgList=[], exitArgList=[]): """ 'if this transition is defined, do it' Attempt transition from currentState to given one, if it exists. Return true if transition exists to given state, false otherwise. It is NOT an error/warning to attempt a cond_request if the transition doesn't exist. This lets people be sloppy about ClassicFSM transitions, letting the same fn be used for different states that may not have the same out transitions. """ assert not self.__internalStateInFlux if not self.__currentState: # Make this a warning for now ClassicFSM.notify.warning( "[%s]: request: never entered initial state" % (self.__name)) self.__currentState = self.__initialState if isinstance(aStateName, types.StringType): aState = self.getStateNamed(aStateName) else: # Allow the caller to pass in a state in itself, not just # the name of a state. aState = aStateName aStateName = aState.getName() if aState == None: ClassicFSM.notify.error("[%s]: request: %s, no such state" % (self.__name, aStateName)) transitionDefined = ( self.__currentState.isTransitionDefined(aStateName) or aStateName in [self.__currentState.getName(), self.__finalState.getName()]) if transitionDefined: return self.request(aStateName, enterArgList, exitArgList) else: assert ClassicFSM.notify.debug( "[%s]: condition_request: %s, transition doesnt exist" % (self.__name, aStateName)) return 0 def view(self): from panda3d.direct.tkpanels import FSMInspector FSMInspector.FSMInspector(self) def isInternalStateInFlux(self): return self.__internalStateInFlux
class ObjectPool: """manipulate a pool of Python objects""" notify = directNotify.newCategory('ObjectPool') def __init__(self, objects): self._objs = list(objects) self._type2objs = {} self._count2types = {} self._len2obj = {} type2count = {} for obj in self._objs: typ = itype(obj) type2count.setdefault(typ, 0) type2count[typ] += 1 self._type2objs.setdefault(typ, []) self._type2objs[typ].append(obj) try: self._len2obj[len(obj)] = obj except: pass self._count2types = invertDictLossless(type2count) def _getInternalObjs(self): return (self._objs, self._type2objs, self._count2types) def destroy(self): del self._objs del self._type2objs del self._count2types def getTypes(self): return self._type2objs.keys() def getObjsOfType(self, type): return self._type2objs.get(type, []) def printObjsOfType(self, type): for obj in self._type2objs.get(type, []): print repr(obj) def diff(self, other): """print difference between this pool and 'other' pool""" thisId2obj = {} otherId2obj = {} for obj in self._objs: thisId2obj[id(obj)] = obj for obj in other._objs: otherId2obj[id(obj)] = obj thisIds = set(thisId2obj.keys()) otherIds = set(otherId2obj.keys()) lostIds = thisIds.difference(otherIds) gainedIds = otherIds.difference(thisIds) del thisIds del otherIds lostObjs = [] for i in lostIds: lostObjs.append(thisId2obj[i]) gainedObjs = [] for i in gainedIds: gainedObjs.append(otherId2obj[i]) return Diff(self.__class__(lostObjs), self.__class__(gainedObjs)) def typeFreqStr(self): s = 'Object Pool: Type Frequencies' s += '\n=============================' counts = list(set(self._count2types.keys())) counts.sort() counts.reverse() for count in counts: types = makeList(self._count2types[count]) for typ in types: s += '\n%s\t%s' % (count, typ) return s def printObjsByType(self, printReferrers=False): print 'Object Pool: Objects By Type' print '\n============================' counts = list(set(self._count2types.keys())) counts.sort() # print types with the smallest number of instances first, in case # there's a large group that waits a long time before printing #counts.reverse() for count in counts: types = makeList(self._count2types[count]) for typ in types: print 'TYPE: %s, %s objects' % (repr(typ), len(self._type2objs[typ])) if printReferrers: for line in getNumberedTypedSortedStringWithReferrersGen( self._type2objs[typ]): print line else: print getNumberedTypedSortedString(self._type2objs[typ]) def containerLenStr(self): s = 'Object Pool: Container Lengths' s += '\n==============================' lengths = list(self._len2obj.keys()) lengths.sort() lengths.reverse() for count in counts: pass def printReferrers(self, numEach=3): """referrers of the first few of each type of object""" counts = list(set(self._count2types.keys())) counts.sort() counts.reverse() for count in counts: types = makeList(self._count2types[count]) for typ in types: print '\n\nTYPE: %s' % repr(typ) for i in xrange(min(numEach, len(self._type2objs[typ]))): obj = self._type2objs[typ][i] print '\nOBJ: %s\n' % safeRepr(obj) referrers = gc.get_referrers(obj) print '%s REFERRERS:\n' % len(referrers) if len(referrers): print getNumberedTypedString(referrers, maxLen=80, numPrefix='REF') else: print '<No Referrers>' def __len__(self): return len(self._objs)
class DistributedObjectGlobalUD(DistributedObjectUD): notify = directNotify.newCategory('DistributedObjectGlobalUD') doNotDeallocateChannel = 1 isGlobalDistObj = 1 def __init__(self, air): DistributedObjectUD.__init__(self, air) self.ExecNamespace = {"self": self} def announceGenerate(self): self.air.registerForChannel(self.doId) DistributedObjectUD.announceGenerate(self) def delete(self): self.air.unregisterForChannel(self.doId) ## self.air.removeDOFromTables(self) DistributedObjectUD.delete(self) def execCommand(self, command, mwMgrId, avId, zoneId): text = self.__execMessage(command) print text dclass = uber.air.dclassesByName.get("PiratesMagicWordManagerAI") dg = dclass.aiFormatUpdate("setMagicWordResponse", mwMgrId, (1 << 32) + avId, uber.air.ourChannel, [text]) uber.air.send(dg) def __execMessage(self, message): if not self.ExecNamespace: # Import some useful variables into the ExecNamespace initially. exec 'from panda3d.pandac import *' in globals( ), self.ExecNamespace #self.importExecNamespace() # Now try to evaluate the expression using ChatInputNormal.ExecNamespace as # the local namespace. try: return str(eval(message, globals(), self.ExecNamespace)) except SyntaxError: # Maybe it's only a statement, like "x = 1", or # "import math". These aren't expressions, so eval() # fails, but they can be exec'ed. try: exec message in globals(), self.ExecNamespace return 'ok' except: exception = sys.exc_info()[0] extraInfo = sys.exc_info()[1] if extraInfo: return str(extraInfo) else: return str(exception) except: exception = sys.exc_info()[0] extraInfo = sys.exc_info()[1] if extraInfo: return str(extraInfo) else: return str(exception)
class DistributedObjectUD(DistributedObjectBase): notify = directNotify.newCategory("DistributedObjectUD") QuietZone = 1 def __init__(self, air): try: self.DistributedObjectUD_initialized except: self.DistributedObjectUD_initialized = 1 DistributedObjectBase.__init__(self, air) self.accountName = '' # Record the repository self.air = air # Record our distributed class className = self.__class__.__name__ self.dclass = self.air.dclassesByName[className] # init doId pre-allocated flag self.__preallocDoId = 0 # used to track zone changes across the quiet zone # NOTE: the quiet zone is defined in OTP, but we need it # here. self.lastNonQuietZone = None self._DOUD_requestedDelete = False # These are used to implement beginBarrier(). self.__nextBarrierContext = 0 self.__barriers = {} self.__generated = False # reference count for multiple inheritance self.__generates = 0 # Uncomment if you want to debug DO leaks #def __del__(self): # """ # For debugging purposes, this just prints out what got deleted # """ # print ("Destructing: " + self.__class__.__name__) if __debug__: def status(self, indent=0): """ print out doId(parentId, zoneId) className and conditionally show generated, disabled, neverDisable, or cachable """ spaces = ' ' * (indent + 2) try: print "%s%s:" % (' ' * indent, self.__class__.__name__) print( "%sfrom " "DistributedObject doId:%s, parent:%s, zone:%s" % (spaces, self.doId, self.parentId, self.zoneId)), flags = [] if self.__generated: flags.append("generated") if self.air == None: flags.append("deleted") if len(flags): print "(%s)" % (" ".join(flags), ), print except Exception, e: print "%serror printing status" % (spaces, ), e
class FunctionInterval(Interval.Interval): # Name counter functionIntervalNum = 1 # Keep a list of function intervals currently in memory for # Control-C-Control-V redefining. These are just weakrefs so they # should not cause any leaks. if __debug__: import weakref FunctionIntervals = weakref.WeakKeyDictionary() @classmethod def replaceMethod(self, oldFunction, newFunction): import new import types count = 0 for ival in self.FunctionIntervals: # print 'testing: ', ival.function, oldFunction # Note: you can only replace methods currently if type(ival.function) == types.MethodType: if (ival.function.im_func == oldFunction): # print 'found: ', ival.function, oldFunction ival.function = new.instancemethod( newFunction, ival.function.im_self, ival.function.im_class) count += 1 return count # create FunctionInterval DirectNotify category notify = directNotify.newCategory('FunctionInterval') # Class methods def __init__(self, function, **kw): """__init__(function, name = None, openEnded = 1, extraArgs = []) """ name = kw.pop('name', None) openEnded = kw.pop('openEnded', 1) extraArgs = kw.pop('extraArgs', []) # Record instance variables self.function = function # Create a unique name for the interval if necessary if (name == None): name = self.makeUniqueName(function) assert isinstance(name, types.StringType) # Record any arguments self.extraArgs = extraArgs self.kw = kw # Initialize superclass # Set openEnded true if privInitialize after end time cause interval # function to be called. If false, privInitialize calls have no effect # Event, Accept, Ignore intervals default to openEnded = 0 # Parent, Pos, Hpr, etc intervals default to openEnded = 1 Interval.Interval.__init__(self, name, duration=0.0, openEnded=openEnded) # For rebinding, let's remember this function interval on the class if __debug__: self.FunctionIntervals[self] = 1 @staticmethod def makeUniqueName(func, suffix=''): name = 'Func-%s-%d' % (func.__name__, FunctionInterval.functionIntervalNum) FunctionInterval.functionIntervalNum += 1 if suffix: name = '%s-%s' % (name, str(suffix)) return name def privInstant(self): # Evaluate the function self.function(*self.extraArgs, **self.kw) # Print debug information self.notify.debug('updateFunc() - %s: executing Function' % self.name)
from panda3d.pandac import ConfigConfigureGetConfigConfigShowbase as config from panda3d.direct.directnotify.DirectNotifyGlobal import directNotify from panda3d.direct.showbase.PythonUtil import fastRepr from exceptions import Exception import sys import types import traceback notify = directNotify.newCategory("ExceptionVarDump") reentry = 0 def _varDump__init__(self, *args, **kArgs): global reentry if reentry > 0: return reentry += 1 # frame zero is this frame f = 1 self._savedExcString = None self._savedStackFrames = [] while True: try: frame = sys._getframe(f) except ValueError, e: break else: f += 1 self._savedStackFrames.append(frame) self._moved__init__(*args, **kArgs) reentry -= 1
class InterestWatcher(DirectObject): """Object that observes all interests adds/removes over a period of time, and sends out an event when all of those interests have closed""" notify = directNotify.newCategory('InterestWatcher') def __init__(self, interestMgr, name, doneEvent=None, recurse=True, start=True, mustCollect=False, doCollectionMgr=None): DirectObject.__init__(self) self._interestMgr = interestMgr if doCollectionMgr is None: doCollectionMgr = interestMgr self._doCollectionMgr = doCollectionMgr self._eGroup = EventGroup(name, doneEvent=doneEvent) self._doneEvent = self._eGroup.getDoneEvent() self._gotEvent = False self._recurse = recurse if self._recurse: # this will hold a dict of parentId to set(zoneIds) that are closing self.closingParent2zones = {} if start: self.startCollect(mustCollect) def startCollect(self, mustCollect=False): self._mustCollect = mustCollect self.accept(self._interestMgr._getAddInterestEvent(), self._handleInterestOpenEvent) self.accept(self._interestMgr._getRemoveInterestEvent(), self._handleInterestCloseEvent) def stopCollect(self): self.ignore(self._interestMgr._getAddInterestEvent()) self.ignore(self._interestMgr._getRemoveInterestEvent()) mustCollect = self._mustCollect del self._mustCollect if not self._gotEvent: if mustCollect: logFunc = self.notify.error else: logFunc = self.notify.warning logFunc('%s: empty interest-complete set' % self.getName()) self.destroy() else: self.accept(self.getDoneEvent(), self.destroy) def destroy(self): if hasattr(self, '_eGroup'): self._eGroup.destroy() del self._eGroup del self._gotEvent del self._interestMgr self.ignoreAll() def getName(self): return self._eGroup.getName() def getDoneEvent(self): return self._doneEvent def _handleInterestOpenEvent(self, event): self._gotEvent = True self._eGroup.addEvent(event) def _handleInterestCloseEvent(self, event, parentId, zoneIdList): self._gotEvent = True self._eGroup.addEvent(event) """
class DoInterestManager(DirectObject.DirectObject): """ Top level Interest Manager """ notify = directNotify.newCategory("DoInterestManager") try: tempbase = base except: tempbase = simbase InterestDebug = tempbase.config.GetBool('interest-debug', False) del tempbase # 'handle' is a number that represents a single interest set that the # client has requested; the interest set may be modified _HandleSerialNum = 0 # high bit is reserved for server interests _HandleMask = 0x7FFF # 'context' refers to a single request to change an interest set _ContextIdSerialNum = 100 _ContextIdMask = 0x3FFFFFFF # avoid making Python create a long _interests = {} if __debug__: _debug_interestHistory = [] _debug_maxDescriptionLen = 40 _SerialGen = SerialNumGen() _SerialNum = serialNum() def __init__(self): assert DoInterestManager.notify.debugCall() DirectObject.DirectObject.__init__(self) self._addInterestEvent = uniqueName('DoInterestManager-Add') self._removeInterestEvent = uniqueName('DoInterestManager-Remove') self._noNewInterests = False self._completeDelayedCallback = None # keep track of request contexts that have not completed self._completeEventCount = ScratchPad(num=0) self._allInterestsCompleteCallbacks = [] def __verbose(self): return self.InterestDebug or self.getVerbose() def _getAnonymousEvent(self, desc): return 'anonymous-%s-%s' % (desc, DoInterestManager._SerialGen.next()) def setNoNewInterests(self, flag): self._noNewInterests = flag def noNewInterests(self): return self._noNewInterests def setAllInterestsCompleteCallback(self, callback): if ((self._completeEventCount.num == 0) and (self._completeDelayedCallback is None)): callback() else: self._allInterestsCompleteCallbacks.append(callback) def getAllInterestsCompleteEvent(self): return 'allInterestsComplete-%s' % DoInterestManager._SerialNum def resetInterestStateForConnectionLoss(self): DoInterestManager._interests.clear() self._completeEventCount = ScratchPad(num=0) if __debug__: self._addDebugInterestHistory("RESET", "", 0, 0, 0, []) def isValidInterestHandle(self, handle): # pass in a handle (or anything else) and this will return true if it is # still a valid interest handle if not isinstance(handle, InterestHandle): return False return DoInterestManager._interests.has_key(handle.asInt()) def updateInterestDescription(self, handle, desc): iState = DoInterestManager._interests.get(handle.asInt()) if iState: iState.setDesc(desc) def addInterest(self, parentId, zoneIdList, description, event=None): """ Look into a (set of) zone(s). """ assert DoInterestManager.notify.debugCall() handle = self._getNextHandle() # print 'base.cr.addInterest(',description,',',handle,'):',globalClock.getFrameCount() if self._noNewInterests: DoInterestManager.notify.warning( "addInterest: addingInterests on delete: %s" % (handle)) return # make sure we've got parenting rules set in the DC if parentId not in (self.getGameDoId(), ): parent = self.getDo(parentId) if not parent: DoInterestManager.notify.error( 'addInterest: attempting to add interest under unknown object %s' % parentId) else: if not parent.hasParentingRules(): DoInterestManager.notify.error( 'addInterest: no setParentingRules defined in the DC for object %s (%s)' '' % (parentId, parent.__class__.__name__)) if event: contextId = self._getNextContextId() else: contextId = 0 # event = self._getAnonymousEvent('addInterest') DoInterestManager._interests[handle] = InterestState( description, InterestState.StateActive, contextId, event, parentId, zoneIdList, self._completeEventCount) if self.__verbose(): print 'CR::INTEREST.addInterest(handle=%s, parentId=%s, zoneIdList=%s, description=%s, event=%s)' % ( handle, parentId, zoneIdList, description, event) self._sendAddInterest(handle, contextId, parentId, zoneIdList, description) if event: messenger.send(self._getAddInterestEvent(), [event]) assert self.printInterestsIfDebug() return InterestHandle(handle) def addAutoInterest(self, parentId, zoneIdList, description): """ Look into a (set of) zone(s). """ assert DoInterestManager.notify.debugCall() handle = self._getNextHandle() if self._noNewInterests: DoInterestManager.notify.warning( "addInterest: addingInterests on delete: %s" % (handle)) return # make sure we've got parenting rules set in the DC if parentId not in (self.getGameDoId(), ): parent = self.getDo(parentId) if not parent: DoInterestManager.notify.error( 'addInterest: attempting to add interest under unknown object %s' % parentId) else: if not parent.hasParentingRules(): DoInterestManager.notify.error( 'addInterest: no setParentingRules defined in the DC for object %s (%s)' '' % (parentId, parent.__class__.__name__)) DoInterestManager._interests[handle] = InterestState( description, InterestState.StateActive, 0, None, parentId, zoneIdList, self._completeEventCount, True) if self.__verbose(): print 'CR::INTEREST.addInterest(handle=%s, parentId=%s, zoneIdList=%s, description=%s)' % ( handle, parentId, zoneIdList, description) assert self.printInterestsIfDebug() return InterestHandle(handle) def removeInterest(self, handle, event=None): """ Stop looking in a (set of) zone(s) """ # print 'base.cr.removeInterest(',handle,'):',globalClock.getFrameCount() assert DoInterestManager.notify.debugCall() assert isinstance(handle, InterestHandle) existed = False if not event: event = self._getAnonymousEvent('removeInterest') handle = handle.asInt() if DoInterestManager._interests.has_key(handle): existed = True intState = DoInterestManager._interests[handle] if event: messenger.send(self._getRemoveInterestEvent(), [event, intState.parentId, intState.zoneIdList]) if intState.isPendingDelete(): self.notify.warning( 'removeInterest: interest %s already pending removal' % handle) # this interest is already pending delete, so let's just tack this # callback onto the list if event is not None: intState.addEvent(event) else: if len(intState.events) > 0: # we're not pending a removal, but we have outstanding events? # probably we are waiting for an add/alter complete. # should we send those events now? assert self.notify.warning( 'removeInterest: abandoning events: %s' % intState.events) intState.clearEvents() intState.state = InterestState.StatePendingDel contextId = self._getNextContextId() intState.context = contextId if event: intState.addEvent(event) self._sendRemoveInterest(handle, contextId) if not event: self._considerRemoveInterest(handle) if self.__verbose(): print 'CR::INTEREST.removeInterest(handle=%s, event=%s)' % ( handle, event) else: DoInterestManager.notify.warning( "removeInterest: handle not found: %s" % (handle)) assert self.printInterestsIfDebug() return existed def removeAutoInterest(self, handle): """ Stop looking in a (set of) zone(s) """ assert DoInterestManager.notify.debugCall() assert isinstance(handle, InterestHandle) existed = False handle = handle.asInt() if DoInterestManager._interests.has_key(handle): existed = True intState = DoInterestManager._interests[handle] if intState.isPendingDelete(): self.notify.warning( 'removeInterest: interest %s already pending removal' % handle) # this interest is already pending delete, so let's just tack this # callback onto the list else: if len(intState.events) > 0: # we're not pending a removal, but we have outstanding events? # probably we are waiting for an add/alter complete. # should we send those events now? self.notify.warning( 'removeInterest: abandoning events: %s' % intState.events) intState.clearEvents() intState.state = InterestState.StatePendingDel self._considerRemoveInterest(handle) if self.__verbose(): print 'CR::INTEREST.removeAutoInterest(handle=%s)' % ( handle) else: DoInterestManager.notify.warning( "removeInterest: handle not found: %s" % (handle)) assert self.printInterestsIfDebug() return existed @report(types=['args'], dConfigParam='want-guildmgr-report') def removeAIInterest(self, handle): """ handle is NOT an InterestHandle. It's just a bare integer representing an AI opened interest. We're making the client close down this interest since the AI has trouble removing interests(that its opened) when the avatar goes offline. See GuildManager(UD) for how it's being used. """ self._sendRemoveAIInterest(handle) def alterInterest(self, handle, parentId, zoneIdList, description=None, event=None): """ Removes old interests and adds new interests. Note that when an interest is changed, only the most recent change's event will be triggered. Previous events are abandoned. If this is a problem, consider opening multiple interests. """ assert DoInterestManager.notify.debugCall() assert isinstance(handle, InterestHandle) #assert not self._noNewInterests handle = handle.asInt() if self._noNewInterests: DoInterestManager.notify.warning( "alterInterest: addingInterests on delete: %s" % (handle)) return exists = False if event is None: event = self._getAnonymousEvent('alterInterest') if DoInterestManager._interests.has_key(handle): if description is not None: DoInterestManager._interests[handle].desc = description else: description = DoInterestManager._interests[handle].desc # are we overriding an existing change? if DoInterestManager._interests[handle].context != NO_CONTEXT: DoInterestManager._interests[handle].clearEvents() contextId = self._getNextContextId() DoInterestManager._interests[handle].context = contextId DoInterestManager._interests[handle].parentId = parentId DoInterestManager._interests[handle].zoneIdList = zoneIdList DoInterestManager._interests[handle].addEvent(event) if self.__verbose(): print 'CR::INTEREST.alterInterest(handle=%s, parentId=%s, zoneIdList=%s, description=%s, event=%s)' % ( handle, parentId, zoneIdList, description, event) self._sendAddInterest(handle, contextId, parentId, zoneIdList, description, action='modify') exists = True assert self.printInterestsIfDebug() else: DoInterestManager.notify.warning( "alterInterest: handle not found: %s" % (handle)) return exists def openAutoInterests(self, obj): if hasattr(obj, '_autoInterestHandle'): # must be multiple inheritance self.notify.debug('openAutoInterests(%s): interests already open' % obj.__class__.__name__) return autoInterests = obj.getAutoInterests() obj._autoInterestHandle = None if not len(autoInterests): return obj._autoInterestHandle = self.addAutoInterest( obj.doId, autoInterests, '%s-autoInterest' % obj.__class__.__name__) def closeAutoInterests(self, obj): if not hasattr(obj, '_autoInterestHandle'): # must be multiple inheritance self.notify.debug( 'closeAutoInterests(%s): interests already closed' % obj) return if obj._autoInterestHandle is not None: self.removeAutoInterest(obj._autoInterestHandle) del obj._autoInterestHandle # events for InterestWatcher def _getAddInterestEvent(self): return self._addInterestEvent def _getRemoveInterestEvent(self): return self._removeInterestEvent def _getInterestState(self, handle): return DoInterestManager._interests[handle] def _getNextHandle(self): handle = DoInterestManager._HandleSerialNum while True: handle = (handle + 1) & DoInterestManager._HandleMask # skip handles that are already in use if handle not in DoInterestManager._interests: break DoInterestManager.notify.warning('interest %s already in use' % handle) DoInterestManager._HandleSerialNum = handle return DoInterestManager._HandleSerialNum def _getNextContextId(self): contextId = DoInterestManager._ContextIdSerialNum while True: contextId = (contextId + 1) & DoInterestManager._ContextIdMask # skip over the 'no context' id if contextId != NO_CONTEXT: break DoInterestManager._ContextIdSerialNum = contextId return DoInterestManager._ContextIdSerialNum def _considerRemoveInterest(self, handle): """ Consider whether we should cull the interest set. """ assert DoInterestManager.notify.debugCall() if DoInterestManager._interests.has_key(handle): if DoInterestManager._interests[handle].isPendingDelete(): # make sure there is no pending event for this interest if DoInterestManager._interests[handle].context == NO_CONTEXT: assert len( DoInterestManager._interests[handle].events) == 0 del DoInterestManager._interests[handle] if __debug__: def printInterestsIfDebug(self): if DoInterestManager.notify.getDebug(): self.printInterests() return 1 # for assert def _addDebugInterestHistory(self, action, description, handle, contextId, parentId, zoneIdList): if description is None: description = '' DoInterestManager._debug_interestHistory.append( (action, description, handle, contextId, parentId, zoneIdList)) DoInterestManager._debug_maxDescriptionLen = max( DoInterestManager._debug_maxDescriptionLen, len(description)) def printInterestHistory(self): print "***************** Interest History *************" format = '%9s %' + str(DoInterestManager._debug_maxDescriptionLen ) + 's %6s %6s %9s %s' print format % ("Action", "Description", "Handle", "Context", "ParentId", "ZoneIdList") for i in DoInterestManager._debug_interestHistory: print format % tuple(i) print "Note: interests with a Context of 0 do not get" \ " done/finished notices." def printInterestSets(self): print "******************* Interest Sets **************" format = '%6s %' + str(DoInterestManager._debug_maxDescriptionLen ) + 's %11s %11s %8s %8s %8s' print format % ("Handle", "Description", "ParentId", "ZoneIdList", "State", "Context", "Event") for id, state in DoInterestManager._interests.items(): if len(state.events) == 0: event = '' elif len(state.events) == 1: event = state.events[0] else: event = state.events print format % (id, state.desc, state.parentId, state.zoneIdList, state.state, state.context, event) print "************************************************" def printInterests(self): self.printInterestHistory() self.printInterestSets() def _sendAddInterest(self, handle, contextId, parentId, zoneIdList, description, action=None): """ Part of the new otp-server code. handle is a client-side created number that refers to a set of interests. The same handle number doesn't necessarily have any relationship to the same handle on another client. """ assert DoInterestManager.notify.debugCall() if __debug__: if isinstance(zoneIdList, types.ListType): zoneIdList.sort() if action is None: action = 'add' self._addDebugInterestHistory(action, description, handle, contextId, parentId, zoneIdList) if parentId == 0: DoInterestManager.notify.error( 'trying to set interest to invalid parent: %s' % parentId) datagram = PyDatagram() # Add message type datagram.addUint16(CLIENT_ADD_INTEREST) datagram.addUint16(handle) datagram.addUint32(contextId) datagram.addUint32(parentId) if isinstance(zoneIdList, types.ListType): vzl = list(zoneIdList) vzl.sort() uniqueElements(vzl) for zone in vzl: datagram.addUint32(zone) else: datagram.addUint32(zoneIdList) self.send(datagram) def _sendRemoveInterest(self, handle, contextId): """ handle is a client-side created number that refers to a set of interests. The same handle number doesn't necessarily have any relationship to the same handle on another client. """ assert DoInterestManager.notify.debugCall() assert handle in DoInterestManager._interests datagram = PyDatagram() # Add message type datagram.addUint16(CLIENT_REMOVE_INTEREST) datagram.addUint16(handle) if contextId != 0: datagram.addUint32(contextId) self.send(datagram) if __debug__: state = DoInterestManager._interests[handle] self._addDebugInterestHistory("remove", state.desc, handle, contextId, state.parentId, state.zoneIdList) def _sendRemoveAIInterest(self, handle): """ handle is a bare int, NOT an InterestHandle. Use this to close an AI opened interest. """ datagram = PyDatagram() # Add message type datagram.addUint16(CLIENT_REMOVE_INTEREST) datagram.addUint16((1 << 15) + handle) self.send(datagram) def cleanupWaitAllInterestsComplete(self): if self._completeDelayedCallback is not None: self._completeDelayedCallback.destroy() self._completeDelayedCallback = None def queueAllInterestsCompleteEvent(self, frames=5): # wait for N frames, if no new interests, send out all-done event # calling this is OK even if there are no pending interest completes def checkMoreInterests(): # if there are new interests, cancel this delayed callback, another # will automatically be scheduled when all interests complete # print 'checkMoreInterests(',self._completeEventCount.num,'):',globalClock.getFrameCount() return self._completeEventCount.num > 0 def sendEvent(): messenger.send(self.getAllInterestsCompleteEvent()) for callback in self._allInterestsCompleteCallbacks: callback() self._allInterestsCompleteCallbacks = [] self.cleanupWaitAllInterestsComplete() self._completeDelayedCallback = FrameDelayedCall( 'waitForAllInterestCompletes', callback=sendEvent, frames=frames, cancelFunc=checkMoreInterests) checkMoreInterests = None sendEvent = None def handleInterestDoneMessage(self, di): """ This handles the interest done messages and may dispatch an event """ assert DoInterestManager.notify.debugCall() handle = di.getUint16() contextId = di.getUint32() if self.__verbose(): print 'CR::INTEREST.interestDone(handle=%s)' % handle DoInterestManager.notify.debug( "handleInterestDoneMessage--> Received handle %s, context %s" % (handle, contextId)) if DoInterestManager._interests.has_key(handle): eventsToSend = [] # if the context matches, send out the event if contextId == DoInterestManager._interests[handle].context: DoInterestManager._interests[handle].context = NO_CONTEXT # the event handlers may call back into the interest manager. Send out # the events after we're once again in a stable state. #DoInterestManager._interests[handle].sendEvents() eventsToSend = list( DoInterestManager._interests[handle].getEvents()) DoInterestManager._interests[handle].clearEvents() else: DoInterestManager.notify.debug( "handleInterestDoneMessage--> handle: %s: Expecting context %s, got %s" % (handle, DoInterestManager._interests[handle].context, contextId)) if __debug__: state = DoInterestManager._interests[handle] self._addDebugInterestHistory("finished", state.desc, handle, contextId, state.parentId, state.zoneIdList) self._considerRemoveInterest(handle) for event in eventsToSend: messenger.send(event) else: DoInterestManager.notify.warning( "handleInterestDoneMessage: handle not found: %s" % (handle)) # if there are no more outstanding interest-completes, send out global all-done event if self._completeEventCount.num == 0: self.queueAllInterestsCompleteEvent() assert self.printInterestsIfDebug()
class DoHierarchy: """ This table has been a source of memory leaks, with DoIds getting left in the table indefinitely. DoHierarchy guards access to the table and ensures correctness. """ notify = directNotify.newCategory("DoHierarchy") def __init__(self): # parentId->zoneId->set(child DoIds) self._table = {} self._allDoIds = set() def isEmpty(self): assert ((len(self._table) == 0) == (len(self._allDoIds) == 0)) return len(self._table) == 0 and len(self._allDoIds) == 0 def __len__(self): return len(self._allDoIds) def clear(self): assert self.notify.debugCall() self._table = {} self._allDoIds = set() def getDoIds(self, getDo, parentId, zoneId=None, classType=None): """ Moved from DoCollectionManager ============================== parentId is any distributed object id. zoneId is a uint32, defaults to None (all zones). Try zone 2 if you're not sure which zone to use (0 is a bad/null zone and 1 has had reserved use in the past as a no messages zone, while 2 has traditionally been a global, uber, misc stuff zone). dclassType is a distributed class type filter, defaults to None (no filter). If dclassName is None then all objects in the zone are returned; otherwise the list is filtered to only include objects of that type. """ parent=self._table.get(parentId) if parent is None: return [] if zoneId is None: r = [] for zone in parent.values(): for obj in zone: r.append(obj) else: r = parent.get(zoneId, []) if classType is not None: a = [] for doId in r: obj = getDo(doId) if isinstance(obj, classType): a.append(doId) r = a return r def storeObjectLocation(self, do, parentId, zoneId): doId = do.doId if doId in self._allDoIds: self.notify.error( 'storeObjectLocation(%s %s) already in _allDoIds; duplicate generate()? or didn\'t clean up previous instance of DO?' % ( do.__class__.__name__, do.doId)) parentZoneDict = self._table.setdefault(parentId, {}) zoneDoSet = parentZoneDict.setdefault(zoneId, set()) zoneDoSet.add(doId) self._allDoIds.add(doId) self.notify.debug('storeObjectLocation: %s(%s) @ (%s, %s)' % ( do.__class__.__name__, doId, parentId, zoneId)) def deleteObjectLocation(self, do, parentId, zoneId): doId = do.doId if doId not in self._allDoIds: self.notify.error( 'deleteObjectLocation(%s %s) not in _allDoIds; duplicate delete()? or invalid previous location on a new object?' % ( do.__class__.__name__, do.doId)) # jbutler: temp hack to get by the assert, this will be fixed soon if (doId not in self._allDoIds): return parentZoneDict = self._table.get(parentId) if parentZoneDict is not None: zoneDoSet = parentZoneDict.get(zoneId) if zoneDoSet is not None: if doId in zoneDoSet: zoneDoSet.remove(doId) self._allDoIds.remove(doId) self.notify.debug('deleteObjectLocation: %s(%s) @ (%s, %s)' % ( do.__class__.__name__, doId, parentId, zoneId)) if len(zoneDoSet) == 0: del parentZoneDict[zoneId] if len(parentZoneDict) == 0: del self._table[parentId] else: self.notify.error( "deleteObjectLocation: objId: %s not found" % doId) else: self.notify.error( "deleteObjectLocation: zoneId: %s not found" % zoneId) else: self.notify.error( "deleteObjectLocation: parentId: %s not found" % parentId)
class Interval(DirectObject): """Interval class: Base class for timeline functionality""" # create Interval DirectNotify category notify = directNotify.newCategory("Interval") playbackCounter = 0 # Class methods def __init__(self, name, duration, openEnded=1): self.name = name self.duration = max(duration, 0.0) self.state = CInterval.SInitial self.currT = 0.0 self.doneEvent = None self.setTHooks = [] self.__startT = 0 self.__startTAtStart = 1 self.__endT = duration self.__endTAtEnd = 1 self.__playRate = 1.0 self.__doLoop = 0 self.__loopCount = 0 self.pstats = None if __debug__ and TaskManager.taskTimerVerbose: self.pname = name.split('-', 1)[0] self.pstats = PStatCollector("App:Show code:ivalLoop:%s" % (self.pname)) # Set true if the interval should be invoked if it was # completely skipped over during initialize or finalize, false # if it should be ignored in this case. self.openEnded = openEnded def getName(self): return self.name def getDuration(self): return self.duration def getOpenEnded(self): return self.openEnded def setLoop(self, loop=1): self.__doLoop = loop def getLoop(self): return self.__doLoop def getState(self): return self.state def isPaused(self): return self.getState() == CInterval.SPaused def isStopped(self): # Returns true if the interval has not been started, has already # played to its completion, or has been explicitly stopped via # finish(). return (self.getState() == CInterval.SInitial or \ self.getState() == CInterval.SFinal) def setT(self, t): # There doesn't seem to be any reason to clamp this, and it # breaks looping intervals. The interval code should properly # handle t values outside the proper range. #t = min(max(t, 0.0), self.getDuration()) state = self.getState() if state == CInterval.SInitial: self.privInitialize(t) if self.isPlaying(): self.setupResume() else: self.privInterrupt() elif state == CInterval.SStarted: # Support modifying t while the interval is playing. We # assume is_playing() will be true in this state. assert self.isPlaying() self.privInterrupt() self.privStep(t) self.setupResume() elif state == CInterval.SPaused: # Support modifying t while the interval is paused. In # this case, we simply step to the new value of t; but # this will change the state to S_started, so we must then # change it back to S_paused by hand (because we're still # paused). self.privStep(t) self.privInterrupt() elif state == CInterval.SFinal: self.privReverseInitialize(t) if self.isPlaying(): self.setupResume() else: self.privInterrupt() else: self.notify.error("Invalid state: %s" % (state)) self.privPostEvent() def getT(self): return self.currT def start(self, startT = 0.0, endT = -1.0, playRate = 1.0): self.setupPlay(startT, endT, playRate, 0) self.__spawnTask() def loop(self, startT = 0.0, endT = -1.0, playRate = 1.0): self.setupPlay(startT, endT, playRate, 1) self.__spawnTask() def pause(self): if self.getState() == CInterval.SStarted: self.privInterrupt() self.privPostEvent() self.__removeTask() return self.getT() def resume(self, startT = None): if startT != None: self.setT(startT) self.setupResume() if not self.isPlaying(): self.__spawnTask() def resumeUntil(self, endT): duration = self.getDuration() if endT < 0 or endT >= duration: self.__endT = duration self.__endTAtEnd = 1 else: self.__endT = endT self.__endTAtEnd = 0 self.setupResume() if not self.isPlaying(): self.__spawnTask() def finish(self): state = self.getState() if state == CInterval.SInitial: self.privInstant() elif state != CInterval.SFinal: self.privFinalize() self.privPostEvent() self.__removeTask() def clearToInitial(self): # This method resets the interval's internal state to the # initial state, abandoning any parts of the interval that # have not yet been called. Calling it is like pausing the # interval and creating a new one in its place. self.pause() self.state = CInterval.SInitial self.currT = 0.0 def isPlaying(self): return taskMgr.hasTaskNamed(self.getName() + '-play') def getPlayRate(self): """ Returns the play rate as set by the last call to start(), loop(), or setPlayRate(). """ return self.__playRate def setPlayRate(self, playRate): """ Changes the play rate of the interval. If the interval is already started, this changes its speed on-the-fly. Note that since playRate is a parameter to start() and loop(), the next call to start() or loop() will reset this parameter. """ if self.isPlaying(): self.pause() self.__playRate = playRate self.resume() else: self.__playRate = playRate def setDoneEvent(self, event): self.doneEvent = event def getDoneEvent(self): return self.doneEvent def privDoEvent(self, t, event): if self.pstats: self.pstats.start() if event == CInterval.ETStep: self.privStep(t) elif event == CInterval.ETFinalize: self.privFinalize() elif event == CInterval.ETInterrupt: self.privInterrupt() elif event == CInterval.ETInstant: self.privInstant() elif event == CInterval.ETInitialize: self.privInitialize(t) elif event == CInterval.ETReverseFinalize: self.privReverseFinalize() elif event == CInterval.ETReverseInstant: self.privReverseInstant() elif event == CInterval.ETReverseInitialize: self.privReverseInitialize(t) else: self.notify.error('Invalid event type: %s' % (event)) if self.pstats: self.pstats.stop() def privInitialize(self, t): # Subclasses may redefine this function self.state = CInterval.SStarted self.privStep(t) def privInstant(self): # Subclasses may redefine this function self.state = CInterval.SStarted self.privStep(self.getDuration()) self.state = CInterval.SFinal self.intervalDone() def privStep(self, t): # Subclasses may redefine this function self.state = CInterval.SStarted self.currT = t def privFinalize(self): # Subclasses may redefine this function self.privStep(self.getDuration()) self.state = CInterval.SFinal self.intervalDone() def privReverseInitialize(self, t): # Subclasses may redefine this function self.state = CInterval.SStarted self.privStep(t) def privReverseInstant(self): # Subclasses may redefine this function self.state = CInterval.SStarted self.privStep(self.getDuration()) self.state = CInterval.SInitial def privReverseFinalize(self): # Subclasses may redefine this function self.privStep(0) self.state = CInterval.SInitial def privInterrupt(self): # Subclasses may redefine this function self.state = CInterval.SPaused def intervalDone(self): # Subclasses should call this when the interval transitions to # its final state. if self.doneEvent: messenger.send(self.doneEvent) def setupPlay(self, startT, endT, playRate, doLoop): duration = self.getDuration() if startT <= 0: self.__startT = 0 self.__startTAtStart = 1 elif startT > duration: self.__startT = duration self.__startTAtStart = 0 else: self.__startT = startT self.__startTAtStart = 0 if endT < 0 or endT >= duration: self.__endT = duration self.__endTAtEnd = 1 else: self.__endT = endT self.__endTAtEnd = 0 self.__clockStart = globalClock.getFrameTime() self.__playRate = playRate self.__doLoop = doLoop self.__loopCount = 0 def setupResume(self): now = globalClock.getFrameTime() if self.__playRate > 0: self.__clockStart = now - ((self.getT() - self.__startT) / self.__playRate) elif self.__playRate < 0: self.__clockStart = now - ((self.getT() - self.__endT) / self.__playRate) self.__loopCount = 0 def stepPlay(self): now = globalClock.getFrameTime() if self.__playRate >= 0: t = (now - self.__clockStart) * self.__playRate + self.__startT if self.__endTAtEnd: self.__endT = self.getDuration() if t < self.__endT: # In the middle of the interval, not a problem. if self.isStopped(): self.privInitialize(t) else: self.privStep(t) else: # Past the ending point; time to finalize. if self.__endTAtEnd: # Only finalize if the playback cycle includes the # whole interval. if self.isStopped(): if self.getOpenEnded() or self.__loopCount != 0: self.privInstant() else: self.privFinalize() else: if self.isStopped(): self.privInitialize(self.__endT) else: self.privStep(self.__endT) # Advance the clock for the next loop cycle. if self.__endT == self.__startT: # If the interval has no length, we loop exactly once. self.__loopCount += 1 else: # Otherwise, figure out how many loops we need to # skip. timePerLoop = (self.__endT - self.__startT) / self.__playRate numLoops = math.floor((now - self.__clockStart) / timePerLoop) self.__loopCount += numLoops self.__clockStart += numLoops * timePerLoop else: # Playing backwards. Not supported at the moment for # Python-style intervals. To add support, copy the code # from C++-style intervals in cInterval.cxx, and modify it # for Python (as the above). pass shouldContinue = (self.__loopCount == 0 or self.__doLoop) if (not shouldContinue and self.getState() == CInterval.SStarted): self.privInterrupt() return shouldContinue def __repr__(self, indent=0): space = '' for l in range(indent): space = space + ' ' return (space + self.name + ' dur: %.2f' % self.duration) # The rest of these methods are duplicates of functions defined # for the CInterval class via the file CInterval-extensions.py. def privPostEvent(self): # Call after calling any of the priv* methods to do any required # Python finishing steps. if self.pstats: self.pstats.start() t = self.getT() if hasattr(self, "setTHooks"): for func in self.setTHooks: func(t) if self.pstats: self.pstats.stop() def __spawnTask(self): # Spawn task self.__removeTask() taskName = self.getName() + '-play' task = Task(self.__playTask) task.interval = self taskMgr.add(task, taskName) def __removeTask(self): # Kill old task(s), including those from a similarly-named but # different interval. taskName = self.getName() + '-play' oldTasks = taskMgr.getTasksNamed(taskName) for task in oldTasks: if hasattr(task, "interval"): task.interval.privInterrupt() taskMgr.remove(task) def __playTask(self, task): again = self.stepPlay() self.privPostEvent() if again: return Task.cont else: return Task.done def popupControls(self, tl = None): """ Popup control panel for interval. """ from panda3d.direct.showbase import TkGlobal import math # I moved this here because Toontown does not ship Tk from Tkinter import Toplevel, Frame, Button, LEFT, X import Pmw from panda3d.direct.tkwidgets import EntryScale if tl == None: tl = Toplevel() tl.title('Interval Controls') outerFrame = Frame(tl) def entryScaleCommand(t, s=self): s.setT(t) s.pause() self.es = es = EntryScale.EntryScale( outerFrame, text = self.getName(), min = 0, max = math.floor(self.getDuration() * 100) / 100, command = entryScaleCommand) es.set(self.getT(), fCommand = 0) es.pack(expand = 1, fill = X) bf = Frame(outerFrame) # Jump to start and end def toStart(s=self, es=es): s.clearToInitial() es.set(0, fCommand = 0) def toEnd(s=self): s.pause() s.setT(s.getDuration()) es.set(s.getDuration(), fCommand = 0) s.pause() jumpToStart = Button(bf, text = '<<', command = toStart) # Stop/play buttons def doPlay(s=self, es=es): s.resume(es.get()) stop = Button(bf, text = 'Stop', command = lambda s=self: s.pause()) play = Button( bf, text = 'Play', command = doPlay) jumpToEnd = Button(bf, text = '>>', command = toEnd) jumpToStart.pack(side = LEFT, expand = 1, fill = X) play.pack(side = LEFT, expand = 1, fill = X) stop.pack(side = LEFT, expand = 1, fill = X) jumpToEnd.pack(side = LEFT, expand = 1, fill = X) bf.pack(expand = 1, fill = X) outerFrame.pack(expand = 1, fill = X) # Add function to update slider during setT calls def update(t, es=es): es.set(t, fCommand = 0) if not hasattr(self, "setTHooks"): self.setTHooks = [] self.setTHooks.append(update) # Clear out function on destroy def onDestroy(e, s=self, u=update): if u in s.setTHooks: s.setTHooks.remove(u) tl.bind('<Destroy>', onDestroy)
from panda3d.direct.extensions_native.Helpers import * Dtool_PreloadDLL("direct") from direct import * ##################################################################### from panda3d.direct.directnotify.DirectNotifyGlobal import directNotify notify = directNotify.newCategory("Interval") Dtool_ObjectToDict(CInterval,"notify", notify) del notify ##################################################################### def setT(self, t): # Overridden from the C++ function to call privPostEvent # afterward. We do this by renaming the C++ function in # FFIRename. self.setT_Old(t) self.privPostEvent() Dtool_ObjectToDict(CInterval, "setT_Old", CInterval.setT) Dtool_funcToMethod(setT, CInterval) del setT ##################################################################### def play(self, t0 = 0.0, duration = None, scale = 1.0): self.notify.error("using deprecated CInterval.play() interface") if duration: # None or 0 implies full length self.start(t0, t0 + duration, scale) else: self.start(t0, -1, scale)
class ContainerReport(Job): notify = directNotify.newCategory("ContainerReport") # set of containers that should not be included in the report PrivateIds = set() def __init__(self, name, log=False, limit=None, threaded=False): Job.__init__(self, name) self._log = log self._limit = limit # set up our data structures self._visitedIds = set() self._id2pathStr = {} self._id2container = {} self._type2id2len = {} self._instanceDictIds = set() # for breadth-first searching self._queue = Queue() jobMgr.add(self) if threaded == False: jobMgr.finish(self) def destroy(self): del self._queue del self._instanceDictIds del self._type2id2len del self._id2container del self._id2pathStr del self._visitedIds del self._limit del self._log def finished(self): if self._log: self.destroy() def run(self): ContainerReport.PrivateIds.update(set([ id(ContainerReport.PrivateIds), id(self._visitedIds), id(self._id2pathStr), id(self._id2container), id(self._type2id2len), id(self._queue), id(self._instanceDictIds), ])) # push on a few things that we want to give priority # for the sake of the variable-name printouts try: base except: pass else: self._enqueueContainer( base.__dict__, 'base') try: simbase except: pass else: self._enqueueContainer( simbase.__dict__, 'simbase') self._queue.push(__builtins__) self._id2pathStr[id(__builtins__)] = '' while len(self._queue) > 0: # yield up here instead of at the end, since we skip back to the # top of the while loop from various points yield None parentObj = self._queue.pop() #print '%s: %s, %s' % (id(parentObj), type(parentObj), self._id2pathStr[id(parentObj)]) isInstanceDict = False if id(parentObj) in self._instanceDictIds: isInstanceDict = True try: if parentObj.__class__.__name__ == 'method-wrapper': continue except: pass if type(parentObj) in (types.StringType, types.UnicodeType): continue if type(parentObj) in (types.ModuleType, types.InstanceType): child = parentObj.__dict__ if self._examine(child): assert (self._queue.back() is child) self._instanceDictIds.add(id(child)) self._id2pathStr[id(child)] = str(self._id2pathStr[id(parentObj)]) continue if type(parentObj) is types.DictType: key = None attr = None keys = parentObj.keys() try: keys.sort() except TypeError, e: self.notify.warning('non-sortable dict keys: %s: %s' % (self._id2pathStr[id(parentObj)], repr(e))) for key in keys: try: attr = parentObj[key] except KeyError, e: self.notify.warning('could not index into %s with key %s' % (self._id2pathStr[id(parentObj)], key)) if id(attr) not in self._visitedIds: self._visitedIds.add(id(attr)) if self._examine(attr): assert (self._queue.back() is attr) if parentObj is __builtins__: self._id2pathStr[id(attr)] = key else: if isInstanceDict: self._id2pathStr[id(attr)] = self._id2pathStr[id(parentObj)] + '.%s' % key else: self._id2pathStr[id(attr)] = self._id2pathStr[id(parentObj)] + '[%s]' % safeRepr(key) del key del attr continue if type(parentObj) is not types.FileType: try: itr = iter(parentObj) except: pass else: try: index = 0 while 1: try: attr = itr.next() except: # some custom classes don't do well when iterated attr = None break if id(attr) not in self._visitedIds: self._visitedIds.add(id(attr)) if self._examine(attr): assert (self._queue.back() is attr) self._id2pathStr[id(attr)] = self._id2pathStr[id(parentObj)] + '[%s]' % index index += 1 del attr except StopIteration, e: pass del itr continue
class ProfileSession: # class that encapsulates a profile of a single callable using Python's standard # 'profile' module # # defers formatting of profile results until they are requested # # implementation sidesteps memory leak in Python profile module, # and redirects file output to RAM file for efficiency TrueClock = TrueClock.getGlobalPtr() notify = directNotify.newCategory("ProfileSession") def __init__(self, name, func=None, logAfterProfile=False): self._func = func self._name = name self._logAfterProfile = logAfterProfile self._filenameBase = 'profileData-%s-%s' % (self._name, id(self)) self._refCount = 0 # if true, accumulate profile results every time we run # if false, throw out old results every time we run self._aggregate = False self._lines = 500 self._sorts = ['cumulative', 'time', 'calls'] self._callInfo = True self._totalTime = None self._reset() self.acquire() def getReference(self): # call this when you want to store a new reference to this session that will # manage its acquire/release reference count independently of an existing reference self.acquire() return self def acquire(self): self._refCount += 1 def release(self): self._refCount -= 1 if not self._refCount: self._destroy() def _destroy(self): del self._func del self._name del self._filenameBase del self._filenameCounter del self._filenames del self._duration del self._filename2ramFile del self._resultCache del self._successfulProfiles def _reset(self): self._filenameCounter = 0 self._filenames = [] # index of next file to be added to stats object self._statFileCounter = 0 self._successfulProfiles = 0 self._duration = None self._filename2ramFile = {} self._stats = None self._resultCache = {} def _getNextFilename(self): filename = '%s-%s' % (self._filenameBase, self._filenameCounter) self._filenameCounter += 1 return filename def run(self): # make sure this instance doesn't get destroyed inside self._func self.acquire() if not self._aggregate: self._reset() # if we're already profiling, just run the func and don't profile if 'globalProfileSessionFunc' in __builtin__.__dict__: self.notify.warning('could not profile %s' % self._func) result = self._func() if self._duration is None: self._duration = 0. else: # put the function in the global namespace so that profile can find it assert callable(self._func) __builtin__.globalProfileSessionFunc = self._func __builtin__.globalProfileSessionResult = [None] # set up the RAM file self._filenames.append(self._getNextFilename()) filename = self._filenames[-1] _installProfileCustomFuncs(filename) # do the profiling Profile = profile.Profile statement = 'globalProfileSessionResult[0]=globalProfileSessionFunc()' sort = -1 retVal = None # this is based on profile.run, the code is replicated here to allow us to # eliminate a memory leak prof = Profile() try: prof = prof.run(statement) except SystemExit: pass # this has to be run immediately after profiling for the timings to be accurate # tell the Profile object to generate output to the RAM file prof.dump_stats(filename) # eliminate the memory leak del prof.dispatcher # store the RAM file for later profData = _getProfileResultFileInfo(filename) self._filename2ramFile[filename] = profData # calculate the duration (this is dependent on the internal Python profile data format. # see profile.py and pstats.py, this was copied from pstats.Stats.strip_dirs) maxTime = 0. for cc, nc, tt, ct, callers in profData[1].itervalues(): if ct > maxTime: maxTime = ct self._duration = maxTime # clean up the RAM file support _removeProfileCustomFuncs(filename) # clean up the globals result = globalProfileSessionResult[0] del __builtin__.__dict__['globalProfileSessionFunc'] del __builtin__.__dict__['globalProfileSessionResult'] self._successfulProfiles += 1 if self._logAfterProfile: self.notify.info(self.getResults()) self.release() return result def getDuration(self): return self._duration def profileSucceeded(self): return self._successfulProfiles > 0 def _restoreRamFile(self, filename): # set up the RAM file _installProfileCustomFuncs(filename) # install the stored RAM file from self.run() _setProfileResultsFileInfo(filename, self._filename2ramFile[filename]) def _discardRamFile(self, filename): # take down the RAM file _removeProfileCustomFuncs(filename) # and discard it del self._filename2ramFile[filename] def setName(self, name): self._name = name def getName(self): return self._name def setFunc(self, func): self._func = func def getFunc(self): return self._func def setAggregate(self, aggregate): self._aggregate = aggregate def getAggregate(self): return self._aggregate def setLogAfterProfile(self, logAfterProfile): self._logAfterProfile = logAfterProfile def getLogAfterProfile(self): return self._logAfterProfile def setLines(self, lines): self._lines = lines def getLines(self): return self._lines def setSorts(self, sorts): self._sorts = sorts def getSorts(self): return self._sorts def setShowCallInfo(self, showCallInfo): self._showCallInfo = showCallInfo def getShowCallInfo(self): return self._showCallInfo def setTotalTime(self, totalTime=None): self._totalTime = totalTime def resetTotalTime(self): self._totalTime = None def getTotalTime(self): return self._totalTime def aggregate(self, other): # pull in stats from another ProfileSession other._compileStats() self._compileStats() self._stats.add(other._stats) def _compileStats(self): # make sure our stats object exists and is up-to-date statsChanged = (self._statFileCounter < len(self._filenames)) if self._stats is None: for filename in self._filenames: self._restoreRamFile(filename) self._stats = PercentStats(*self._filenames) self._statFileCounter = len(self._filenames) for filename in self._filenames: self._discardRamFile(filename) else: while self._statFileCounter < len(self._filenames): filename = self._filenames[self._statFileCounter] self._restoreRamFile(filename) self._stats.add(filename) self._discardRamFile(filename) if statsChanged: self._stats.strip_dirs() # throw out any cached result strings self._resultCache = {} return statsChanged def getResults(self, lines=Default, sorts=Default, callInfo=Default, totalTime=Default): if not self.profileSucceeded(): output = '%s: profiler already running, could not profile' % self._name else: if lines is Default: lines = self._lines if sorts is Default: sorts = self._sorts if callInfo is Default: callInfo = self._callInfo if totalTime is Default: totalTime = self._totalTime self._compileStats() if totalTime is None: totalTime = self._stats.total_tt # make sure the arguments will hash efficiently if callers provide different types lines = int(lines) sorts = list(sorts) callInfo = bool(callInfo) totalTime = float(totalTime) k = str((lines, sorts, callInfo, totalTime)) if k in self._resultCache: # we've already created this output string, get it from the cache output = self._resultCache[k] else: # now get human-readable output from the profile stats # capture print output to a string sc = StdoutCapture() # print the info to stdout s = self._stats # make sure our percentages are relative to the correct total time s.setTotalTime(totalTime) for sort in sorts: s.sort_stats(sort) s.print_stats(lines) if callInfo: s.print_callees(lines) s.print_callers(lines) # make a copy of the print output output = sc.getString() # restore stdout to what it was before sc.destroy() # cache this result self._resultCache[k] = output return output
import direct from panda3d.pandac import HttpRequest from panda3d.direct.directnotify.DirectNotifyGlobal import directNotify from panda3d.direct.task.TaskManagerGlobal import taskMgr from panda3d.direct.task import Task from LandingPage import LandingPage notify = directNotify.newCategory('WebRequestDispatcher') class WebRequest(object): """ Pointer to a single web request (maps to an open HTTP socket). An instance of this class maps to a single client waiting for a response. connection is an instance of libdirect.HttpRequest """ def __init__(self,connection): self.connection = connection def getURI(self): return self.connection.GetRequestURL() def getRequestType(self): return self.connection.GetRequestType() def dictFromGET(self): result = {} for pair in self.connection.GetRequestOptionString().split('&'): arg = pair.split('=',1) if len(arg) > 1: result[arg[0]] = arg[1]
class TestInterval(Interval): # Name counter particleNum = 1 # create ParticleInterval DirectNotify category notify = directNotify.newCategory('TestInterval') # Class methods def __init__(self, particleEffect, duration=0.0, parent = None, renderParent = None, name=None): """ particleEffect is ?? parent is ?? worldRelative is a boolean loop is a boolean duration is a float for the time name is ?? """ # Generate unique name id = 'Particle-%d' % TestInterval.particleNum TestInterval.particleNum += 1 if name == None: name = id # Record instance variables self.particleEffect = particleEffect self.parent = parent self.renderParent = renderParent Interval.__init__(self, name, duration) def __del__(self): pass def __step(self,dt): self.particleEffect.accelerate(dt,1,0.05) def start(self,*args,**kwargs): self.particleEffect.clearToInitial() self.currT = 0 Interval.start(self,*args,**kwargs) def privInitialize(self, t): if self.parent != None: self.particleEffect.reparentTo(self.parent) if self.renderParent != None: self.setRenderParent(self.renderParent.node()) self.state = CInterval.SStarted #self.particleEffect.enable() """ if (self.particleEffect.renderParent != None): for p in self.particleEffect.particlesDict.values(): p.setRenderParent(self.particleEffect.renderParent.node()) """ for f in self.particleEffect.forceGroupDict.values(): f.enable() """ for p in self.particleEffect.particlesDict.values(): p.enable() self.particleEffect.fEnabled = 1 """ self.__step(t-self.currT) self.currT = t def privStep(self, t): if self.state == CInterval.SPaused: # Restarting from a pause. self.privInitialize(t) else: self.state = CInterval.SStarted self.__step(t-self.currT) self.currT = t def privFinalize(self): self.__step(self.getDuration()-self.currT) self.currT = self.getDuration() self.state = CInterval.SFinal def privInstant(self): """ Full jump from Initial state to Final State """ self.__step(self.getDuration()-self.currT) self.currT = self.getDuration() self.state = CInterval.SFinal def privInterrupt(self): if not self.isStopped(): self.state = CInterval.SPaused
class ParticleInterval(Interval): """ Use this interval when you want to have greater control over a ParticleEffect. The interval does not register the effect with the global particle and physics managers, but it does call upon them to perform its stepping. You should NOT call particleEffect.start() with an effect that is being controlled by a ParticleInterval. """ # Name counter particleNum = 1 # create ParticleInterval DirectNotify category notify = directNotify.newCategory('ParticleInterval') # Class methods def __init__(self, particleEffect, parent, worldRelative=1, renderParent=None, duration=0.0, softStopT=0.0, cleanup=False, name=None): """ particleEffect is a ParticleEffect parent is a NodePath: this is where the effect will be parented in the scenegraph worldRelative is a boolean: this will override 'renderParent' with render renderParent is a NodePath: this is where the particles will be rendered in the scenegraph duration is a float: for the time softStopT is a float: no effect if 0.0, a positive value will count from the start of the interval, a negative value will count from the end of the interval cleanup is a boolean: if True the effect will be destroyed and removed from the scenegraph upon interval completion set to False if planning on reusing the interval name is a string: use this for unique intervals so that they can be easily found in the taskMgr """ # Generate unique name id = 'Particle-%d' % ParticleInterval.particleNum ParticleInterval.particleNum += 1 if name == None: name = id # Record instance variables self.particleEffect = particleEffect self.cleanup = cleanup if parent != None: self.particleEffect.reparentTo(parent) if worldRelative: renderParent = render if renderParent: for particles in self.particleEffect.getParticlesList(): particles.setRenderParent(renderParent.node()) self.__softStopped = False if softStopT == 0.0: self.softStopT = duration elif softStopT < 0.0: self.softStopT = duration + softStopT else: self.softStopT = softStopT # Initialize superclass Interval.__init__(self, name, duration) def __step(self, dt): if self.particleEffect: self.particleEffect.accelerate(dt, 1, 0.05) def __softStart(self): if self.particleEffect: self.particleEffect.softStart() self.__softStopped = False def __softStop(self): if self.particleEffect: self.particleEffect.softStop() self.__softStopped = True def privInitialize(self, t): if self.state != CInterval.SPaused: # Restarting from a hard stop or just interrupting the # current play self.__softStart() if self.particleEffect: self.particleEffect.clearToInitial() self.currT = 0 if self.particleEffect: for forceGroup in self.particleEffect.getForceGroupList(): forceGroup.enable() Interval.privInitialize(self, t) def privInstant(self): self.privInitialize(self.getDuration()) self.privFinalize() def privStep(self, t): if self.state == CInterval.SPaused or t < self.currT: # Restarting from a pause. self.privInitialize(t) else: if not self.__softStopped and t > self.softStopT: self.__step(self.softStopT - self.currT) self.__softStop() self.__step(t - self.softStopT) else: self.__step(t - self.currT) Interval.privStep(self, t) def privFinalize(self): Interval.privFinalize(self) if self.cleanup and self.particleEffect: self.particleEffect.disable() self.particleEffect = None