def detectLeaks(self): if not __dev__: return events = messenger.getAllAccepting(self) 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))
def __init__(self, width, height, depth): self.forms = {f.name: f for f in load_forms()} Block.FORMS = self.forms self.width = width self.height = height self.depth = depth self.size = core.Point3(self.width, self.height, self.depth) self.midpoint = core.Point3(self.width // 2, self.height // 2, self.depth // 2) self.blocks = {coords: None for coords in self.grid()} self.notify = directNotify.newCategory('world') messenger.accept('console-command', self, self.command)
def checkForGarbageLeaks(): gc.collect() numGarbage = len(gc.garbage) if (numGarbage > 0 and config.GetBool('auto-garbage-logging', 0)): 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
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))
#!/usr/bin/env python from direct.showbase.ShowBase import ShowBase from direct.task import Task from direct.distributed.AstronInternalRepository import AstronInternalRepository from direct.directnotify.DirectNotifyGlobal import directNotify from panda3d.core import loadPrcFileData from time import sleep # Globally known object and channel IDs from simple_example_globals_server import LoginManagerId, UDChannel, SSChannel # No camera or window is needed, but notifications are. loadPrcFileData("", "\n".join(["window-type none", "notify-level-udserver debug"])) notify = directNotify.newCategory("udserver") # This class manages the UberDOGs, which in this case is just one, the login # manager. class SimpleServer(ShowBase): def __init__(self, server_framerate = 60): ShowBase.__init__(self) # First, set up the idle task that forces a sleep # that reduces the servers speed to a given framerate. self.server_frametime = 1./server_framerate self.taskMgr.add(self.idle, 'idle task', sort = 47) # Start UberDOG self.startUberDOG() # Idle task, so that the server doesn't eat 100% CPU def idle(self, task):
from direct.extensions_native import VBase4_extensions from direct.extensions_native import NodePath_extensions from panda3d.core import loadPrcFile if __debug__: loadPrcFile("config/general.prc") loadPrcFile("config/release/dev.prc") from direct.directnotify.DirectNotifyGlobal import directNotify notify = directNotify.newCategory("ClientStart") notify.setInfo(True) from otp.settings.Settings import Settings preferencesFilename = ConfigVariableString("preferences-filename", "preferences.json").getValue() notify.info("Reading %s..." % preferencesFilename) __builtin__.settings = Settings(preferencesFilename) if "fullscreen" not in settings: settings["fullscreen"] = False if "music" not in settings: settings["music"] = True if "sfx" not in settings: settings["sfx"] = True
from toontown.toonbase.ToontownGlobals import * from direct.directnotify.DirectNotifyGlobal import directNotify zoneUtilNotify = directNotify.newCategory('ZoneUtil') tutorialDict = None def isGoofySpeedwayZone(zoneId): return zoneId == 8000 def isCogHQZone(zoneId): return zoneId >= 10000 and zoneId < 15000 def isMintInteriorZone(zoneId): return zoneId in (CashbotMintIntA, CashbotMintIntB, CashbotMintIntC) def isDynamicZone(zoneId): return zoneId >= DynamicZonesBegin and zoneId < DynamicZonesEnd def getStreetName(branchId): global tutorialDict if tutorialDict: return StreetNames[20000][-1] else: return StreetNames[branchId][-1]
class DropGag(Gag, LocationGag): notify = directNotify.newCategory('DropGag') def __init__(self, name, model, anim, damage, hitSfx, missSfx, scale, playRate): Gag.__init__(self, name, model, damage, GagType.DROP, hitSfx, anim=anim, playRate=playRate, scale=scale, autoRelease=True) LocationGag.__init__(self, 10, 50) self.missSfx = None self.fallSoundPath = 'phase_5/audio/sfx/incoming_whistleALT.ogg' self.fallSoundInterval = None self.fallSfx = None self.chooseLocFrame = 34 self.completeFrame = 77 self.collHandlerF = CollisionHandlerFloor() self.fallDuration = 0.75 self.isDropping = False self.timeout = 3.0 if game.process == 'client': self.missSfx = base.audio3d.loadSfx(missSfx) self.fallSfx = base.audio3d.loadSfx(self.fallSoundPath) self.crashSite = None self.crashSiteGag = None self.crashSiteShadow = None self.crashSiteIval = None self.crashStartPos = None self.crashEndPos = None self.crashBegun = False self.shadowIdleTaskName = 'Handle-IdleShadow' self.shadowIdleTime = 0.0 return def tickShadowIdleTask(self, task): task.delayTime = 0.1 self.shadowIdleTime += 0.1 if self.shadowIdleTime >= 0.25: self.startCrashEffect() return task.again def __shadowMoved(self): self.shadowIdleTime = 0.0 base.taskMgr.remove(self.shadowIdleTaskName) base.taskMgr.add(self.tickShadowIdleTask, self.shadowIdleTaskName) if self.crashSite: self.cleanupCrashIval() self.crashSite.hide() def startCrashEffect(self): if not self.getLocationSeeker() or self.crashBegun: return self.cleanupCrashIval() self.crashBegun = True if self.crashSite is None: self.crashSite = loader.loadModel('phase_6/models/props/piano.bam') self.crashSite.setScale(0.5) self.crashSite.setTransparency(TransparencyAttrib.MAlpha) self.crashSite.setColorScale(1.0, 1.0, 1.0, 0.25) self.crashSite.reparentTo(render) self.crashSiteGag = self.crashSite.find('**/crashed_piano') self.crashSiteGag.setTransparency(TransparencyAttrib.MAlpha) self.crashSiteShadow = self.crashSite.find('**/shadow_crack') for node in self.crashSite.findAllMatches('**/*coll*'): node.removeNode() self.crashSite.show() dropShadow = self.getLocationSeeker().getDropShadow() if not dropShadow: return location = self.getLocationSeeker().getDropShadow().getPos(render) self.crashSite.setPos(location.getX(), location.getY(), location.getZ()) self.crashEndPos = self.crashSiteShadow.getPos() self.crashStartPos = Point3(self.crashEndPos.getX(), self.crashEndPos.getY(), self.crashEndPos.getZ() + 8.5) self.crashSiteIval = Sequence( Func(self.crashSiteShadow.hide), Func(self.crashSiteGag.headsUp, base.localAvatar), Parallel( Sequence( LerpPosHprInterval(self.crashSiteGag, duration=0.75, pos=self.crashEndPos, startPos=self.crashStartPos, startHpr=Point3(0.0, 15.0, 21.3), hpr=Point3(0.0, 0.0, 0.0)), Func(self.crashSiteShadow.show))), LerpColorScaleInterval(self.crashSiteShadow, duration=0.75, colorScale=Vec4(1.0, 1.0, 1.0, 0.0), startColorScale=Vec4(1.0, 1.0, 1.0, 1.0)), Func(self.crashSiteShadow.hide), Func(self.crashSiteShadow.setColorScale, 1.0, 1.0, 1.0, 1.0)) self.crashSiteIval.loop() return def resetCrashEffect(self): base.taskMgr.remove(self.shadowIdleTaskName) base.ignore(self.getLocationSeeker().getShadowMovedName()) self.cleanupCrashIval() if self.crashSite: self.crashSite.removeNode() self.crashSite = None self.crashSiteGag = None self.crashSiteShadow = None self.crashStartPos = None self.crashEndPos = None return def cleanupCrashIval(self): self.crashBegun = False if self.crashSiteIval: self.crashSiteIval.pause() self.crashSiteIval = None return def completeDrop(self): LocationGag.complete(self) self.isDropping = False if game.process != 'client': return self.reset() def start(self): super(DropGag, self).start() LocationGag.start(self, self.avatar) if self.isLocal() and self.getName() == CIGlobals.GrandPiano: base.taskMgr.add(self.tickShadowIdleTask, self.shadowIdleTaskName) base.accept(self.getLocationSeeker().getShadowMovedName(), self.__shadowMoved) def unEquip(self): LocationGag.cleanupLocationSeeker(self) super(DropGag, self).unEquip() if self.state != GagState.LOADED: self.completeDrop() def onActivate(self, ignore, suit): pass def buildCollisions(self): pass def onCollision(self, entry): if not self.gag: return intoNP = entry.getIntoNodePath() avNP = intoNP.getParent() hitCog = False self.fallSoundInterval.pause() self.fallSoundInterval = None shrinkTrack = Sequence() if self.avatar.doId == base.localAvatar.doId: for key in base.cr.doId2do.keys(): obj = base.cr.doId2do[key] if obj.__class__.__name__ in CIGlobals.SuitClasses: if obj.getKey() == avNP.getKey(): obj.sendUpdate('hitByGag', [self.getID()]) self.avatar.b_trapActivate(self.getID(), self.avatar.doId, 0, obj.doId) hitCog = True gagObj = self.gag if hitCog: SoundInterval(self.hitSfx, node=self.gag).start() shrinkTrack.append(Wait(0.5)) else: SoundInterval(self.missSfx, node=self.gag).start() shrinkTrack.append(Wait(0.25)) shrinkTrack.append( LerpScaleInterval(self.gag, 0.3, Point3(0.01, 0.01, 0.01), startScale=self.gag.getScale())) shrinkTrack.append(Func(gagObj.removeNode)) shrinkTrack.append(Func(self.cleanupGag)) shrinkTrack.start() return def onSuitHit(self, suit): pass @abc.abstractmethod def startDrop(self): pass def cleanupGag(self): if not self.isDropping: super(DropGag, self).cleanupGag() def release(self): if self.isLocal(): self.startTimeout() self.resetCrashEffect() LocationGag.release(self) self.build() self.isDropping = True actorTrack = LocationGag.getActorTrack(self) self.fallSoundInterval = LocationGag.getSoundTrack(self) if actorTrack: actorTrack.append(Func(self.startDrop)) actorTrack.start() self.fallSoundInterval.append( Parallel(SoundInterval(self.fallSfx, node=self.avatar))) self.fallSoundInterval.start() if self.isLocal(): base.localAvatar.sendUpdate('usedGag', [self.id]) def setEndPos(self, x, y, z): LocationGag.setDropLoc(self, x, y, z)
class AvatarFriendsManager(DistributedObjectGlobal): """ The Avatar Friends Manager is a global object. This object handles client requests on avatar-level (as opposed to player-level) friends. See Also: "otp/src/friends/AvatarFriendsManagerUD.py" "otp/src/friends/PlayerFriendsManager.py" "pirates/src/friends/PiratesFriendsList.py" "otp/src/configfiles/otp.dc" "pirates/src/configfiles/pirates.dc" """ notify = directNotify.newCategory('AvatarFriendsManager') def __init__(self, cr): assert self.notify.debugCall() DistributedObjectGlobal.__init__(self, cr) self.reset() def reset(self): self.avatarFriendsList = set() self.avatarId2Info = {} self.invitedAvatarsList = [] self.ignoredAvatarList = [] #Client only functions def addIgnore(self, avId): if avId not in self.ignoredAvatarList: self.ignoredAvatarList.append(avId) base.cr.centralLogger.writeClientEvent('ignoring %s' % (avId, )) messenger.send("AvatarIgnoreChange") def removeIgnore(self, avId): if avId in self.ignoredAvatarList: self.ignoredAvatarList.remove(avId) base.cr.centralLogger.writeClientEvent('stopped ignoring %s' % (avId, )) messenger.send("AvatarIgnoreChange") def checkIgnored(self, avId): """ This should be checked before querying the user for interaction or popping up guis requested by other avatars """ return avId and avId in self.ignoredAvatarList #Client->UD request functions def sendRequestInvite(self, avId): self.notify.debugCall() self.sendUpdate("requestInvite", [avId]) self.invitedAvatarsList.append(avId) def sendRequestRemove(self, avId): self.notify.debugCall() self.sendUpdate("requestRemove", [avId]) if avId in self.invitedAvatarsList: self.invitedAvatarsList.remove(avId) #Functions called from UD def friendConsidering(self, avId): self.notify.debugCall() messenger.send(OTPGlobals.AvatarFriendConsideringEvent, [1, avId]) def invitationFrom(self, avId, avatarName): self.notify.debugCall() messenger.send(OTPGlobals.AvatarFriendInvitationEvent, [avId, avatarName]) def retractInvite(self, avId): self.notify.debugCall() messenger.send(OTPGlobals.AvatarFriendRetractInviteEvent, [avId]) if avId in self.invitedAvatarsList: self.invitedAvatarsList.remove(avId) def rejectInvite(self, avId, reason): self.notify.debugCall() messenger.send(OTPGlobals.AvatarFriendRejectInviteEvent, [avId, reason]) if avId in self.invitedAvatarsList: self.invitedAvatarsList.remove(avId) def rejectRemove(self, avId, reason): self.notify.debugCall() messenger.send(OTPGlobals.AvatarFriendRejectRemoveEvent, [avId, reason]) #Functions called from UD def updateAvatarFriend(self, avId, info): if hasattr(info, "avatarId") and (not info.avatarId) and avId: #info.avatarId wasn't being populated, I think this is fixed now # though it's odd to have the data in two places info.avatarId = avId assert self.notify.debugCall() if not avId in self.avatarFriendsList: self.avatarFriendsList.add(avId) self.avatarId2Info[ avId] = info #get the data there before we tell you it's there messenger.send(OTPGlobals.AvatarFriendAddEvent, [avId, info]) if self.avatarId2Info[avId].onlineYesNo != info.onlineYesNo: base.talkAssistant.receiveFriendUpdate(avId, info.getName(), info.onlineYesNo) self.avatarId2Info[avId] = info messenger.send(OTPGlobals.AvatarFriendUpdateEvent, [avId, info]) if avId in self.invitedAvatarsList: self.invitedAvatarsList.remove(avId) messenger.send(OTPGlobals.AvatarNewFriendAddEvent, [avId]) def removeAvatarFriend(self, avId): assert self.notify.debugCall() self.avatarFriendsList.remove(avId) self.avatarId2Info.pop(avId, None) messenger.send(OTPGlobals.AvatarFriendRemoveEvent, [avId]) def setFriends(self, avatarIds): self.notify.debugCall() assert 0, "setFriends should not be sent to client." # client-called helper functions def isFriend(self, avId): return self.isAvatarFriend(avId) def isAvatarFriend(self, avId): return avId in self.avatarFriendsList def getFriendInfo(self, avId): return self.avatarId2Info.get(avId) def countTrueFriends(self): count = 0 for id in self.avatarId2Info: if self.avatarId2Info[id].openChatFriendshipYesNo: count += 1 return count
class NamePage(StateData): notify = directNotify.newCategory('NamePage') def __init__(self, book, parentFSM): self.book = book self.parentFSM = parentFSM StateData.__init__(self, 'namePageDone') self.fsm = ClassicFSM('NamePage', [ State('off', self.enterOff, self.exitOff), State('basePage', self.enterBasePage, self.exitBasePage) ], 'off', 'off') self.fsm.enterInitialState() self.parentFSM.getStateNamed('namePage').addChild(self.fsm) self.nameServ = base.cr.nameServicesManager self.baseRequestIndex = 0 self.requestsPerCluster = 5 # GUI elements self.requestsContainer = {} self.loadingLabel = None self.selectedName = None self.nameButtons = [] self.avId2NameData = {} geom = CIGlobals.getDefaultBtnGeom() self.acceptBtn = DirectButton( geom=geom, text_scale=0.04, relief=None, scale=0.5, text="Accept", pos=(0.5, posY, 0), text_pos=(0, -0.01), command=self.acceptName, ) self.acceptBtn.hide() self.declineBtn = DirectButton( geom=geom, text_scale=0.04, relief=None, scale=0.5, text="Decline", pos=(0.75, posY, 0), text_pos=(0, -0.01), command=self.declineName, ) self.declineBtn.hide() self.avIdLbl = OnscreenText(text="", scale=0.08, pos=(0.3, 0, 0.5), align=TextNode.ACenter) self.avIdLbl.hide() self.accIdLbl = OnscreenText(text="", scale=0.08, pos=(0.3, 0, 0.3), align=TextNode.ACenter) self.accIdLbl.hide() def handleRequests(self): gui = loader.loadModel('phase_3.5/models/gui/friendslist_gui.bam') self.nameList = DirectScrolledList( relief=None, pos=(-0.54, 0, 0.08), incButton_image=(gui.find('**/FndsLst_ScrollUp'), gui.find('**/FndsLst_ScrollDN'), gui.find('**/FndsLst_ScrollUp_Rllvr'), gui.find('**/FndsLst_ScrollUp')), incButton_relief=None, incButton_scale=(arrowButtonScale, arrowButtonScale, -arrowButtonScale), incButton_pos=(buttonXstart, 0, itemFrameZorigin - 0.999), incButton_image3_color=Vec4(1, 1, 1, 0.2), incButton_command=self.__moveItems, incButton_extraArgs=[1], decButton_image=(gui.find('**/FndsLst_ScrollUp'), gui.find('**/FndsLst_ScrollDN'), gui.find('**/FndsLst_ScrollUp_Rllvr'), gui.find('**/FndsLst_ScrollUp')), decButton_relief=None, decButton_scale=(arrowButtonScale, arrowButtonScale, arrowButtonScale), decButton_pos=(buttonXstart, 0, itemFrameZorigin + 0.125), decButton_image3_color=Vec4(1, 1, 1, 0.2), decButton_command=self.__moveItems, decButton_extraArgs=[0], itemFrame_pos=(itemFrameXorigin, 0, itemFrameZorigin), itemFrame_scale=1.0, itemFrame_relief=DGG.SUNKEN, itemFrame_frameSize=(listXorigin, listXorigin + listFrameSizeX, listZorigin, listZorigin + listFrameSizeZ), itemFrame_frameColor=(0.85, 0.95, 1, 1), itemFrame_borderWidth=(0.01, 0.01), numItemsVisible=5, forceHeight=0.075, items=self.nameButtons) self.__buildItems() def __moveItems(self, direction): if direction == 0: # Moving down! self.baseRequestIndex += 1 elif direction == 1: # Moving up! self.baseRequestIndex -= 1 self.clearItems() self.__buildItems() def clearItems(self): for btn in self.nameButtons: btn.destroy() self.nameButtons = [] self.nameList.removeAndDestroyAllItems() def __buildItems(self): for i in xrange(self.requestsPerCluster): request = self.nameServ.getNameRequests()[self.baseRequestIndex + i] date = request['date'] date = date.replace(' ', '-') data = NameData(request['name'], date, request['avId'], request['accId']) self.avId2NameData[data.avId] = data btn = DirectButton(relief=None, text=data.name, text_scale=0.07, text_align=TextNode.ALeft, text1_bg=textDownColor, text2_bg=textRolloverColor, text3_fg=textDisabledColor, textMayChange=0, command=self.__handleNameButton, extraArgs=[data], text_pos=(0, 0, 0.0)) data.btn = btn self.nameButtons.append(btn) self.loadingLabel.hide() def __handleNameButton(self, data): self.selectedName = data data.btn['state'] = DGG.DISABLED self.avIdLbl.setText("Avatar ID:\n" + str(data.avId)) self.avIdLbl.show() self.accIdLbl.setText("Account ID:\n" + str(data.accId)) self.accIdLbl.show() self.acceptBtn.show() self.declineBtn.show() def acceptName(self): pass def load(self): StateData.load(self) self.loadingLabel = OnscreenText(text='Loading...', font=CIGlobals.getToonFont(), pos=(0, 0.1, 0), scale=0.08, parent=aspect2d) def unload(self): StateData.unload(self) self.loadingLabel.destroy() self.loadingLabel = None for request in self.requestsContainer.values(): for element in request: element.destroy() self.requestsContainer = {} def enter(self): StateData.enter(self) self.fsm.request('basePage') base.acceptOnce(self.nameServ.getRequestCompleteName(), self.handleRequests) self.nameServ.d_requestNameData() def exit(self): self.fsm.requestFinalState() StateData.exit(self) def enterBasePage(self): self.book.createPageButtons('adminPage', None) self.book.setTitle('Name Approval') def exitBasePage(self): self.book.deletePageButtons(True, False) self.book.clearTitle() def enterOff(self): pass def exitOff(self): pass
class DistributedDodgeballGameAI(DistributedToonFPSGameAI, TeamMinigameAI): """The winter dodgeball game (AI/server side)""" notify = directNotify.newCategory("DistributedDodgeballGameAI") GameOverTime = 15.0 GameTime = 120 def __init__(self, air): DistributedToonFPSGameAI.__init__(self, air) TeamMinigameAI.__init__(self) self.setZeroCommand(self.__gameOver_time) self.setInitialTime(self.GameTime) self.fsm = ClassicFSM.ClassicFSM('DDodgeballGameAI', [ State.State('off', self.enterOff, self.exitOff), State.State('waitForChooseTeam', self.enterWaitForChooseTeam, self.exitWaitForChooseTeam), State.State('play', self.enterPlay, self.exitPlay), State.State('roundIntermission', self.enterRoundIntermission, self.exitRoundIntermission) ], 'off', 'off') self.fsm.enterInitialState() self.playersReadyToStart = 0 self.resetNumFrozen() self.__resetSnowballOwners() self.availableSpawnsByTeam = {BLUE: [0, 1, 2, 3], RED: [0, 1, 2, 3]} self.announcedWinner = False self.winnerPrize = 200 self.loserPrize = 0 self.gameInAction = False def __resetSnowballOwners(self): self.doId2snowballIndex = {} def reqPickupSnowball(self, idx): sender = self.air.getAvatarIdFromSender() if (idx not in self.doId2snowballIndex.values() and sender not in self.doId2snowballIndex.keys() and self.gameInAction): self.doId2snowballIndex[sender] = idx self.sendUpdateToAvatarId(sender, 'snowballPickupResp', [1, idx]) else: self.sendUpdateToAvatarId(sender, 'snowballPickupResp', [0, idx]) def __clearSnowballOwner(self, idx): sender = self.air.getAvatarIdFromSender() if self.doId2snowballIndex.has_key(sender): if self.doId2snowballIndex[sender] == idx: del self.doId2snowballIndex[sender] else: self.notify.warning( "Player {0} does not own snowball {1}".format(sender, idx)) def snowballHitWall(self, snowballIndex): self.__clearSnowballOwner(snowballIndex) def snowballHitGround(self, snowballIndex): self.__clearSnowballOwner(snowballIndex) def snowballHitPlayer(self, damagedPlayer, throwerTeam, snowballIndex): self.__clearSnowballOwner(snowballIndex) def resetNumFrozen(self): self.numFrozenByTeam = {RED: 0, BLUE: 0} def enterRoundIntermission(self): pass def exitRoundIntermission(self): pass def __gameOver_time(self): self.__gameOver(1) def __decideWinner(self): teams = [BLUE, RED] teams.sort(key=lambda team: self.scoreByTeam[team], reverse=True) self.winnerTeam = teams[0] def __gameOver(self, timeRanOut=0): self.timeRanOutLastRound = timeRanOut self.fsm.request('off') self.resetNumFrozen() self.__resetSnowballOwners() self.gameInAction = False if self.round == MaxRounds: self.__decideWinner() self.sendUpdate('teamWon', [self.winnerTeam, timeRanOut]) else: self.sendUpdate('roundOver', [timeRanOut]) self.fsm.request('play') def enemyFrozeMe(self, myTeam, enemyTeam): if not self.gameInAction: return self.scoreByTeam[enemyTeam] += 1 self.numFrozenByTeam[myTeam] += 1 self.sendUpdate('incrementTeamScore', [enemyTeam]) if self.numFrozenByTeam[myTeam] >= len( self.playerListByTeam[myTeam]) and not self.announcedWinner: # All of the players on this team are frozen! The enemy team wins! self.announcedWinner = True self.timeRanOutLastRound = 0 self.fsm.request('off') self.resetNumFrozen() self.__resetSnowballOwners() self.gameInAction = False if self.round == MaxRounds: self.__decideWinner() self.sendUpdate('teamWon', [self.winnerTeam, 0]) taskMgr.doMethodLater(self.GameOverTime, self.__gameOverTask, self.uniqueName("gameOverTask")) else: self.sendUpdate('roundOver', [0]) self.fsm.request('play') def __gameOverTask(self, task): winners = list(self.playerListByTeam[self.winnerTeam]) self.d_gameOver(1, winners) return task.done def teamMateUnfrozeMe(self, myTeam): self.numFrozenByTeam[myTeam] -= 1 def enterOff(self): pass def exitOff(self): pass def enterWaitForChooseTeam(self): for avatar in self.avatars: self.sendUpdate('setupRemoteAvatar', [avatar.doId]) self.sendUpdate('chooseUrTeam') def exitWaitForChooseTeam(self): pass def enterPlay(self): self.announcedWinner = False self.gameInAction = False self.setRound(self.getRound() + 1) if self.getRound() == 1: self.d_startGame() time = 18.0 else: mult = 2 if self.timeRanOutLastRound: mult = 3 time = (2.05 * mult) + 8.0 base.taskMgr.doMethodLater(time, self.__actuallyStarted, self.uniqueName('actuallyStarted')) def __actuallyStarted(self, task): self.gameInAction = True self.setInitialTime(self.GameTime) self.startTiming() return task.done def exitPlay(self): self.gameInAction = False self.stopTiming() base.taskMgr.remove(self.uniqueName('actuallyStarted')) def allAvatarsReady(self): self.fsm.request('waitForChooseTeam') def choseTeam(self, team): avId = self.air.getAvatarIdFromSender() # We'll send our own accepted message. isOnTeam = TeamMinigameAI.choseTeam(self, team, avId, sendAcceptedMsg=False) if isOnTeam: # Pick a spawn point for them spawnIndex = random.choice(self.availableSpawnsByTeam[team]) self.availableSpawnsByTeam[team].remove(spawnIndex) self.sendUpdateToAvatarId(avId, 'acceptedIntoTeam', [spawnIndex]) def readyToStart(self): self.playersReadyToStart += 1 if self.playersReadyToStart == len(self.avatars): self.fsm.request('play') def delete(self): taskMgr.remove(self.uniqueName('gameOverTask')) self.fsm.requestFinalState() del self.fsm del self.playersReadyToStart del self.availableSpawnsByTeam del self.doId2snowballIndex TeamMinigameAI.cleanup(self) DistributedToonFPSGameAI.delete(self)
# This file is automatically generated by makepanda.py. Do not modify. from __future__ import absolute_import from ._direct import * ### BEGIN direct/src/extensions_native/CInterval_extensions.py from .core import Dtool_funcToMethod from direct.directnotify.DirectNotifyGlobal import directNotify CInterval.DtoolClassDict["notify"] = directNotify.newCategory("Interval") ##################################################################### 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() CInterval.DtoolClassDict["setT_Old"] = CInterval.setT Dtool_funcToMethod(setT, CInterval) del setT ##################################################################### def play(self, t0 = 0.0, duration = None, scale = 1.0): self.notify.error("CInterval.play() is deprecated, use start() instead") if duration: # None or 0 implies full length self.start(t0, t0 + duration, scale) else: self.start(t0, -1, scale) Dtool_funcToMethod(play, CInterval) del play
import os, sys, socket, random #from urllib import quote_plus from pandac.PandaModules import HTTPClient from pandac.PandaModules import HTTPCookie from pandac.PandaModules import URLSpec from pandac.PandaModules import Ramfile from pandac.PandaModules import Ostream from pandac.PandaModules import HTTPDate from pandac.PandaModules import DocumentSpec from direct.task.Task import Task from direct.directnotify.DirectNotifyGlobal import directNotify notify = directNotify.newCategory('UserFunnel') class UserFunnel: def __init__(self): self.hitboxAcct = 'DM53030620EW' self.language = 'en-us' self.cgRoot = 'ToonTown_Online' self.cgBeta = 'Beta' self.cgRelease = 'Release' self.cgLocation = 'US' self.campaignID = '' self.cfCookieFile = 'cf.txt' self.dynamicVRFunnel = 'http://download.toontown.com/' self.hostDict = {0: 'Internal Disney PHP Collector Site', 1: 'ehg-dig.hitbox.com/HG?', 2: 'ehg-dig.hitbox.com/HG?', 3: 'build64.online.disney.com:5020/index.php?'} self.CurrentHost = '' self.URLtoSend = ''
class SetNameTypedFSM(AvatarOperationFSM): notify = directNotify.newCategory('SetNameTypedFSM') POST_ACCOUNT_STATE = 'RetrieveAvatar' def enterStart(self, avId, name): self.avId = avId self.name = name if self.avId: self.demand('RetrieveAccount') return self.demand('JudgeName') def enterRetrieveAvatar(self): if self.avId and self.avId not in self.avList: self.demand( 'Kill', 'Tried to name an avatar (%d) not in the account!' % self.avId) return self.csm.air.dbInterface.queryObject(self.csm.air.dbId, self.avId, self.__handleAvatar) def __handleAvatar(self, dclass, fields): if dclass != self.csm.air.dclassesByName['DistributedToonUD']: self.demand( 'Kill', "One of the account's avatars is invalid! Its DClass is %s, but it should be DistributedToonUD!" % dclass) return if fields['WishNameState'] != WISHNAME_OPEN: self.demand( 'Kill', 'Avatar %d is not supposed to be able to be named right now! Its name status is %d.' % (self.avId, fields['WishNameState'])) return self.demand('JudgeName') def enterJudgeName(self): status = judgeName(self.name) if self.avId and status: if status == 2: self.csm.air.dbInterface.updateObject( self.csm.air.dbId, self.avId, self.csm.air.dclassesByName['DistributedToonUD'], { 'WishNameState': WISHNAME_LOCKED, 'WishName': '', 'setName': (self.name, ) }) else: self.csm.air.dbInterface.updateObject( self.csm.air.dbId, self.avId, self.csm.air.dclassesByName['DistributedToonUD'], { 'WishNameState': WISHNAME_PENDING, 'WishName': self.name, 'WishNameTimestamp': int(time.time()) }) if self.avId: self.csm.air.writeServerEvent('avatar-wishname', self.avId, self.name) self.csm.sendUpdateToAccountId(self.target, 'setNameTypedResp', [self.avId, status]) self.demand('Off')
class SetNamePatternFSM(AvatarOperationFSM): notify = directNotify.newCategory('SetNamePatternFSM') POST_ACCOUNT_STATE = 'RetrieveAvatar' def enterStart(self, avId, pattern): self.avId = avId self.pattern = pattern self.demand('RetrieveAccount') def enterRetrieveAvatar(self): if self.avId and self.avId not in self.avList: self.demand( 'Kill', 'Tried to name an avatar (%d) not in the account!' % self.avId) return self.csm.air.dbInterface.queryObject(self.csm.air.dbId, self.avId, self.__handleAvatar) def __handleAvatar(self, dclass, fields): if dclass != self.csm.air.dclassesByName['DistributedToonUD']: self.demand( 'Kill', "One of the account's avatars is invalid! Its DClass is %s, but it should be DistributedToonUD!" % dclass) return if fields['WishNameState'] != WISHNAME_OPEN: self.demand( 'Kill', 'Avatar %d is not supposed to be able to be named right now! Its name status is %d.' % (self.avId, fields['WishNameState'])) return self.demand('SetName') def enterSetName(self): parts = [] for p, f in self.pattern: if p == 213: p = 212 part = self.csm.nameGenerator.nameDictionary.get(p, ('', ''))[1] if f: part = part[:1].upper() + part[1:] else: part = part.lower() parts.append(part) parts[2] += parts.pop(3) while '' in parts: parts.remove('') name = (' ').join(parts) self.csm.air.dbInterface.updateObject( self.csm.air.dbId, self.avId, self.csm.air.dclassesByName['DistributedToonUD'], { 'WishNameState': WISHNAME_LOCKED, 'WishName': '', 'setName': (name, ) }) self.csm.air.writeServerEvent('avatar-named', avId=self.avId, name=name) self.csm.sendUpdateToAccountId(self.target, 'setNamePatternResp', [self.avId, 1]) self.demand('Off')
class GetAvatarsFSM(AvatarOperationFSM): notify = directNotify.newCategory('GetAvatarsFSM') POST_ACCOUNT_STATE = 'QueryAvatars' def enterStart(self): self.demand('RetrieveAccount') def enterQueryAvatars(self): self.pendingAvatars = set() self.avatarFields = {} for avId in self.avList: if avId: self.pendingAvatars.add(avId) def response(dclass, fields, avId=avId): if self.state != 'QueryAvatars': return if dclass != self.csm.air.dclassesByName[ 'DistributedToonUD']: self.demand( 'Kill', "One of the account's avatars is invalid! Its DClass is %s, but it should be DistributedToonUD!" % dclass) return if not fields.has_key('setDISLid'): self.csm.air.dbInterface.updateObject( self.csm.air.dbId, avId, self.csm.air.dclassesByName['DistributedToonUD'], {'setDISLid': [self.target]}) self.avatarFields[avId] = fields self.pendingAvatars.remove(avId) if not self.pendingAvatars: self.demand('SendAvatars') self.csm.air.dbInterface.queryObject(self.csm.air.dbId, avId, response) if not self.pendingAvatars: self.demand('SendAvatars') def enterSendAvatars(self): potentialAvs = [] for avId, fields in self.avatarFields.items(): index = self.avList.index(avId) wns = fields.get('WishNameState', WISHNAME_LOCKED) name = fields['setName'][0] if wns == WISHNAME_OPEN: nameState = 1 else: if wns == WISHNAME_PENDING: nameState = 2 else: if wns == WISHNAME_APPROVED: nameState = 3 name = fields['WishName'] else: if wns == WISHNAME_REJECTED: nameState = 4 else: if wns == WISHNAME_LOCKED: nameState = 0 else: self.csm.notify.warning( 'Avatar %d is in unknown name state %s.' % (avId, wns)) nameState = 0 potentialAvs.append( [avId, name, fields['setDNAString'][0], index, nameState]) self.csm.sendUpdateToAccountId(self.target, 'setTrialer', [self.trialer]) self.csm.sendUpdateToAccountId(self.target, 'setAvatars', [potentialAvs]) self.demand('Off')
class CreateAvatarFSM(OperationFSM): notify = directNotify.newCategory('CreateAvatarFSM') def enterStart(self, dna, index, skipTutorial): if index >= 7: self.demand('Kill', 'Invalid index (%d) specified!' % index) return if not ToonDNA().isValidNetString(dna): self.demand('Kill', 'Invalid DNA specified!') return self.index = index self.dna = dna self.skipTutorial = skipTutorial self.demand('RetrieveAccount') def enterRetrieveAccount(self): self.csm.air.dbInterface.queryObject(self.csm.air.dbId, self.target, self.__handleRetrieve) def __handleRetrieve(self, dclass, fields): if dclass != self.csm.air.dclassesByName['AccountUD']: self.demand( 'Kill', 'Your account object (%s) was not found in the database!' % dclass) return self.account = fields self.avList = self.account['ACCOUNT_AV_SET'] self.avList = self.avList[:7] self.avList += [0] * (7 - len(self.avList)) if self.avList[self.index]: self.demand( 'Kill', 'Avatar slot %d is already taken by another avatar (%s)!' % (self.index, str(self.avList[self.index]))) return self.demand('CreateAvatar') def enterCreateAvatar(self): dna = ToonDNA() dna.makeFromNetString(self.dna) colorstring = TTLocalizer.NumToColor[dna.headColor] animaltype = TTLocalizer.AnimalToSpecies[dna.getAnimal()] if self.index == 6: name = 'Episode Toon' else: name = colorstring + ' ' + animaltype toonFields = { 'setName': (name, ), 'WishNameState': WISHNAME_OPEN, 'WishName': '', 'setDNAString': (self.dna, ), 'setTutorialAck': (int(self.skipTutorial), ), 'setDISLid': (self.target, ) } self.csm.air.dbInterface.createObject( self.csm.air.dbId, self.csm.air.dclassesByName['DistributedToonUD'], toonFields, self.__handleCreate) def __handleCreate(self, avId): if not avId: self.demand('Kill', 'Database failed to create the new avatar object!') return self.avId = avId self.demand('StoreAvatar') def enterStoreAvatar(self): self.avList[self.index] = self.avId self.csm.air.dbInterface.updateObject( self.csm.air.dbId, self.target, self.csm.air.dclassesByName['AccountUD'], {'ACCOUNT_AV_SET': self.avList}, {'ACCOUNT_AV_SET': self.account['ACCOUNT_AV_SET']}, self.__handleStoreAvatar) def __handleStoreAvatar(self, fields): if fields: self.demand( 'Kill', 'Database failed to associate the new avatar to your account!') return self.csm.air.writeServerEvent('avatar-created', avId=self.avId, accId=self.target, dna=self.dna.encode('hex'), slot=self.index) self.csm.sendUpdateToAccountId(self.target, 'createAvatarResp', [self.avId]) self.demand('Off')
class LoginAccountFSM(OperationFSM): TARGET_CONNECTION = True notify = directNotify.newCategory('LoginAccountFSM') def enterStart(self, cookie): self.cookie = cookie self.demand('QueryAccountDB') def enterQueryAccountDB(self): self.csm.accountDB.lookup(self.cookie, self.__handleLookup) def __handleLookup(self, result): if not result.get('success'): self.csm.air.writeServerEvent('cookie-rejected', clientId=self.target, cookie=self.cookie) self.demand( 'Kill', result.get('reason', 'The accounts database rejected your cookie.')) return self.databaseId = result.get('databaseId', 0) accountId = result.get('accountId', 0) self.adminAccess = result.get('adminAccess', 0) if self.adminAccess < simbase.config.GetInt('minimum-access', 100): self.csm.air.writeServerEvent('insufficient-access', self.target, self.cookie) self.demand( 'Kill', result.get( 'reason', 'You have insufficient access to login.\nYou have access level %d and you need access level %d.' % (self.adminAccess, simbase.config.GetInt('minimum-access', 100)))) return if accountId: self.accountId = accountId self.demand('RetrieveAccount') else: self.demand('CreateAccount') def enterRetrieveAccount(self): self.csm.air.dbInterface.queryObject(self.csm.air.dbId, self.accountId, self.__handleRetrieve) def __handleRetrieve(self, dclass, fields): if dclass != self.csm.air.dclassesByName['AccountUD']: self.demand( 'Kill', 'Your account object (%s) was not found in the database!' % dclass) return self.account = fields self.demand('SetAccount') def enterCreateAccount(self): self.account = { 'ACCOUNT_AV_SET': [0] * 7, 'ESTATE_ID': 0, 'ACCOUNT_AV_SET_DEL': [], 'CREATED': time.ctime(), 'LAST_LOGIN': time.ctime(), 'ACCOUNT_ID': str(self.databaseId), 'ADMIN_ACCESS': self.adminAccess, 'TRIALER': 0 } self.csm.air.dbInterface.createObject( self.csm.air.dbId, self.csm.air.dclassesByName['AccountUD'], self.account, self.__handleCreate) def __handleCreate(self, accountId): if self.state != 'CreateAccount': self.notify.warning( 'Received create account response outside of CreateAccount state.' ) return if not accountId: self.notify.warning( 'Database failed to construct an account object!') self.demand( 'Kill', 'Your account object could not be created in the game database.' ) return self.csm.air.writeServerEvent('accountCreated', accountId) self.accountId = accountId self.demand('StoreAccountID') def enterStoreAccountID(self): self.csm.accountDB.storeAccountID(self.databaseId, self.accountId, self.__handleStored) def __handleStored(self, success=True): if not success: self.demand( 'Kill', 'The account server could not save your account DB ID!') return self.demand('SetAccount') def enterSetAccount(self): dg = PyDatagram() dg.addServerHeader( self.csm.GetAccountConnectionChannel(self.accountId), self.csm.air.ourChannel, CLIENTAGENT_EJECT) dg.addUint16(100) dg.addString('This account has been logged in elsewhere.') self.csm.air.send(dg) dg = PyDatagram() dg.addServerHeader(self.target, self.csm.air.ourChannel, CLIENTAGENT_OPEN_CHANNEL) dg.addChannel(self.csm.GetAccountConnectionChannel(self.accountId)) self.csm.air.send(dg) access = self.account.get('ADMIN_ACCESS', 0) if access >= 400: dg = PyDatagram() dg.addServerHeader(self.target, self.csm.air.ourChannel, CLIENTAGENT_OPEN_CHANNEL) dg.addChannel(OtpDoGlobals.OTP_MOD_CHANNEL) self.csm.air.send(dg) if access >= 500: dg = PyDatagram() dg.addServerHeader(self.target, self.csm.air.ourChannel, CLIENTAGENT_OPEN_CHANNEL) dg.addChannel(OtpDoGlobals.OTP_ADMIN_CHANNEL) self.csm.air.send(dg) dg = PyDatagram() dg.addServerHeader(self.target, self.csm.air.ourChannel, CLIENTAGENT_SET_CLIENT_ID) dg.addChannel(self.accountId << 32) self.csm.air.send(dg) dg = PyDatagram() dg.addServerHeader(self.target, self.csm.air.ourChannel, CLIENTAGENT_SET_STATE) dg.addUint16(2) self.csm.air.send(dg) self.csm.air.dbInterface.updateObject( self.csm.air.dbId, self.accountId, self.csm.air.dclassesByName['AccountUD'], { 'LAST_LOGIN': time.ctime(), 'ACCOUNT_ID': str(self.databaseId), 'ADMIN_ACCESS': self.adminAccess }) self.csm.air.writeServerEvent('account-login', clientId=self.target, accId=self.accountId, webAccId=self.databaseId, cookie=self.cookie) self.csm.sendUpdateToChannel(self.target, 'acceptLogin', []) self.demand('Off')
from direct.extensions_native import VBase4_extensions from direct.extensions_native import NodePath_extensions from panda3d.core import loadPrcFile if __debug__: loadPrcFile('config/general.prc') loadPrcFile('config/release/dev.prc') from direct.directnotify.DirectNotifyGlobal import directNotify notify = directNotify.newCategory('ClientStart') notify.setInfo(True) from otp.settings.Settings import Settings preferencesFilename = ConfigVariableString( 'preferences-filename', 'preferences.json').getValue() notify.info('Reading %s...' % preferencesFilename) __builtin__.settings = Settings(preferencesFilename) if 'fullscreen' not in settings: settings['fullscreen'] = False if 'music' not in settings: settings['music'] = True if 'sfx' not in settings:
class SuitPursueToonBehaviorAI(SuitPathBehaviorAI): notify = directNotify.newCategory('SuitPursueToonBehaviorAI') RemakePathDistance = 10.0 DivertDistance = 5.0 MaxNonSafeDistance = 40.0 MaxAttackersPerTarget = 2 AttackCooldownFactor = 6.0 PickTargetRetryTime = 1.0 def __init__(self, suit, pathFinder): SuitPathBehaviorAI.__init__(self, suit, False) self.fsm = ClassicFSM.ClassicFSM('SuitPursueToonBehaviorAI', [State.State('off', self.enterOff, self.exitOff), State.State('pursue', self.enterPursue, self.exitPursue), State.State('divert', self.enterDivert, self.exitDivert), State.State('attack', self.enterAttack, self.exitAttack)], 'off', 'off') self.fsm.enterInitialState() self.air = self.suit.air self.target = None self.targetId = None self.pathFinder = pathFinder self.suitList = None self.suitDict = None def setSuitList(self, sList): self.suitList = sList def setSuitDict(self, sDict): self.suitDict = sDict def enter(self): SuitPathBehaviorAI.enter(self) self.__tryPickAndPursue() def __tryPickAndPursue(self): if self.pickTarget(): # Choose a distance that is good enough to attack this target. # the more damage we do, the more distance we can have and still be effective dmgMod = self.suit.suitPlan.getCogClassAttrs().dmgMod self.attackSafeDistance = random.uniform(15 * dmgMod, 30 * dmgMod) # Now, chase them down! self.fsm.request('pursue') def setTarget(self, toon): self.targetId = toon.doId self.target = toon self.suit.sendUpdate('setChaseTarget', [self.targetId]) def pickTarget(self): if not self.battle: return if len(self.battle.getAvatars()) == 0: # We have nothing to do. return # Choose the toon with the least amount of attackers to target (a maximum of two attackers per target). avIds = list(self.battle.getAvatars()) avIds.sort(key = lambda avId: self.battle.getNumSuitsTargeting(avId)) leastAmt = self.battle.getNumSuitsTargeting(avIds[0]) for avId in self.battle.getAvatars(): if self.battle.getNumSuitsTargeting(avId) != leastAmt and avId in avIds: avIds.remove(avId) #for avId in self.battle.getAvatars(): # numAttackers = len(self.suit.getBattleZone().getSuitsTargetingAvId(avId)) # if numAttackers >= self.MaxAttackersPerTarget: # # This toon has too many attackers already. # print str(self.suit.doId) + ": Toon " + str(avId) + " already has " + str(numAttackers) + " attackers." # avIds.remove(avId) # Temporary fix for district resets. TODO: Actually correct this. for avId in self.battle.getAvatars(): av = self.air.doId2do.get(avId) if av is None and avId in avIds: avIds.remove(avId) elif av is not None and av.isDead() and avId in avIds: # Don't target dead toons. avIds.remove(avId) elif av is not None and (not self.isPlayerVisible(av)) and avId in avIds: avIds.remove(avId) # Make sure we found some avatars to pursue. if len(avIds) == 0: taskMgr.doMethodLater(self.PickTargetRetryTime, self.__pickTargetRetryTask, self.suit.uniqueName("PickTargetRetryTask")) return 0 # At this point the avIds are only toons with the least amount of attackers. # For example, there may be two toons with no attackers, so randomly pick between those two toons. self.targetId = random.choice(avIds) self.target = self.air.doId2do.get(self.targetId) self.suit.sendUpdate('setChaseTarget', [self.targetId]) self.suit.getBattleZone().newTarget(self.suit.doId, self.targetId) return 1 def resetNextFrame(self): if not hasattr(self, 'suit') or not self.suit: return taskMgr.remove(self.suit.uniqueName('resetNextFrame')) taskMgr.doMethodLater(self.PickTargetRetryTime, self.reset, self.suit.taskName('resetNextFrame')) def reset(self, task = None): if not hasattr(self, 'suit') or not self.suit: return self.exit() self.enter() if task: return task.done def exit(self): taskMgr.remove(self.suit.uniqueName('resetNextFrame')) taskMgr.remove(self.suit.uniqueName("PickTargetRetryTask")) self.fsm.request('off') self.target = None self.targetId = None self.suit.getBattleZone().clearTargets(self.suit.doId) self.suit.sendUpdate('setChaseTarget', [0]) SuitPathBehaviorAI.exit(self) def unload(self): self.mgr = None self.battle = None self.target = None self.targetId = None self.suitList = None self.suitDict = None self.air = None SuitPathBehaviorAI.unload(self) def enterOff(self): pass def exitOff(self): pass def __pickTargetRetryTask(self, task): self.__tryPickAndPursue() return task.done def enterAttack(self, useSafeDistance = True): taskMgr.add(self._attackTask, self.suit.uniqueName('attackToonTask'), extraArgs = [useSafeDistance], appendTask = True) def _attackTask(self, useSafeDistance, task): if not self.isAvatarReachable(self.target) or not self.isPlayerVisible(self.target, checkVisionAngle = False): self.resetNextFrame() return task.done if useSafeDistance: safeDistance = self.attackSafeDistance else: safeDistance = SuitPursueToonBehaviorAI.MaxNonSafeDistance if self.suit.getDistance(self.target) > safeDistance: # Nope, we're too far away! We need to chase them down! self.fsm.request('pursue') return task.done if self.target.isDead(): # They've died, stop attacking self.resetNextFrame() return task.done attack = SuitUtils.attack(self.suit, self.target) timeout = SuitAttacks.SuitAttacks.attack2attackClass[attack].length task.delayTime = timeout return task.again def exitAttack(self): taskMgr.remove(self.suit.uniqueName('attackToonTask')) def enterDivert(self): moveVector = Vec2() currPos = Point2(self.suit.getX(render), self.suit.getY(render)) if self.suitList is not None: data = self.suitList elif self.suitDict is not None: data = self.suitDict.values() for suit in data: if suit == self.suit: continue otherPos = Point2(suit.getX(render), suit.getY(render)) moveAway = currPos - otherPos if moveAway.length() > self.DivertDistance: continue moveMag = 1.0 / max(moveAway.lengthSquared(), 0.1) moveAway.normalize() moveAway *= moveMag moveVector += moveAway moveVector.normalize() x, y = currPos + (moveVector * self.DivertDistance) if not self.createPath(pos = (x, y, self.suit.getZ(render))): self.resetNextFrame() def walkDone(self): if self.fsm.getCurrentState().getName() == 'divert': self.fsm.request('pursue') def exitDivert(self): self.clearWalkTrack() def enterPursue(self): # Make our initial path to the toon. if not self.isAvatarReachable(self.target): self.resetNextFrame() return self.lastCheckedPos = self.target.getPos(render) if not self.isPlayerVisible(self.target, checkVisionAngle = False) or not self.createPath(self.target): # We couldn't figure out a good path to this target. # Try again in a little bit. self.resetNextFrame() return taskMgr.add(self._pursueTask, self.suit.uniqueName('pursueToonTask')) taskMgr.add(self._scanTask, self.suit.uniqueName('scanTask')) def _scanTask(self, task): if self.suitList is not None: data = self.suitList elif self.suitDict is not None: data = self.suitDict.values() else: return task.done myClass = self.suit.suitPlan.getCogClass() myLevel = self.suit.getLevel() for suit in data: if suit == self.suit or not suit.suitPlan: continue theirClass = suit.suitPlan.getCogClass() theirLevel = suit.getLevel() if theirClass < myClass: # Don't make way for a lower class than me. continue elif theirClass == myClass and theirLevel < myLevel: # If we are the same class, don't make way for a lower level than me. continue elif (theirLevel == myLevel and suit.doId > self.suit.doId): # If we are the same level, don't make way for a younger cog. continue currPos = Point2(self.suit.getX(render), self.suit.getY(render)) otherPos = Point2(suit.getX(render), suit.getY(render)) if (currPos - otherPos).length() < self.DivertDistance: self.fsm.request('divert') return task.done return task.again def _pursueTask(self, task): if self.target: if self.target.isDead() or not hasattr(self, 'attackSafeDistance'): self.resetNextFrame() return task.done currPos = self.target.getPos(render) if self.suit.getDistance(self.target) <= self.attackSafeDistance and not self.target.isDead(): # We're a good distance to attack this toon. Let's do it. self.fsm.request('attack') return task.done elif (currPos.getXy() - self.lastCheckedPos.getXy()).length() >= SuitPursueToonBehaviorAI.RemakePathDistance: # They're too far from where we're trying to go! Make a new path to where they are! self.lastCheckedPos = self.target.getPos(render) self.createPath(self.target) task.delayTime = 1.0 return task.again def exitPursue(self): taskMgr.remove(self.suit.uniqueName('scanTask')) taskMgr.remove(self.suit.uniqueName('pursueToonTask')) if hasattr(self, 'lastCheckedPos'): del self.lastCheckedPos self.clearWalkTrack() def shouldStart(self): return True
class LoadAvatarFSM(AvatarOperationFSM): notify = directNotify.newCategory('LoadAvatarFSM') POST_ACCOUNT_STATE = 'GetTargetAvatar' def enterStart(self, avId): self.avId = avId self.demand('RetrieveAccount') def enterGetTargetAvatar(self): if self.avId not in self.avList: self.demand( 'Kill', 'Tried to play an avatar (%d) not in the account!' % self.avId) return self.csm.air.dbInterface.queryObject(self.csm.air.dbId, self.avId, self.__handleAvatar) def __handleAvatar(self, dclass, fields): if dclass != self.csm.air.dclassesByName['DistributedToonUD']: self.demand( 'Kill', "One of the account's avatars is invalid! Its DClass is %s, but it should be DistributedToonUD!" % dclass) return self.avatar = fields self.demand('SetAvatar') def enterSetAvatar(self): channel = self.csm.GetAccountConnectionChannel(self.target) dgcleanup = PyDatagram() dgcleanup.addServerHeader(self.avId, channel, STATESERVER_OBJECT_DELETE_RAM) dgcleanup.addUint32(self.avId) dg = PyDatagram() dg.addServerHeader(channel, self.csm.air.ourChannel, CLIENTAGENT_ADD_POST_REMOVE) dg.addString(dgcleanup.getMessage()) self.csm.air.send(dg) adminAccess = self.account.get('ADMIN_ACCESS', 0) adminAccess = adminAccess - adminAccess % 100 self.csm.air.sendActivate( self.avId, 0, 0, self.csm.air.dclassesByName['DistributedToonUD'], {'setAdminAccess': [self.account.get('ADMIN_ACCESS', 0)]}) dg = PyDatagram() dg.addServerHeader(channel, self.csm.air.ourChannel, CLIENTAGENT_OPEN_CHANNEL) dg.addChannel(self.csm.GetPuppetConnectionChannel(self.avId)) self.csm.air.send(dg) dg = PyDatagram() dg.addServerHeader(channel, self.csm.air.ourChannel, CLIENTAGENT_ADD_SESSION_OBJECT) dg.addUint32(self.avId) self.csm.air.send(dg) dg = PyDatagram() dg.addServerHeader(channel, self.csm.air.ourChannel, CLIENTAGENT_SET_CLIENT_ID) dg.addChannel(self.target << 32 | self.avId) self.csm.air.send(dg) dg = PyDatagram() dg.addServerHeader(self.avId, self.csm.air.ourChannel, STATESERVER_OBJECT_SET_OWNER) dg.addChannel(self.csm.GetAccountConnectionChannel(self.target)) self.csm.air.send(dg) fields = self.avatar fields.update( {'setAdminAccess': [self.account.get('ADMIN_ACCESS', 0)]}) self.csm.air.friendsManager.toonOnline(self.avId, fields) self.csm.air.globalPartyMgr.avatarJoined(self.avId) self.csm.air.writeServerEvent('avatar-chosen', avId=self.avId, accId=self.target) self.demand('Off')
class DLPlayground(Playground): notify = directNotify.newCategory("DLPlayground")
import direct from pandac.PandaModules import HttpRequest from direct.directnotify.DirectNotifyGlobal import directNotify from direct.task.TaskManagerGlobal import taskMgr from direct.task import Task from LandingPage import LandingPage from direct.showbase import ElementTree as ET 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 ClientServicesManagerUD(DistributedObjectGlobalUD): notify = directNotify.newCategory('ClientServicesManagerUD') def announceGenerate(self): DistributedObjectGlobalUD.announceGenerate(self) self.connection2fsm = {} self.account2fsm = {} self.nameGenerator = NameGenerator() self.wantMiniServer = config.GetBool('want-mini-server', False) dbtype = config.GetString('accountdb-type', 'developer') if dbtype == 'developer': self.accountDB = DeveloperAccountDB(self) else: if dbtype == 'local': self.accountDB = LocalAccountDB(self) else: self.notify.error('Invalid account DB type configured: %s' % dbtype) self.loginsEnabled = True def killConnection(self, connId, code=122, reason=''): self.notify.info('Booting client: %d out for reason(%d): %s' % (int(connId), int(code), str(reason))) dg = PyDatagram() dg.addServerHeader(connId, self.air.ourChannel, CLIENTAGENT_EJECT) dg.addUint16(int(code)) dg.addString(str(reason)) self.air.send(dg) def killConnectionFSM(self, connId): fsm = self.connection2fsm.get(connId) if not fsm: self.notify.warning( 'Tried to kill connection %d for duplicate FSM, but none exists!' % connId) return self.killConnection(connId, reason='An operation is already underway: ' + fsm.name) def killAccount(self, accountId, reason): self.killConnection(self.GetAccountConnectionChannel(accountId), reason=reason) def killAccountFSM(self, accountId): fsm = self.account2fsm.get(accountId) if not fsm: self.notify.warning( 'Tried to kill account %d for duplicate FSM, but none exists!' % accountId) return self.killAccount(accountId, 'An operation is already underway: ' + fsm.name) def runAccountFSM(self, fsmtype, *args): sender = self.air.getAccountIdFromSender() if not sender: self.killAccount(sender, 'Client is not logged in.') if sender in self.account2fsm: self.killAccountFSM(sender) return self.account2fsm[sender] = fsmtype(self, sender) self.account2fsm[sender].request('Start', *args) def setLoginEnabled(self, enable): if not enable: self.notify.warning( 'The CSMUD has been told to reject logins! All future logins will now be rejected.' ) self.loginsEnabled = enable def login(self, cookie, hwId, sig, serverURL): self.notify.debug('Received login cookie %r from %d' % (cookie, self.air.getMsgSender())) sender = self.air.getMsgSender() self.air.writeServerEvent('login', hwId=hwId, serverURL=serverURL, cookie=cookie) if not self.wantMiniServer and serverURL != '127.0.0.1': self.killConnection( sender, 201, 'This server is not currently running in mini-server mode. Please try again later.' ) return if not self.loginsEnabled: self.killConnection( sender, 200, 'Logins are currently disabled. Please try again later.') return if sender >> 32: self.killConnection(sender, reason='Client is already logged in.') return key = config.GetString('csmud-secret', 'broken-code-store') + config.GetString( 'server-version', 'no_version_set') + str( self.air.hashVal) + FIXED_KEY computedSig = hmac.new(key, cookie, hashlib.sha256).digest() if sig != computedSig: self.killConnection( sender, reason='The accounts database rejected your cookie') return if config.GetString('server-password', ''): self.sendUpdateToChannel(sender, 'loginResponse', [True]) return self.sendUpdateToChannel(sender, 'loginResponse', [False]) self.performLogin(hwId, cookie) def authenticateLogin(self, cookie, password, hwId): sender = self.air.getMsgSender() if password == config.GetString('server-password', ''): self.sendUpdateToChannel(sender, 'authenticationResponse', [True]) self.performLogin(hwId, cookie) else: self.sendUpdateToChannel(sender, 'authenticationResponse', [False]) def performLogin(self, hwId, cookie): immuneHardwareIds = ['0x5800e36aeac8L'] sender = self.air.getMsgSender() data = simbase.air.banManager.getActiveBans()[0] if hwId in data and hwId not in immuneHardwareIds: data = data[hwId] reason = data['reason'] simbase.air.writeServerEvent( 'security', issue= 'Client has attempted to login but their account has been terminated!', accId=data['accId'], hwId=hwId, cookie=cookie, reason=reason) self.killConnection(connId=sender, code=152, reason=data['reason']) if sender in self.connection2fsm: self.killConnectionFSM(sender) return self.connection2fsm[sender] = LoginAccountFSM(self, sender) self.connection2fsm[sender].request('Start', cookie) def requestAvatars(self): self.notify.debug('Received avatar list request from %d' % self.air.getMsgSender()) self.runAccountFSM(GetAvatarsFSM) def createAvatar(self, dna, index, skipTutorial): self.runAccountFSM(CreateAvatarFSM, dna, index, skipTutorial) def deleteAvatar(self, avId): self.runAccountFSM(DeleteAvatarFSM, avId) def setNameTyped(self, avId, name): self.runAccountFSM(SetNameTypedFSM, avId, name) def setNamePattern(self, avId, p1, f1, p2, f2, p3, f3, p4, f4): self.runAccountFSM(SetNamePatternFSM, avId, [(p1, f1), (p2, f2), (p3, f3), (p4, f4)]) def acknowledgeAvatarName(self, avId): self.runAccountFSM(AcknowledgeNameFSM, avId) def chooseAvatar(self, avId): currentAvId = self.air.getAvatarIdFromSender() accountId = self.air.getAccountIdFromSender() if currentAvId and avId: self.killAccount(accountId, 'A Toon is already chosen!') return if not currentAvId and not avId: return if avId: self.runAccountFSM(LoadAvatarFSM, avId) else: self.runAccountFSM(UnloadAvatarFSM, currentAvId) def reportPlayer(self, avId, category): reporterId = self.air.getAvatarIdFromSender() if len(REPORT_REASONS) <= category: self.air.writeServerEvent( 'suspicious', avId=reporterId, issue='Invalid report reason index (%d) sent by avatar.' % category) return self.air.writeServerEvent('player-reported', reporterId=reporterId, avId=avId, category=REPORT_REASONS[category])
class CharSelection(DirectObject): notify = directNotify.newCategory('CharSelection') STAGE_TOON_POS = (66.4, 74.47, -25) STAGE_TOON_HPR = (227.73, 0, 0) NO_TOON = "Empty Slot" PLAY = "Play" CREATE = "Create" TITLE = "Pick A Toon To Play" def __init__(self, avChooser): self.avChooser = avChooser self.choice = None self.charList = None self.charNameLabel = None self.charButtons = [] self.playOrCreateButton = None self.deleteButton = None self.quitButton = None self.world = None self.sky = None self.fog = None self.title = None self.stageToon = None self.deleteConf = None self.frame = None self.selectionFSM = ClassicFSM.ClassicFSM( 'CharSelection', [ State.State('off', self.enterOff, self.exitOff), State.State('character', self.enterCharSelected, self.exitCharSelected), State.State('empty', self.enterEmptySelected, self.exitEmptySelected) ], 'off', 'off' ) self.selectionFSM.enterInitialState() def __setupStageToon(self): self.stageToon = Toon(base.cr) self.stageToon.setPos(self.STAGE_TOON_POS) self.stageToon.setHpr(self.STAGE_TOON_HPR) def cleanupStageToon(self): if self.stageToon != None: self.stageToon.disable() self.stageToon.delete() self.stageToon = None def enterOff(self): pass def exitOff(self): pass def enterCharSelected(self, slot): self.choice = self.avChooser.getAvChoiceBySlot(slot) dna = self.choice.dna name = self.choice.name self.stageToon.setName(name) self.stageToon.setDNAStrand(dna) self.stageToon.nametag.setNametagColor(NametagGlobals.NametagColors[NametagGlobals.CCLocal]) self.stageToon.nametag.setActive(0) self.stageToon.nametag.updateAll() self.stageToon.nametag.nametag3d.request('Rollover') self.stageToon.animFSM.request('neutral') self.stageToon.reparentTo(base.render) self.charNameLabel.setText(name) self.playOrCreateButton['text'] = self.PLAY self.playOrCreateButton['extraArgs'] = ['play'] self.playOrCreateButton.show() self.deleteButton.show() def exitCharSelected(self): self.stageToon.animFSM.requestFinalState() self.stageToon.deleteCurrentToon() self.stageToon.reparentTo(base.hidden) self.playOrCreateButton.hide() self.deleteButton.hide() self.choice = None def enterEmptySelected(self): self.charNameLabel.setText(self.NO_TOON) self.playOrCreateButton['text'] = self.CREATE self.playOrCreateButton['extraArgs'] = ['create'] self.playOrCreateButton.show() def exitEmptySelected(self): self.playOrCreateButton.hide() def __action(self, action): for btn in self.charButtons: if btn['state'] == DGG.DISABLED: self.slot = btn.getPythonTag('slot') break func = None arg = None doFade = True if action == 'delete': func = self.deleteToon arg = self.choice.avId doFade = False elif action == 'play': func = self.playGame arg = self.choice.slot elif action == 'create': func = self.enterMAT elif action == 'quit': func = sys.exit if doFade: base.transitions.fadeOut(0.3) if arg != None: Sequence(Wait(0.31), Func(func, arg)).start() else: Sequence(Wait(0.31), Func(func)).start() else: if arg != None: func(arg) else: func() def playGame(self, slot): messenger.send("avChooseDone", [self.avChooser.getAvChoiceBySlot(slot)]) def enterMAT(self): messenger.send("enterMakeAToon", [self.slot]) def deleteToon(self, avId): # Show a confirmation message self.deleteConf = Dialog.GlobalDialog( message = 'This will delete {0} forever. Are you sure?'.format(self.avChooser.getNameFromAvId(avId)), style = Dialog.YesNo, doneEvent = 'deleteConfResponse', extraArgs = [avId]) self.acceptOnce('deleteConfResponse', self.__handleDeleteConfResponse) self.deleteConf.show() def __handleDeleteConfResponse(self, avId): doneStatus = self.deleteConf.getValue() if doneStatus: # Alright, they pressed yes. No complaining to us. self.avChooser.avChooseFSM.request("waitForToonDelResponse", [avId]) else: self.deleteConf.cleanup() self.deleteConf = None def __handleCharButton(self, slot): for btn in self.charButtons: if btn.getPythonTag('slot') == slot: btn['state'] = DGG.DISABLED else: btn['state'] = DGG.NORMAL if self.avChooser.hasToonInSlot(slot): self.selectionFSM.request('character', [slot]) else: self.selectionFSM.request('empty') def load(self): base.cr.renderFrame() base.camLens.setMinFov(CIGlobals.DefaultCameraFov / (4./3.)) self.__setupStageToon() holidayMgr = base.cr.holidayManager self.props = [] self.world = loader.loadModel('phase_9/models/cogHQ/SellbotHQExterior.bam') self.world.reparentTo(base.render) self.world.setPos(0, 227.09, -25.36) self.sky = loader.loadModel('phase_9/models/cogHQ/cog_sky.bam') self.sky.setScale(1) self.sky.reparentTo(base.render) self.sky.find('**/InnerGroup').removeNode() self.fog = Fog('charSelectFog') self.fog.setColor(0.2, 0.2, 0.2) self.fog.setExpDensity(0.003) base.render.setFog(self.fog) # Let's fix the flickering doors. doors = self.world.find('**/doors').getChildren() for door in doors: for frameHole in door.findAllMatches('**/doorFrameHole*'): frameHole.removeNode() if holidayMgr.getHoliday() == HolidayType.CHRISTMAS: piles = { 'half' : {'pos' : (57.28, 86.47, -25.00), 'hpr' : (46.79, 0, 0)}, 'full' : {'pos' : (71.23, 85.2, -25.00), 'hpr' : (290.82, 0, 0)}, 'half_2' : {'pos' : (-15, 128.69, -25), 'hpr' : (60.26, 0, 0)} } for pileType, info in piles.items(): if '_' in pileType: pileType = pileType[:-2] pile = loader.loadModel('phase_8/models/props/snow_pile_%s.bam' % (pileType)) pile.reparentTo(render) pile.setPos(info['pos']) pile.setHpr(info['hpr']) self.props.append(pile) self.world.find('**/TopRocks').removeNode() snowTxt = loader.loadTexture('winter/maps/sbhq_snow.png') self.world.find('**/Ground').setTexture(snowTxt, 1) self.particles = ParticleLoader.loadParticleEffect('phase_8/etc/snowdisk.ptf') self.particles.setPos(0, 0, 5) self.particlesRender = self.world.attachNewNode('snowRender') self.particlesRender.setDepthWrite(0) self.particlesRender.setBin('fixed', 1) self.particles.start(parent = camera, renderParent = self.particlesRender) self.fog.setColor(0.486, 0.784, 1) self.fog.setExpDensity(0.006) base.render.setFog(self.fog) self.title = DirectLabel(text=self.TITLE, text_font=CIGlobals.getMickeyFont(), text_fg=(1, 0.9, 0.1, 1), relief=None, text_scale=0.13, pos=(0, 0, 0.82)) self.charNameLabel = OnscreenText(text = "", font = CIGlobals.getMickeyFont(), pos = (-0.25, 0.5, 0), fg = (1, 0.9, 0.1, 1.0)) self.charNameLabel.hide() self.frame = DirectFrame() self.frame['image'] = DGG.getDefaultDialogGeom() self.frame['image_color'] = CIGlobals.DialogColor self.frame['image_scale'] = (-0.9, -0.9, 0.8) self.frame['image_pos'] = (0.82, 0, -0.125) self.playOrCreateButton = DirectButton(text = "", pos = (0.8125, 0, -0.35), command = self.__action, geom = CIGlobals.getDefaultBtnGeom(), text_scale = 0.06, relief = None, text_pos = (0, -0.01)) self.playOrCreateButton.hide() self.deleteButton = DirectButton(text = "Delete", pos = (0.8125, 0, -0.45), command = self.__action, extraArgs = ['delete'], geom = CIGlobals.getDefaultBtnGeom(), text_scale = 0.06, relief = None, text_pos = (0, -0.01)) self.deleteButton.hide() self.quitButton = DirectButton(text = "Quit", pos = (-1.10, 0, -0.925), command = self.__action, extraArgs = ['quit'], text_scale = 0.06, geom = CIGlobals.getDefaultBtnGeom(), relief = None, text_pos = (0, -0.01)) textRolloverColor = Vec4(1, 1, 0, 1) textDownColor = Vec4(0.5, 0.9, 1, 1) textDisabledColor = Vec4(0.4, 0.8, 0.4, 1) for slot in range(6): if self.avChooser.hasToonInSlot(slot): choice = self.avChooser.getAvChoiceBySlot(slot) text = choice.name else: text = self.NO_TOON btn = DirectButton( relief=None, text = text, text_scale=0.06, text_align=TextNode.ALeft, text1_bg=textDownColor, text2_bg=textRolloverColor, text3_fg=textDisabledColor, textMayChange=0, command=self.__handleCharButton, extraArgs=[slot], text_pos = (0, 0, 0.0) ) btn.setPythonTag('slot', slot) self.charButtons.append(btn) btn['state'] = DGG.NORMAL gui = loader.loadModel('phase_3.5/models/gui/friendslist_gui.bam') listXorigin = -0.02 listFrameSizeX = 0.625 listZorigin = -0.43 listFrameSizeZ = 0.51 arrowButtonScale = 0.0 itemFrameXorigin = -0.237 itemFrameZorigin = 0.365 buttonXstart = itemFrameXorigin + 0.293 self.charList = DirectScrolledList( relief=None, pos=(0.75, 0, -0.225), incButton_image=None, #incButton_relief=None, incButton_scale=(arrowButtonScale, arrowButtonScale, -arrowButtonScale), #incButton_pos=(buttonXstart, 0, itemFrameZorigin - 0.999), #incButton_image3_color=Vec4(1, 1, 1, 0.2), decButton_image=None, #decButton_relief=None, decButton_scale=(arrowButtonScale, arrowButtonScale, arrowButtonScale), #decButton_pos=(buttonXstart, 0, itemFrameZorigin + 0.125), #decButton_image3_color=Vec4(1, 1, 1, 0.2), itemFrame_pos=(itemFrameXorigin, 0, itemFrameZorigin), itemFrame_scale=1.0, itemFrame_relief=DGG.SUNKEN, itemFrame_frameSize=(listXorigin, listXorigin + listFrameSizeX, listZorigin, listZorigin + listFrameSizeZ), itemFrame_frameColor=(0.85, 0.95, 1, 1), itemFrame_borderWidth=(0.01, 0.01), numItemsVisible=15, forceHeight=0.075, items=self.charButtons, parent = self.frame ) base.camera.setPos(75.12, 63.22, -23) base.camera.setHpr(26.57, 9.62, 0) def unload(self): self.selectionFSM.requestFinalState() self.cleanupStageToon() self.choice = None if self.frame: self.frame.destroy() self.frame = None if self.charButtons: for btn in self.charButtons: btn.destroy() self.charButtons = None if self.deleteConf: self.deleteConf.cleanup() self.deleteConf = None if self.charList: self.charList.destroy() self.charList = None if self.charNameLabel: self.charNameLabel.destroy() self.charNameLabel = None if self.playOrCreateButton: self.playOrCreateButton.destroy() self.playOrCreateButton = None if self.deleteButton: self.deleteButton.destroy() self.deleteButton = None if self.quitButton: self.quitButton.destroy() self.quitButton = None if self.sky: self.sky.removeNode() self.sky = None if self.world: self.world.removeNode() self.world = None if self.title: self.title.destroy() self.title = None for prop in self.props: if not prop.isEmpty(): prop.removeNode() self.props = None if hasattr(self, 'particles'): self.particles.cleanup() self.particlesRender.removeNode() self.particles = None del self.particlesRender base.render.clearFog() self.fog = None base.camera.setPos(0, 0, 0) base.camera.setHpr(0, 0, 0) base.transitions.noTransitions() del self.selectionFSM
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(0) 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 = ClockObject.getGlobalClock().getFrameTime() self.__playRate = playRate self.__doLoop = doLoop self.__loopCount = 0 def setupResume(self): now = ClockObject.getGlobalClock().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 = ClockObject.getGlobalClock().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 t = (now - self.__clockStart) * self.__playRate + self.__endT if t >= self.__startT: # 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.__startTAtStart: # Only finalize if the playback cycle includes the # whole interval. if self.isStopped(): if self.getOpenEnded() or self.__loopCount != 0: self.privReverseInstant() else: self.privReverseFinalize() else: if self.isStopped(): self.privReverseInitialize(self.__startT) else: self.privStep(self.__startT) # 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 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) open_ended = property(getOpenEnded) stopped = property(isStopped) t = property(getT, setT) play_rate = property(getPlayRate, setPlayRate) done_event = property(getDoneEvent, setDoneEvent) # 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. """ # Don't use a regular import, to prevent ModuleFinder from picking # it up as a dependency when building a .p3d package. import importlib, sys if sys.version_info >= (3, 0): tkinter = importlib.import_module('tkinter') else: tkinter = importlib.import_module('Tkinter') if tl == None: tl = tkinter.Toplevel() tl.title('Interval Controls') outerFrame = tkinter.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=tkinter.X) bf = tkinter.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 = tkinter.Button(bf, text='<<', command=toStart) # Stop/play buttons def doPlay(s=self, es=es): s.resume(es.get()) stop = tkinter.Button(bf, text='Stop', command=lambda s=self: s.pause()) play = tkinter.Button(bf, text='Play', command=doPlay) jumpToEnd = tkinter.Button(bf, text='>>', command=toEnd) jumpToStart.pack(side=tkinter.LEFT, expand=1, fill=tkinter.X) play.pack(side=tkinter.LEFT, expand=1, fill=tkinter.X) stop.pack(side=tkinter.LEFT, expand=1, fill=tkinter.X) jumpToEnd.pack(side=tkinter.LEFT, expand=1, fill=tkinter.X) bf.pack(expand=1, fill=tkinter.X) outerFrame.pack(expand=1, fill=tkinter.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)
class AssetBrowser(QtWidgets.QDialog): FileExtensions = [] PreloadItems = True notify = directNotify.newCategory("AssetBrowser") notify.setDebug(True) def __init__(self, parent): QtWidgets.QDialog.__init__(self, parent) self.ui = Ui_AssetBrowser() self.ui.setupUi(self) self.setModal(True) self.setAttribute(QtCore.Qt.WA_DeleteOnClose, False) self.callback = None self.firstTime = True self.recentlyUsed = [] self.currentFolder = None self.selectedAsset = None self.modelTrees = [] self.folderContext = None self.recentlyUsedContext = None self.ui.folderView.itemSelectionChanged.connect( self.__handleFolderSelectionChanged) self.ui.fileView.itemDoubleClicked.connect(self.__confirmAsset) self.ui.fileView.itemSelectionChanged.connect(self.__selectAsset) self.ui.recentView.itemDoubleClicked.connect(self.__confirmAsset) self.ui.recentView.itemSelectionChanged.connect( self.__selectRecentAsset) self.ui.leFileFilter.textChanged.connect(self.__filterList) def show(self, parent, callback): self.callback = callback #self.setParent(parent) self.setModal(True) QtWidgets.QDialog.show(self) if self.firstTime: self.firstTime = False self.initialize() else: self.generateRecentlyUsedAssets() if self.folderContext: self.folderContext.resume() def initialize(self): assert self.notify.debug("Initializing asset browser") # Find all of the models in the tree self.generateAssetTree() # Then create a widget for each folder that contains models self.generateFolderWidgetTree() # Generate the recently used items self.generateRecentlyUsedAssets() self.ui.folderView.setCurrentItem(self.modelTrees[0].rootFolder.item) def generateRecentlyUsedAssets(self): if self.recentlyUsedContext: self.recentlyUsedContext.cleanup() self.ui.recentView.clear() ctx = AssetCreationContext(self, list(self.recentlyUsed), self.ui.recentView, False) ctx.createAssets() self.recentlyUsedContext = ctx def __selectAsset(self): items = self.ui.fileView.selectedItems() if len(items) > 0: item = items[0] self.selectedAsset = item.filename self.ui.recentView.setCurrentItem(None) def __selectRecentAsset(self): items = self.ui.recentView.selectedItems() if len(items) > 0: item = items[0] self.selectedAsset = item.filename self.ui.fileView.setCurrentItem(None) def __confirmAsset(self, item): # User double clicked a model, confirm selection and close dialog self.selectedAsset = item.filename self.hide(1) def __filterList(self, text): text = text.lower() for i in range(self.ui.fileView.count()): self.ui.fileView.setRowHidden(i, False) if len(text) == 0: return for i in range(self.ui.fileView.count()): if text not in self.ui.fileView.item(i).text().lower(): self.ui.fileView.setRowHidden(i, True) def filterFileList(self): self.__filterList(self.ui.leFileFilter.text()) def __handleFolderSelectionChanged(self): item = self.ui.folderView.selectedItems()[0] self.currentFolder = item.assetFolder self.generateAssets() def generateFolderWidgetTree(self): self.ui.folderView.clear() for tree in self.modelTrees: self.r_generateFolderWidgetTree(tree.rootFolder, None) self.ui.folderView.expandToDepth(0) def r_generateFolderWidgetTree(self, folder, parent): item = AssetFolderWidgetItem() item.assetFolder = folder folder.item = item if folder.parentRelativeFilename: item.setText(0, folder.parentRelativeFilename.getFullpath()) elif folder.alias: item.setText(0, folder.alias) else: item.setText(0, folder.filename.getFullpath()) item.setToolTip(0, item.text(0)) if parent: parent.addChild(item) else: self.ui.folderView.addTopLevelItem(item) for child in folder.children: self.r_generateFolderWidgetTree(child, item) def r_generateAssets(self, tree, dirFilename, parentModelFolder): vfs = core.VirtualFileSystem.getGlobalPtr() contents = vfs.scanDirectory(dirFilename) gotHit = False subfolders = [] assetFiles = [] for virtualFile in contents.getFiles(): filename = virtualFile.getFilename() if virtualFile.isDirectory(): subfolders.append(filename) elif filename.getExtension() in self.FileExtensions: assert self.notify.debug("Found asset " + filename.getFullpath()) filename.makeRelativeTo(tree.absRoot) assert self.notify.debug("Rel path: " + filename.getFullpath()) assetFiles.append(filename) gotHit = True thisFolder = AssetFolder(dirFilename, parentModelFolder) thisFolder.assetFiles = assetFiles gotChildHits = False for sub in subfolders: _, ret = self.r_generateAssets(tree, sub, thisFolder) if not gotChildHits: gotChildHits = ret if parentModelFolder and (gotChildHits or gotHit): parentModelFolder.children.append(thisFolder) return [thisFolder, gotHit or gotChildHits] def r_createFilesList(self, folder): self.files += folder.assetFiles for child in folder.children: self.r_createFilesList(child) def generateAssetTree(self): # Generate an asset tree for each tree we're attached to ending in # MODELS. self.modelTrees = [] ctprojs = os.environ.get("CTPROJS") if not ctprojs: return projs = ctprojs.split("+") for proj_data in projs: name, flavor = proj_data.split(":") if not name.endswith("MODELS"): # Not a model tree. continue path = os.environ.get(name.upper()) rootFilename = core.Filename.fromOsSpecific(path) + "/built" assert self.notify.debug("Generating asset tree for " + name + " (" + rootFilename.getFullpath() + ")") tree = ModelTree("$" + name.upper(), rootFilename) tree.rootFolder = self.r_generateAssets(tree, rootFilename, None)[0] tree.rootFolder.alias = tree.name self.modelTrees.append(tree) def generateAssets(self): if self.folderContext: self.folderContext.cleanup() self.ui.fileView.clear() self.files = [] self.r_createFilesList(self.currentFolder) self.files.sort() # Generate the assets thumbnails in the selected folder ctx = AssetCreationContext(self, self.files, self.ui.fileView, True) ctx.createAssets() self.folderContext = ctx def hide(self, ret): self.setParent(None) if self.folderContext: if not self.folderContext.done: self.folderContext.pause() else: self.folderContext.cleanup() self.folderContext = None if self.recentlyUsedContext: self.recentlyUsedContext.cleanup() self.recentlyUsedContext = None if ret and self.selectedAsset: fullpath = self.selectedAsset.getFullpath() if not fullpath in self.recentlyUsed: self.recentlyUsed.insert(0, fullpath) else: # If the asset is already in the recently-used list, # bring it to the front. self.recentlyUsed.insert( 0, self.recentlyUsed.pop(self.recentlyUsed.index(fullpath))) if self.callback: self.callback(ret, self.selectedAsset) self.callback = None QtWidgets.QDialog.hide(self) def done(self, ret): self.hide(ret)
class DistributedPieTurretManager(DistributedObject): notify = directNotify.newCategory('DistributedPieTurretManager') def __init__(self, cr): DistributedObject.__init__(self, cr) self.myTurret = None self.guiFrame = None self.guiLabel = None self.guiBar = None self.guiBg = None self.turretGag = None def announceGenerate(self): DistributedObject.announceGenerate(self) base.taskMgr.add(self.__pollMyBattle, '__pollMyBattle') def disable(self): base.taskMgr.remove("DPTM.pollTurret") base.taskMgr.remove("__pollMyBattle") if hasattr(self, 'makeTurretBtn'): self.makeTurretBtn.destroy() del self.makeTurretBtn self.destroyGui() self.myTurret = None if base.localAvatar.getBattleZone(): base.localAvatar.getBattleZone().setTurretManager(None) DistributedObject.disable(self) def clearTurret(self): self.turret = None def __pollTurret(self, turretId, task): turret = self.cr.doId2do.get(turretId) if turret != None: self.myTurret = turret self.myTurret.b_setGag(self.turretGag) self.turretGag = None self.acceptOnce(turret.getDeathEvent(), self.clearTurret) self.makeGui() return Task.done return Task.cont def setGag(self, upgradeId): self.turretGag = upgradeId def d_requestPlace(self, posHpr): self.sendUpdate('requestPlace', [posHpr]) def turretPlaced(self, turretId): base.taskMgr.add(self.__pollTurret, 'DPTM.pollTurret', extraArgs = [turretId], appendTask = True) def yourTurretIsDead(self): base.taskMgr.remove('DPTM.pollTurret') self.destroyGui() self.myTurret = None if base.localAvatar.getPUInventory()[0] > 0: self.createTurretButton() def makeGui(self): self.destroyGui() self.guiFrame = DirectFrame(parent = base.a2dBottomRight, pos=(-0.55, 0, 0.15)) self.guiBg = OnscreenImage(image = "phase_4/maps/turret_gui_bg.png", scale = (0.15, 0, 0.075), parent = self.guiFrame) self.guiBg.setTransparency(True) self.guiLabel = DirectLabel(text = "Turret", text_fg = (1, 0, 0, 1), relief = None, text_scale = 0.05, text_font = loader.loadFont("phase_3/models/fonts/ImpressBT.ttf"), pos = (0, 0, 0.025), parent = self.guiFrame) self.guiBar = DirectWaitBar(range = self.myTurret.getMaxHealth(), value = self.myTurret.getHealth(), scale = 0.125, parent = self.guiFrame, pos = (0, 0, -0.01)) def createTurretButton(self): self.makeTurretBtn = DirectButton( relief = None, geom = CIGlobals.getDefaultBtnGeom(), text = 'Turret', text_scale = 0.055, command = self.handleMakeTurretBtn, pos = (-0.47, 0, 0.1), geom_scale = (0.5, 1.0, 1.0), text_pos = (0, -0.01), parent = base.a2dBottomRight ) if base.localAvatar.getPUInventory()[0]: self.setGag(base.localAvatar.getPUInventory()[1]) def handleMakeTurretBtn(self): self.makeTurretBtn.destroy() del self.makeTurretBtn x, y, z = base.localAvatar.getPos() h, p, r = base.localAvatar.getHpr() self.d_requestPlace([x, y, z, h, p, r]) base.localAvatar.sendUpdate('usedPU', [0]) def __pollMyBattle(self, task): if base.localAvatar.getBattleZone(): base.localAvatar.getBattleZone().setTurretManager(self) if base.localAvatar.getPUInventory()[0] > 0: self.createTurretButton() return Task.done return Task.cont def destroyGui(self): if self.guiBar: self.guiBar.destroy() self.guiBar = None if self.guiLabel: self.guiLabel.destroy() self.guiLabel = None if self.guiBg: self.guiBg.destroy() self.guiBg = None if self.guiFrame: self.guiFrame.destroy() self.guiFrame = None def updateTurretGui(self): if self.guiBar: self.guiBar.update(self.myTurret.getHealth()) def getTurret(self): return self.myTurret
class FriendsManagerUD(DistributedObjectGlobalUD): notify = directNotify.newCategory("FriendsManagerUD") def __init__(self, air): DistributedObjectGlobalUD.__init__(self, air) self.toonsOnline = [] self.air.netMessenger.accept('avatarOnline', self, self.toonOnline) self.air.netMessenger.accept('avatarOffline', self, self.toonOffline) def sendWhisper(self, target, message): sender = self.air.getAvatarIdFromSender() def senderAvatarResponse(dclass, fields): if dclass != self.air.dclassesByName['DistributedPlayerToonUD']: return name = fields['setName'][0] self.sendUpdateToAvatarId(target, 'whisper', [sender, message, name]) self.air.dbInterface.queryObject(self.air.dbId, sender, senderAvatarResponse) def requestFriendsList(self, sender=None): if sender is None: sender = self.air.getAvatarIdFromSender() RequestFriendsListProcess(self, self.air, sender) def toonOnline(self, avatarId): def avatarResponse(dclass, fields): if dclass != self.air.dclassesByName['DistributedPlayerToonUD']: self.notify.warning( 'toonOnline: avatarResponse: Attempted to get name of a newly online Toon and retrieved non-toon.' ) return name = fields['setName'][0] friendsList = fields['setFriendsList'][0] self.d_toonOnline(avatarId, friendsList, name) self.air.dbInterface.queryObject(self.air.dbId, avatarId, avatarResponse) def toonOffline(self, avatarId): def avatarResponse(dclass, fields): if dclass != self.air.dclassesByName['DistributedPlayerToonUD']: self.notify.warning( 'toonOffline: avatarResponse: Attempted to get name of an offline Toon and retrieved non-toon.' ) return name = fields['setName'][0] friendsList = fields['setFriendsList'][0] self.d_toonOffline(avatarId, friendsList, name) self.air.dbInterface.queryObject(self.air.dbId, avatarId, avatarResponse) def d_toonOnline(self, avatarId, friendsList, name): if not avatarId in self.toonsOnline: self.toonsOnline.append(avatarId) for friendId in friendsList: if friendId in self.toonsOnline: self.sendUpdateToAvatarId(friendId, 'toonOnline', [avatarId, name]) def d_toonOffline(self, avatarId, friendsList, name): if avatarId in self.toonsOnline: self.toonsOnline.remove(avatarId) for friendId in friendsList: if friendId in self.toonsOnline: self.sendUpdateToAvatarId(friendId, 'toonOffline', [avatarId, name]) def requestAvatarInfo(self, avId): sender = self.air.getAvatarIdFromSender() def avatarResponse(dclass, fields): if dclass != self.air.dclassesByName["DistributedPlayerToonUD"]: self.notify.warning( "requestAvatarInfo: avatarResponse: It's not a toon.") return name = fields['setName'][0] dna = fields['setDNAStrand'][0] maxHP = fields['setMaxHealth'][0] hp = fields['setHealth'][0] zoneId = fields['setLastHood'][0] try: shardId = fields['setDefaultShard'][0] except: shardId = 0 accessLevel = fields['setAccessLevel'][0] isOnline = int(avId in self.toonsOnline) self.sendUpdateToAvatarId( sender, 'avatarInfo', [name, dna, maxHP, hp, zoneId, shardId, isOnline, accessLevel]) self.air.dbInterface.queryObject(self.air.dbId, avId, avatarResponse) def askAvatarToBeFriends(self, avId): sender = self.air.getAvatarIdFromSender() def avatarResponse(dclass, fields): if dclass != self.air.dclassesByName["DistributedPlayerToonUD"]: self.notify.warning( "requestAvatarInfo: avatarResponse: It's not a toon.") return name = fields['setName'][0] dna = fields['setDNAStrand'][0] self.sendUpdateToAvatarId(avId, 'friendRequest', [sender, name, dna]) self.air.dbInterface.queryObject(self.air.dbId, sender, avatarResponse) def iRemovedFriend(self, friendId): sender = self.air.getAvatarIdFromSender() def removerAvatarResponse(dclass, fields): if dclass != self.air.dclassesByName["DistributedPlayerToonUD"]: self.notify.warning( "iRemovedFriend: removerAvatarResponse: It's not a toon.") return newList = list(fields['setFriendsList'][0]) newList.remove(friendId) dg = dclass.aiFormatUpdate('setFriendsList', sender, sender, self.air.ourChannel, [newList]) self.air.send(dg) self.air.dbInterface.updateObject(self.air.dbId, sender, dclass, {'setFriendsList': [newList]}) def removeeAvatarResponse(dclass, fields): if dclass != self.air.dclassesByName["DistributedPlayerToonUD"]: self.notify.warning( "iRemovedFriend: removeeAvatarResponse: It's not a toon.") return newList = list(fields['setFriendsList'][0]) newList.remove(sender) dg = dclass.aiFormatUpdate('setFriendsList', friendId, friendId, self.air.ourChannel, [newList]) self.air.send(dg) self.air.dbInterface.updateObject(self.air.dbId, friendId, dclass, {'setFriendsList': [newList]}) self.air.dbInterface.queryObject(self.air.dbId, sender, removerAvatarResponse) self.air.dbInterface.queryObject(self.air.dbId, friendId, removeeAvatarResponse) self.sendUpdateToAvatarId(friendId, 'friendLeftYourList', [sender]) def iAcceptedFriendRequest(self, avatarId): sender = self.air.getAvatarIdFromSender() def accepterAvatarResponse(dclass, fields): if dclass != self.air.dclassesByName["DistributedPlayerToonUD"]: self.notify.warning( "iAcceptedFriendRequest: accepterAvatarResponse: It's not a toon." ) return newList = list(fields['setFriendsList'][0]) newList.append(avatarId) dg = dclass.aiFormatUpdate('setFriendsList', sender, sender, self.air.ourChannel, [newList]) self.air.send(dg) self.air.dbInterface.updateObject(self.air.dbId, sender, dclass, {'setFriendsList': [newList]}) def requesterAvatarResponse(dclass, fields): if dclass != self.air.dclassesByName["DistributedPlayerToonUD"]: self.notify.warning( "iAcceptedFriendRequest: requesterAvatarResponse: It's not a toon." ) return newList = list(fields['setFriendsList'][0]) newList.append(sender) dg = dclass.aiFormatUpdate('setFriendsList', avatarId, avatarId, self.air.ourChannel, [newList]) self.air.send(dg) self.air.dbInterface.updateObject(self.air.dbId, avatarId, dclass, {'setFriendsList': [newList]}) self.air.dbInterface.queryObject(self.air.dbId, sender, accepterAvatarResponse) self.air.dbInterface.queryObject(self.air.dbId, avatarId, requesterAvatarResponse) self.sendUpdateToAvatarId(avatarId, 'acceptedFriendRequest', []) def iRejectedFriendRequest(self, avatarId): self.sendUpdateToAvatarId(avatarId, 'rejectedFriendRequest', []) def iCancelledFriendRequest(self, avatarId): sender = self.air.getAvatarIdFromSender() self.sendUpdateToAvatarId(avatarId, 'cancelFriendRequest', [sender]) def requestAvatarStatus(self, avatarId): sender = self.air.getAvatarIdFromSender() self.sendUpdateToAvatarId(avatarId, 'someoneWantsYourStatus', [sender]) def myAvatarStatus(self, avatarId, status): sender = self.air.getAvatarIdFromSender() self.sendUpdateToAvatarId(avatarId, 'avatarStatus', [sender, status]) def iWantToTeleportToAvatar(self, avatarId): sender = self.air.getAvatarIdFromSender() self.sendUpdateToAvatarId(avatarId, 'avatarWantsYourLocation', [sender]) def myAvatarLocation(self, avatarId, shardId, zoneId): sender = self.air.getAvatarIdFromSender() def teleportingAvatarResponse(dclass, fields): if dclass != self.air.dclassesByName['DistributedPlayerToonUD']: return name = fields['setName'][0] self.sendUpdateToAvatarId(sender, 'teleportNotify', [name]) self.air.dbInterface.queryObject(self.air.dbId, avatarId, teleportingAvatarResponse) self.sendUpdateToAvatarId(avatarId, 'avatarLocation', [sender, shardId, zoneId])
from extension_native_helpers import * Dtool_PreloadDLL('libdirect') from libdirect import * from extension_native_helpers import * try: Dtool_PreloadDLL('libp3direct') from libp3direct import * except: Dtool_PreloadDLL('libdirect') from libdirect import * from direct.directnotify.DirectNotifyGlobal import directNotify notify = directNotify.newCategory('Interval') Dtool_ObjectToDict(CInterval, 'notify', notify) del notify def setT(self, t): 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: self.start(t0, t0 + duration, scale) else: self.start(t0, -1, scale)
class StateData(DirectObject): """ A StateData is a base class for a single state within a Finite State Machine (ClassicFSM). """ notify = directNotify.newCategory('StateData') def __init__(self, doneEvent): self.doneEvent = doneEvent self.doneStatus = None self.isLoaded = 0 self.isEntered = 0 def enter(self): """ Enters the StateData. This makes it active in whatever sense this applies. Returns true if this is a change (i.e. it was not previously entered), or false if this is the same (i.e. it was already entered). """ if self.isEntered: return 0 if not self.isLoaded: self.notify.warning("entered StateData before it was loaded") self.load() self.isEntered = 1 StateData.notify.debug('enter()') return 1 def exit(self): """ Exits the StateData. Returns true if this is a change (i.e. it was previously entered), or false if this is the same (i.e. it was already exited). """ if not self.isEntered: return 0 self.isEntered = 0 StateData.notify.debug('exit()') return 1 def load(self): """ Loads the StateData. This loads whatever assets are needed from disk, and otherwise prepares the StateData for being entered, without actually entering it. Returns true if this is a change (i.e. it was not already loaded), or false if this is the same (i.e. it was previously loaded). """ if self.isLoaded: return 0 self.isLoaded = 1 StateData.notify.debug('load()') return 1 def unload(self): """ Unloads the StateData. This frees whatever assets were loaded by load(), and generally makes the memory usage for this thing be as small as possible. Some StateData-derived classes can load and unload repeatedly; others are useless once they have been unloaded. """ if not self.isLoaded: return 0 if self.isEntered: self.notify.warning("unloaded StateData before it was exited") self.exit() self.isLoaded = 0 StateData.notify.debug('unload()') return 1 def getDoneStatus(self): """ The done status of a state data may be anything. It is common practice to return a Python dictionary or a string; the default value is None. """ return self.doneStatus
from __future__ import division import itertools import struct import panda3d.core as core from direct.showbase.DirectObject import DirectObject from direct.task.TaskManagerGlobal import taskMgr from direct.showbase.MessengerGlobal import messenger from direct.directnotify.DirectNotifyGlobal import directNotify import world notify = directNotify.newCategory('geometry') class GeomBuilder(object): def __init__(self, name, master): self.name = name self.master = master self.primitive = core.GeomTriangles(core.Geom.UHStatic) self.primitive.setIndexType(core.Geom.NTUint32) self.indices = [] def add_block(self, x, y, form, hidden): if hidden: form = world.Block.FORMS['Hidden'] else: form = form offset = self.master.index_offset(x, y, form) self.indices.extend([i + offset for i in form.indices])
class ToontownRPCConnection: notify = directNotify.newCategory('ToontownRPCConnection') def __init__(self, socket, handler): self.socket = socket self.dispatcher = ToontownRPCDispatcher(handler) self.socketLock = threading.Lock() self.readLock = threading.RLock() self.writeLock = threading.RLock() self.readBuffer = '' self.writeQueue = [] self.writeSemaphore = threading.Semaphore(0) self.writeThread = threading.Thread(target=self.__writeThread) self.writeThread.start() def __readHeaders(self): # Acquire a read lock so that nothing intervenes: self.readLock.acquire() # Read data until we find the '\r\n\r\n' terminator: while '\r\n\r\n' not in self.readBuffer: try: self.readBuffer += self.socket.recv(2048) except: self.readLock.release() return {} if not self.readBuffer: # It looks like we have nothing to read. self.readLock.release() return {} # Collect the data before the terminator: terminatorIndex = self.readBuffer.find('\r\n\r\n') data = self.readBuffer[:terminatorIndex] # Truncate the remaining data: self.readBuffer = self.readBuffer[terminatorIndex + 4:] # We're done working with the read buffer, so: self.readLock.release() # Return the parsed headers in the form of a dictionary: return self.__parseHeaders(data) def __parseHeaders(self, data): headers = {} for i, line in enumerate(data.split('\n')): line = line.rstrip('\r') if i == 0: # This is the HTTP request. words = line.split(' ') if len(words) != 3: self.writeHTTPError(400) return {} command, _, version = words if command != 'POST': self.writeHTTPError(501) return {} if version not in ('HTTP/1.0', 'HTTP/1.1'): self.writeHTTPError(505) return {} else: # This is an HTTP header. words = line.split(': ', 1) if len(words) != 2: self.writeHTTPError(400) return {} headers[words[0].lower()] = words[1] return headers def read(self, timeout=None): """ Read an HTTP POST request from the socket, and return the body. """ self.socketLock.acquire() self.socket.settimeout(timeout) # Read our HTTP headers: headers = self.__readHeaders() if not headers: # It looks like we have nothing to read. self.socketLock.release() return '' # We need a content-length in order to read POST data: contentLength = headers.get('content-length', '') if (not contentLength) or (not contentLength.isdigit()): self.socketLock.release() self.writeHTTPError(400) return '' # Acquire a read lock so nothing touches the read buffer while we work: self.readLock.acquire() contentLength = int(contentLength) # Ensure we have all of our content: while len(self.readBuffer) < contentLength: try: self.readBuffer += self.socket.recv(2048) except: self.readLock.release() self.socketLock.release() return '' if not self.readBuffer: # It looks like we have nothing to read. self.readLock.release() self.socketLock.release() return '' # Collect the content: data = self.readBuffer[:contentLength] # Truncate the remaining data: self.readBuffer = self.readBuffer[contentLength + 1:] # Release our thread locks: self.readLock.release() self.socketLock.release() try: return data.decode('utf-8') except UnicodeDecodeError: return '' def __writeNow(self, data, timeout=None): # Acquire a write lock so nothing intervenes: self.writeLock.acquire() self.socket.settimeout(timeout) # Ensure the data ends with a new line: if not data.endswith('\n'): data += '\n' while data: try: sent = self.socket.send(data) except: break if sent == 0: break data = data[sent:] # Release our write lock: self.writeLock.release() def __writeThread(self): while True: self.writeSemaphore.acquire() # Ensure we have a request in the write queue: if not self.writeQueue: continue request = self.writeQueue.pop(0) terminate = request.get('terminate') if terminate is not None: # Clear the write queue, and stop: self.writeQueue = [] terminate.set() break # Write the data to the socket: data = request.get('data') if data: self.__writeNow(data, timeout=request.get('timeout')) def write(self, data, timeout=None): """ Add data to the write queue. """ self.writeQueue.append({'data': data, 'timeout': timeout}) self.writeSemaphore.release() def writeHTTPResponse(self, body, contentType=None, code=200): """ Write an HTTP response to the socket. """ response = 'HTTP/1.1 %d %s\r\n' % (code, httplib.responses.get(code)) # Add the standard headers: response += 'Date: %s\r\n' % time.strftime('%a, %d %b %Y %H:%M:%S GMT', time.gmtime()) response += 'Server: TTC-RPCServer/0.1\r\n' # Add the content headers: response += 'Content-Length: %d\r\n' % len(body) if contentType is not None: response += 'Content-Type: %s\r\n' % contentType # Add the body: response += '\r\n' + body # Finally, send it out: self.write(response, timeout=5) def writeHTTPError(self, code): """ Write an HTTP error response to the socket. """ self.notify.warning('Received a bad HTTP request: ' + str(code)) body = '%d %s' % (code, httplib.responses.get(code)) self.writeHTTPResponse(body, contentType='text/plain', code=code) def writeJSONResponse(self, response, id=None): """ Write a JSON response object to the socket. """ response.update({'jsonrpc': '2.0', 'id': id}) try: body = json.dumps(response, encoding='latin-1') except TypeError: self.writeJSONError(-32603, 'Internal error') return self.writeHTTPResponse(body, contentType='application/json') def writeJSONError(self, code, message, id=None): """ Write a JSON error response object to the socket. """ self.notify.warning('Received a bad JSON request: %d %s' % (code, message)) response = {'error': {'code': code, 'message': message}} self.writeJSONResponse(response, id=id) def close(self): """ Wait until the write queue is empty, then shutdown and close our socket. """ terminate = threading2.Event() self.writeQueue.append({'terminate': terminate}) self.writeSemaphore.release() terminate.wait() try: self.socket.shutdown(socket.SHUT_RDWR) except socket.error: pass self.socket.close() def dispatchUntilEmpty(self): """ Read and dispatch until there is nothing left. """ while True: data = self.read(timeout=5) if not data: # We have nothing left to read. break try: request = json.loads(data) except ValueError: self.writeJSONError(-32700, 'Parse error') continue request = ToontownRPCRequest(self, request.get('method'), params=request.get('params') or (), id=request.get('id'), notification=('id' not in request)) self.dispatcher.dispatch(request)
import world import camera import gui import geometry import block_picker import zmap import console import dorf import tools import designation #loadPrcFileData("", "want-directtools #t") #loadPrcFileData("", "want-tk #t") notify = directNotify.newCategory('dorfmain') class Dorfdelf(ShowBase): def __init__(self): ShowBase.__init__(self) wp = core.WindowProperties() wp.setTitle("Dorfdelf") self.win.requestProperties(wp) self.render.setAntialias(core.AntialiasAttrib.MAuto) self.setBackgroundColor(0.5, 0.5, 0.5) self.disableMouse() self.enableParticles() font = self.loader.loadFont('media/Carlito-Regular.ttf')
class ToonBase(ShowBase): notify = directNotify.newCategory("ToonBase") def __init__(self): ShowBase.__init__(self) self.toons = [] types = [("boy", "dgl"), ("boy", "dgm"), ("boy", "dgs"), ("girl", "dgl"), ("girl", "dgm"), ("girl", "dgs")] x = 0 for data in types: toon = self.makeToon(*data) toon.setX(x) self.toons.append(toon) x += 5 def makeToon(self, gender, torso): if gender == "boy": torso += "_shorts" elif gender == "girl": torso += "_skirt" toon = Actor(None, None, None, flattenable=0, setFinal=1) # head mdl + anim toon.loadModel('phase_3/models/char/tt_a_chr_dgm_skirt_head_1000.bam', 'head') toon.loadAnims( { "neutral": "phase_3/models/char/tt_a_chr_dgm_skirt_head_neutral.bam" }, 'head') # torso mdl + anim toon.loadModel( 'phase_3/models/char/tt_a_chr_' + torso + '_torso_1000.bam', 'torso') toon.loadAnims( { 'neutral': 'phase_3/models/char/tt_a_chr_' + torso + '_torso_neutral.bam' }, 'torso') # legs mdl + anim toon.loadModel('phase_3/models/char/tt_a_chr_dgm_shorts_legs_1000.bam', 'legs') toon.loadAnims( { 'neutral': 'phase_3/models/char/tt_a_chr_dgm_shorts_legs_neutral.bam' }, 'legs') # attach parts toon.attach('head', 'torso', 'def_head') toon.attach('torso', 'legs', 'joint_hips') color = (0.347656, 0.820312, 0.953125, 1.0) toon.find('**/arms').setColor(color) toon.find('**/legs').setColor(color) toon.find('**/feet').setColor(color) toon.find('**/neck').setColor(color) toon.find('**/head').setColor(color) toon.find('**/head-front').setColor(color) toon.find('**/hands').setColor((1, 1, 1, 1)) toon.find('**/shoes').removeNode() toon.find('**/boots_long').removeNode() toon.find('**/boots_short').removeNode() shirt = loader.loadTexture('phase_4/maps/4thJulyShirt2.jpg') sleeve = loader.loadTexture('phase_4/maps/4thJulySleeve2.jpg') if gender == "boy": bottom = loader.loadTexture('phase_4/maps/4thJulyShorts1.jpg') elif gender == "girl": bottom = loader.loadTexture('phase_4/maps/4thJulySkirt1.jpg') toon.find('**/torso-top').setTexture(shirt, 1) toon.find('**/sleeves').setTexture(sleeve, 1) toon.find('**/torso-bot').setTexture(bottom, 1) glasses = loader.loadModel( 'phase_4/models/accessories/tt_m_chr_avt_acc_msk_dorkGlasses.bam') glassesNode = toon.getPart('head').attachNewNode('glassesNode') glasses.reparentTo(glassesNode) glasses.setScale(0.27, 0.2, 0.35) glasses.setH(180) glasses.setZ(0.27) glasses.setY(0.2) shadow = loader.loadModel('phase_3/models/props/drop_shadow.bam') shadow.reparentTo(toon.find('**/joint_shadow')) shadow.setScale(0.4) shadow.flattenMedium() shadow.setBillboardAxis(4) shadow.setColor(0, 0, 0, 0.5, 1) toon.reparentTo(render) toon.loop('neutral') return toon
from DistributedObjectAI import DistributedObjectAI from direct.directnotify.DirectNotifyGlobal import directNotify if __debug__: notify = directNotify.newCategory('DistributedObjectGlobalAI') class DistributedObjectGlobalAI(DistributedObjectAI): if __debug__: notify = notify 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:
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(builtins.__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 """
# TO DO: THIS IS QUITE DISGUSTING # MOVE THIS TO ToontownInternalRepository (this might be interesting for AI) _data = {} if len(extras.items()) != 0: for k, v in extras.items(): _data[k] = v signature = hashlib.sha512(json.dumps(_data) + apiSecret).hexdigest() data = urllib.urlencode({'data': json.dumps(_data), 'hmac': signature}) req = urllib2.Request('http://www.toontowncrystal.com/api/' + url, data) req.get_method = lambda: "POST" try: return urllib2.urlopen(req).read() except: return None notify = directNotify.newCategory('ClientServicesManagerUD') def executeHttpRequestAndLog(url, **extras): # SEE ABOVE response = executeHttpRequest(url, extras) if response is None: notify.error('A request to ' + url + ' went wrong.') return None try: data = json.loads(response) except: notify.error('Malformed response from ' + url + '.') return None
class AppRunner(DirectObject): """ This class is intended to be compiled into the Panda3D runtime distributable, to execute a packaged p3d application. It also provides some useful runtime services while running in that packaged environment. It does not usually exist while running Python directly, but you can use dummyAppRunner() to create one at startup for testing or development purposes. """ notify = directNotify.newCategory("AppRunner") ConfigBasename = 'config.xml' # Default values for parameters that are absent from the config file: maxDiskUsage = 2048 * 1048576 # 2 GB # Values for verifyContents, from p3d_plugin.h P3DVCNone = 0 P3DVCNormal = 1 P3DVCForce = 2 P3DVCNever = 3 # Also from p3d_plugin.h P3D_CONTENTS_DEFAULT_MAX_AGE = 5 def __init__(self): DirectObject.__init__(self) # We direct both our stdout and stderr objects onto Panda's # Notify stream. This ensures that unadorned print statements # made within Python will get routed into the log properly. stream = StreamWriter(Notify.out(), False) sys.stdout = stream sys.stderr = stream # This is set true by dummyAppRunner(), below. self.dummy = False # These will be set from the application flags when # setP3DFilename() is called. self.allowPythonDev = False self.guiApp = False self.interactiveConsole = False self.initialAppImport = False self.trueFileIO = False self.respectPerPlatform = None self.verifyContents = self.P3DVCNone self.sessionId = 0 self.packedAppEnvironmentInitialized = False self.gotWindow = False self.gotP3DFilename = False self.p3dFilename = None self.p3dUrl = None self.started = False self.windowOpened = False self.windowPrc = None self.http = None if hasattr(core, 'HTTPClient'): self.http = core.HTTPClient.getGlobalPtr() self.Undefined = Undefined self.ConcreteStruct = ConcreteStruct # This is per session. self.nextScriptId = 0 # TODO: we need one of these per instance, not per session. self.instanceId = None # The root Panda3D install directory. This is filled in when # the instance starts up. self.rootDir = None # The log directory. Also filled in when the instance starts. self.logDirectory = None # self.superMirrorUrl, if nonempty, is the "super mirror" URL # that should be contacted first before trying the actual # host. This is primarily used for "downloading" from a # locally-stored Panda3D installation. This is also filled in # when the instance starts up. self.superMirrorUrl = None # A list of the Panda3D packages that have been loaded. self.installedPackages = [] # A list of the Panda3D packages that in the queue to be # downloaded. self.downloadingPackages = [] # A dictionary of HostInfo objects for the various download # hosts we have imported packages from. self.hosts = {} # The altHost string that is in effect from the HTML tokens, # if any, and the dictionary of URL remapping: orig host url # -> alt host url. self.altHost = None self.altHostMap = {} # The URL from which Panda itself should be downloaded. self.pandaHostUrl = PandaSystem.getPackageHostUrl() # Application code can assign a callable object here; if so, # it will be invoked when an uncaught exception propagates to # the top of the TaskMgr.run() loop. self.exceptionHandler = None # Managing packages for runtime download. self.downloadingPackages = [] self.downloadTask = None # The mount point for the multifile. For now, this is always # the current working directory, for convenience; but when we # move to multiple-instance sessions, it may have to be # different for each instance. self.multifileRoot = str(ExecutionEnvironment.getCwd()) # The "main" object will be exposed to the DOM as a property # of the plugin object; that is, document.pluginobject.main in # JavaScript will be appRunner.main here. This may be # replaced with a direct reference to the JavaScript object # later, in setInstanceInfo(). self.main = ScriptAttributes() # By default, we publish a stop() method so the browser can # easy stop the plugin. A particular application can remove # this if it chooses. self.main.stop = self.stop # This will be the browser's toplevel window DOM object; # e.g. self.dom.document will be the document. self.dom = None # This is the list of expressions we will evaluate when # self.dom gets assigned. self.deferredEvals = [] # This is the default requestFunc that is installed if we # never call setRequestFunc(). def defaultRequestFunc(*args): if args[1] == 'notify': # Quietly ignore notifies. return self.notify.info("Ignoring request: %s" % (args, )) self.requestFunc = defaultRequestFunc # This will be filled in with the default WindowProperties for # this instance, e.g. the WindowProperties necessary to # re-embed a window in the browser frame. self.windowProperties = None # Store our pointer so DirectStart-based apps can find us. if AppRunnerGlobal.appRunner is None: AppRunnerGlobal.appRunner = self # We use this messenger hook to dispatch this __startIfReady() # call back to the main thread. self.accept('AppRunner_startIfReady', self.__startIfReady) def getToken(self, tokenName): """ Returns the value of the indicated web token as a string, if it was set, or None if it was not. """ return self.tokenDict.get(tokenName.lower(), None) def getTokenInt(self, tokenName): """ Returns the value of the indicated web token as an integer value, if it was set, or None if it was not, or not an integer. """ value = self.getToken(tokenName) if value is not None: try: value = int(value) except ValueError: value = None return value def getTokenFloat(self, tokenName): """ Returns the value of the indicated web token as a floating-point value value, if it was set, or None if it was not, or not a number. """ value = self.getToken(tokenName) if value is not None: try: value = float(value) except ValueError: value = None return value def getTokenBool(self, tokenName): """ Returns the value of the indicated web token as a boolean value, if it was set, or None if it was not. """ value = self.getTokenInt(tokenName) if value is not None: value = bool(value) return value def installPackage(self, packageName, version=None, hostUrl=None): """ Installs the named package, downloading it first if necessary. Returns true on success, false on failure. This method runs synchronously, and will block until it is finished; see the PackageInstaller class if you want this to happen asynchronously instead. """ host = self.getHostWithAlt(hostUrl) if not host.downloadContentsFile(self.http): return False # All right, get the package info now. package = host.getPackage(packageName, version) if not package: self.notify.warning("Package %s %s not known on %s" % (packageName, version, hostUrl)) return False return self.__rInstallPackage(package, []) def __rInstallPackage(self, package, nested): """ The recursive implementation of installPackage(). The new parameter, nested, is a list of packages that we are recursively calling this from, to avoid recursive loops. """ package.checkStatus() if not package.downloadDescFile(self.http): return False # Now that we've downloaded and read the desc file, we can # install all of the required packages first. nested = nested[:] + [self] for packageName, version, host in package.requires: if host.downloadContentsFile(self.http): p2 = host.getPackage(packageName, version) if not p2: self.notify.warning("Couldn't find %s %s on %s" % (packageName, version, host.hostUrl)) else: if p2 not in nested: self.__rInstallPackage(p2, nested) # Now that all of the required packages are installed, carry # on to download and install this package. if not package.downloadPackage(self.http): return False if not package.installPackage(self): return False self.notify.info("Package %s %s installed." % (package.packageName, package.packageVersion)) return True def getHostWithAlt(self, hostUrl): """ Returns a suitable HostInfo object for downloading contents from the indicated URL. This is almost always the same thing as getHost(), except in the rare case when we have an alt_host specified in the HTML tokens; in this case, we may actually want to download the contents from a different URL than the one given, for instance to download a version in testing. """ if hostUrl is None: hostUrl = self.pandaHostUrl altUrl = self.altHostMap.get(hostUrl, None) if altUrl: # We got an alternate host. Use it. return self.getHost(altUrl) # We didn't get an aternate host, use the original. host = self.getHost(hostUrl) # But we might need to consult the host itself to see if *it* # recommends an altHost. if self.altHost: # This means forcing the host to download its contents # file on the spot, a blocking operation. This is a # little unfortunate, but since alt_host is so rarely # used, probably not really a problem. host.downloadContentsFile(self.http) altUrl = host.altHosts.get(self.altHost, None) if altUrl: return self.getHost(altUrl) # No shenanigans, just return the requested host. return host def getHost(self, hostUrl, hostDir=None): """ Returns a new HostInfo object corresponding to the indicated host URL. If we have already seen this URL previously, returns the same object. This returns the literal referenced host. To return the mapped host, which is the one we should actually download from, see getHostWithAlt(). """ if not hostUrl: hostUrl = self.pandaHostUrl host = self.hosts.get(hostUrl, None) if not host: host = HostInfo(hostUrl, appRunner=self, hostDir=hostDir) self.hosts[hostUrl] = host return host def getHostWithDir(self, hostDir): """ Returns the HostInfo object that corresponds to the indicated on-disk host directory. This would be used when reading a host directory from disk, instead of downloading it from a server. Supply the full path to the host directory, as a Filename. Returns None if the contents.xml in the indicated host directory cannot be read or doesn't seem consistent. """ host = HostInfo(None, hostDir=hostDir, appRunner=self) if not host.hasContentsFile: if not host.readContentsFile(): # Couldn't read the contents.xml file return None if not host.hostUrl: # The contents.xml file there didn't seem to indicate the # same host directory. return None host2 = self.hosts.get(host.hostUrl) if host2 is None: # No such host already; store this one. self.hosts[host.hostUrl] = host return host if host2.hostDir != host.hostDir: # Hmm, we already have that host somewhere else. return None # We already have that host, and it's consistent. return host2 def deletePackages(self, packages): """ Removes all of the indicated packages from the disk, uninstalling them and deleting all of their files. The packages parameter must be a list of one or more PackageInfo objects, for instance as returned by getHost().getPackage(). Returns the list of packages that were NOT found. """ for hostUrl, host in self.hosts.items(): packages = host.deletePackages(packages) if not host.packages: # If that's all of the packages for this host, delete # the host directory too. del self.hosts[hostUrl] self.__deleteHostFiles(host) return packages def __deleteHostFiles(self, host): """ Called by deletePackages(), this removes all the files for the indicated host (for which we have presumably already removed all of the packages). """ self.notify.info("Deleting host %s: %s" % (host.hostUrl, host.hostDir)) self.rmtree(host.hostDir) self.sendRequest('forget_package', host.hostUrl, '', '') def freshenFile(self, host, fileSpec, localPathname): """ Ensures that the localPathname is the most current version of the file defined by fileSpec, as offered by host. If not, it downloads a new version on-the-spot. Returns true on success, false on failure. """ assert self.http return host.freshenFile(self.http, fileSpec, localPathname) def scanInstalledPackages(self): """ Scans the hosts and packages already installed locally on the system. Returns a list of InstalledHostData objects, each of which contains a list of InstalledPackageData objects. """ result = [] hostsFilename = Filename(self.rootDir, 'hosts') hostsDir = ScanDirectoryNode(hostsFilename) for dirnode in hostsDir.nested: host = self.getHostWithDir(dirnode.pathname) hostData = InstalledHostData(host, dirnode) if host: for package in host.getAllPackages(includeAllPlatforms=True): packageDir = package.getPackageDir() if not packageDir.exists(): continue subdir = dirnode.extractSubdir(packageDir) if not subdir: # This package, while defined by the host, isn't installed # locally; ignore it. continue packageData = InstalledPackageData(package, subdir) hostData.packages.append(packageData) # Now that we've examined all of the packages for the host, # anything left over is junk. for subdir in dirnode.nested: packageData = InstalledPackageData(None, subdir) hostData.packages.append(packageData) result.append(hostData) return result def readConfigXml(self): """ Reads the config.xml file that may be present in the root directory. """ if not hasattr(core, 'TiXmlDocument'): return filename = Filename(self.rootDir, self.ConfigBasename) doc = core.TiXmlDocument(filename.toOsSpecific()) if not doc.LoadFile(): return xconfig = doc.FirstChildElement('config') if xconfig: maxDiskUsage = xconfig.Attribute('max_disk_usage') try: self.maxDiskUsage = int(maxDiskUsage or '') except ValueError: pass def writeConfigXml(self): """ Rewrites the config.xml to the root directory. This isn't called automatically; an application may call this after adjusting some parameters (such as self.maxDiskUsage). """ from panda3d.core import TiXmlDocument, TiXmlDeclaration, TiXmlElement filename = Filename(self.rootDir, self.ConfigBasename) doc = TiXmlDocument(filename.toOsSpecific()) decl = TiXmlDeclaration("1.0", "utf-8", "") doc.InsertEndChild(decl) xconfig = TiXmlElement('config') xconfig.SetAttribute('max_disk_usage', str(self.maxDiskUsage)) doc.InsertEndChild(xconfig) # Write the file to a temporary filename, then atomically move # it to its actual filename, to avoid race conditions when # updating this file. tfile = Filename.temporary(str(self.rootDir), '.xml') if doc.SaveFile(tfile.toOsSpecific()): tfile.renameTo(filename) def checkDiskUsage(self): """ Checks the total disk space used by all packages, and removes old packages if necessary. """ totalSize = 0 hosts = self.scanInstalledPackages() for hostData in hosts: for packageData in hostData.packages: totalSize += packageData.totalSize self.notify.info("Total Panda3D disk space used: %s MB" % ((totalSize + 524288) // 1048576)) if self.verifyContents == self.P3DVCNever: # We're not allowed to delete anything anyway. return self.notify.info("Configured max usage is: %s MB" % ((self.maxDiskUsage + 524288) // 1048576)) if totalSize <= self.maxDiskUsage: # Still within budget; no need to clean up anything. return # OK, we're over budget. Now we have to remove old packages. usedPackages = [] for hostData in hosts: for packageData in hostData.packages: if packageData.package and packageData.package.installed: # Don't uninstall any packages we're currently using. continue usedPackages.append((packageData.lastUse, packageData)) # Sort the packages into oldest-first order. usedPackages.sort() # Delete packages until we free up enough space. packages = [] for lastUse, packageData in usedPackages: if totalSize <= self.maxDiskUsage: break totalSize -= packageData.totalSize if packageData.package: packages.append(packageData.package) else: # If it's an unknown package, just delete it directly. print("Deleting unknown package %s" % (packageData.pathname)) self.rmtree(packageData.pathname) packages = self.deletePackages(packages) if packages: print("Unable to delete %s packages" % (len(packages))) return def stop(self): """ This method can be called by JavaScript to stop the application. """ # We defer the actual exit for a few frames, so we don't raise # an exception and invalidate the JavaScript call; and also to # help protect against race conditions as the application # shuts down. taskMgr.doMethodLater(0.5, sys.exit, 'exit') def run(self): """ This method calls taskMgr.run(), with an optional exception handler. This is generally the program's main loop when running in a p3d environment (except on unusual platforms like the iPhone, which have to hand the main loop off to the OS, and don't use this interface). """ try: taskMgr.run() except SystemExit as err: # Presumably the window has already been shut down here, but shut # it down again for good measure. if hasattr(builtins, "base"): base.destroy() self.notify.info("Normal exit with status %s." % repr(err.code)) raise except: # Some unexpected Python exception; pass it to the # optional handler, if it is defined. if self.exceptionHandler and not self.interactiveConsole: self.exceptionHandler() else: raise def rmtree(self, filename): """ This is like shutil.rmtree(), but it can remove read-only files on Windows. It receives a Filename, the root directory to delete. """ if filename.isDirectory(): for child in filename.scanDirectory(): self.rmtree(Filename(filename, child)) if not filename.rmdir(): print("could not remove directory %s" % (filename)) else: if not filename.unlink(): print("could not delete %s" % (filename)) def setSessionId(self, sessionId): """ This message should come in at startup. """ self.sessionId = sessionId self.nextScriptId = self.sessionId * 1000 + 10000 def initPackedAppEnvironment(self): """ This function sets up the Python environment suitably for running a packed app. It should only run once in any given session (and it includes logic to ensure this). """ if self.packedAppEnvironmentInitialized: return self.packedAppEnvironmentInitialized = True vfs = VirtualFileSystem.getGlobalPtr() # Now set up Python to import this stuff. VFSImporter.register() sys.path.append(self.multifileRoot) # Make sure that $MAIN_DIR is set to the p3d root before we # start executing the code in this file. ExecutionEnvironment.setEnvironmentVariable( "MAIN_DIR", Filename(self.multifileRoot).toOsSpecific()) # Put our root directory on the model-path, too. getModelPath().appendDirectory(self.multifileRoot) if not self.trueFileIO: # Replace the builtin open and file symbols so user code will get # our versions by default, which can open and read files out of # the multifile. builtins.open = file.open if sys.version_info < (3, 0): builtins.file = file.open builtins.execfile = file.execfile os.listdir = file.listdir os.walk = file.walk os.path.join = file.join os.path.isfile = file.isfile os.path.isdir = file.isdir os.path.exists = file.exists os.path.lexists = file.lexists os.path.getmtime = file.getmtime os.path.getsize = file.getsize sys.modules['glob'] = glob self.checkDiskUsage() def __startIfReady(self): """ Called internally to start the application. """ if self.started: return if self.gotWindow and self.gotP3DFilename: self.started = True # Now we can ignore future calls to startIfReady(). self.ignore('AppRunner_startIfReady') # Hang a hook so we know when the window is actually opened. self.acceptOnce('window-event', self.__windowEvent) # Look for the startup Python file. This might be a magic # filename (like "__main__", or any filename that contains # invalid module characters), so we can't just import it # directly; instead, we go through the low-level importer. # If there's no p3d_info.xml file, we look for "main". moduleName = 'main' if self.p3dPackage: mainName = self.p3dPackage.Attribute('main_module') if mainName: moduleName = mainName # Temporarily set this flag while we import the app, so # that if the app calls run() within its own main.py, it # will properly get ignored by ShowBase. self.initialAppImport = True # Python won't let us import a module named __main__. So, # we have to do that manually, via the VFSImporter. if moduleName == '__main__': dirName = Filename(self.multifileRoot).toOsSpecific() importer = VFSImporter.VFSImporter(dirName) loader = importer.find_module('__main__') if loader is None: raise ImportError('No module named __main__') mainModule = loader.load_module('__main__') else: __import__(moduleName) mainModule = sys.modules[moduleName] # Check if it has a main() function. If so, call it. if hasattr(mainModule, 'main') and hasattr(mainModule.main, '__call__'): mainModule.main(self) # Now clear this flag. self.initialAppImport = False if self.interactiveConsole: # At this point, we have successfully loaded the app. # If the interactive_console flag is enabled, stop the # main loop now and give the user a Python prompt. taskMgr.stop() def getPandaScriptObject(self): """ Called by the browser to query the Panda instance's toplevel scripting object, for querying properties in the Panda instance. The attributes on this object are mapped to document.pluginobject.main within the DOM. """ return self.main def setBrowserScriptObject(self, dom): """ Called by the browser to supply the browser's toplevel DOM object, for controlling the JavaScript and the document in the same page with the Panda3D plugin. """ self.dom = dom # Now evaluate any deferred expressions. for expression in self.deferredEvals: self.scriptRequest('eval', self.dom, value=expression, needsResponse=False) self.deferredEvals = [] def setInstanceInfo(self, rootDir, logDirectory, superMirrorUrl, verifyContents, main, respectPerPlatform): """ Called by the browser to set some global information about the instance. """ # rootDir is the root Panda3D install directory on the local # machine. self.rootDir = Filename.fromOsSpecific(rootDir) # logDirectory is the directory name where all log files end # up. if logDirectory: self.logDirectory = Filename.fromOsSpecific(logDirectory) else: self.logDirectory = Filename(rootDir, 'log') # The "super mirror" URL, generally used only by panda3d.exe. self.superMirrorUrl = superMirrorUrl # How anxious should we be about contacting the server for # the latest code? self.verifyContents = verifyContents # The initial "main" object, if specified. if main is not None: self.main = main self.respectPerPlatform = respectPerPlatform #self.notify.info("respectPerPlatform = %s" % (self.respectPerPlatform)) # Now that we have rootDir, we can read the config file. self.readConfigXml() def addPackageInfo(self, name, platform, version, hostUrl, hostDir=None, recurse=False): """ Called by the browser for each one of the "required" packages that were preloaded before starting the application. If for some reason the package isn't already downloaded, this will download it on the spot. Raises OSError on failure. """ host = self.getHost(hostUrl, hostDir=hostDir) if not host.hasContentsFile: # Always pre-read these hosts' contents.xml files, even if # we have P3DVCForce in effect, since presumably we've # already forced them on the plugin side. host.readContentsFile() if not host.downloadContentsFile(self.http): # Couldn't download? Must have failed to download in the # plugin as well. But since we launched, we probably have # a copy already local; let's use it. message = "Host %s cannot be downloaded, cannot preload %s." % ( hostUrl, name) if not host.hasContentsFile: # This is weird. How did we launch without having # this file at all? raise OSError, message # Just make it a warning and continue. self.notify.warning(message) if name == 'panda3d' and not self.pandaHostUrl: # A special case: in case we don't have the PackageHostUrl # compiled in, infer it from the first package we # installed named "panda3d". self.pandaHostUrl = hostUrl if not platform: platform = None package = host.getPackage(name, version, platform=platform) if not package: if not recurse: # Maybe the contents.xml file isn't current. Re-fetch it. if host.redownloadContentsFile(self.http): return self.addPackageInfo(name, platform, version, hostUrl, hostDir=hostDir, recurse=True) message = "Couldn't find %s %s on %s" % (name, version, hostUrl) raise OSError, message package.checkStatus() if not package.downloadDescFile(self.http): message = "Couldn't get desc file for %s" % (name) raise OSError, message if not package.downloadPackage(self.http): message = "Couldn't download %s" % (name) raise OSError, message if not package.installPackage(self): message = "Couldn't install %s" % (name) raise OSError, message if package.guiApp: self.guiApp = True init_app_for_gui() def setP3DFilename(self, p3dFilename, tokens, argv, instanceId, interactiveConsole, p3dOffset=0, p3dUrl=None): """ Called by the browser to specify the p3d file that contains the application itself, along with the web tokens and/or command-line arguments. Once this method has been called, the application is effectively started. """ # One day we will have support for multiple instances within a # Python session. Against that day, we save the instance ID # for this instance. self.instanceId = instanceId self.tokens = tokens self.argv = argv # We build up a token dictionary with care, so that if a given # token appears twice in the token list, we record only the # first value, not the second or later. This is consistent # with the internal behavior of the core API. self.tokenDict = {} for token, keyword in tokens: self.tokenDict.setdefault(token, keyword) # Also store the arguments on sys, for applications that # aren't instance-ready. sys.argv = argv # That means we now know the altHost in effect. self.altHost = self.tokenDict.get('alt_host', None) # Tell the browser that Python is up and running, and ready to # respond to queries. self.notifyRequest('onpythonload') # Now go load the applet. fname = Filename.fromOsSpecific(p3dFilename) vfs = VirtualFileSystem.getGlobalPtr() if not vfs.exists(fname): raise ArgumentError, "No such file: %s" % (p3dFilename) fname.makeAbsolute() fname.setBinary() mf = Multifile() if p3dOffset == 0: if not mf.openRead(fname): raise ArgumentError, "Not a Panda3D application: %s" % ( p3dFilename) else: if not mf.openRead(fname, p3dOffset): raise ArgumentError, "Not a Panda3D application: %s at offset: %s" % ( p3dFilename, p3dOffset) # Now load the p3dInfo file. self.p3dInfo = None self.p3dPackage = None self.p3dConfig = None self.allowPythonDev = False i = mf.findSubfile('p3d_info.xml') if i >= 0 and hasattr(core, 'readXmlStream'): stream = mf.openReadSubfile(i) self.p3dInfo = core.readXmlStream(stream) mf.closeReadSubfile(stream) if self.p3dInfo: self.p3dPackage = self.p3dInfo.FirstChildElement('package') if self.p3dPackage: self.p3dConfig = self.p3dPackage.FirstChildElement('config') xhost = self.p3dPackage.FirstChildElement('host') while xhost: self.__readHostXml(xhost) xhost = xhost.NextSiblingElement('host') if self.p3dConfig: allowPythonDev = self.p3dConfig.Attribute('allow_python_dev') if allowPythonDev: self.allowPythonDev = int(allowPythonDev) guiApp = self.p3dConfig.Attribute('gui_app') if guiApp: self.guiApp = int(guiApp) trueFileIO = self.p3dConfig.Attribute('true_file_io') if trueFileIO: self.trueFileIO = int(trueFileIO) # The interactiveConsole flag can only be set true if the # application has allow_python_dev set. if not self.allowPythonDev and interactiveConsole: raise StandardError, "Impossible, interactive_console set without allow_python_dev." self.interactiveConsole = interactiveConsole if self.allowPythonDev: # Set the fps text to remind the user that # allow_python_dev is enabled. ConfigVariableString('frame-rate-meter-text-pattern').setValue( 'allow_python_dev %0.1f fps') if self.guiApp: init_app_for_gui() self.initPackedAppEnvironment() # Mount the Multifile under self.multifileRoot. vfs.mount(mf, self.multifileRoot, vfs.MFReadOnly) self.p3dMultifile = mf VFSImporter.reloadSharedPackages() self.loadMultifilePrcFiles(mf, self.multifileRoot) self.gotP3DFilename = True self.p3dFilename = fname if p3dUrl: # The url from which the p3d file was downloaded is # provided if available. It is only for documentation # purposes; the actual p3d file has already been # downloaded to p3dFilename. self.p3dUrl = core.URLSpec(p3dUrl) # Send this call to the main thread; don't call it directly. messenger.send('AppRunner_startIfReady', taskChain='default') def __readHostXml(self, xhost): """ Reads the data in the indicated <host> entry. """ url = xhost.Attribute('url') host = self.getHost(url) host.readHostXml(xhost) # Scan for a matching <alt_host>. If found, it means we # should use the alternate URL instead of the original URL. if self.altHost: xalthost = xhost.FirstChildElement('alt_host') while xalthost: keyword = xalthost.Attribute('keyword') if keyword == self.altHost: origUrl = xhost.Attribute('url') newUrl = xalthost.Attribute('url') self.altHostMap[origUrl] = newUrl break xalthost = xalthost.NextSiblingElement('alt_host') def loadMultifilePrcFiles(self, mf, root): """ Loads any prc files in the root of the indicated Multifile, which is presumed to have been mounted already under root. """ # We have to load these prc files explicitly, since the # ConfigPageManager can't directly look inside the vfs. Use # the Multifile interface to find the prc files, rather than # vfs.scanDirectory(), so we only pick up the files in this # particular multifile. cpMgr = ConfigPageManager.getGlobalPtr() for f in mf.getSubfileNames(): fn = Filename(f) if fn.getDirname() == '' and fn.getExtension() == 'prc': pathname = '%s/%s' % (root, f) alreadyLoaded = False for cpi in range(cpMgr.getNumImplicitPages()): if cpMgr.getImplicitPage(cpi).getName() == pathname: # No need to load this file twice. alreadyLoaded = True break if not alreadyLoaded: data = file.open(Filename(pathname), 'r').read() cp = loadPrcFileData(pathname, data) # Set it to sort value 20, behind the implicit pages. cp.setSort(20) def __clearWindowProperties(self): """ Clears the windowPrc file that was created in a previous call to setupWindow(), if any. """ if self.windowPrc: unloadPrcFile(self.windowPrc) self.windowPrc = None WindowProperties.clearDefault() # However, we keep the self.windowProperties object around, in # case an application wants to return the window to the # browser frame. def setupWindow(self, windowType, x, y, width, height, parent): """ Applies the indicated window parameters to the prc settings, for future windows; or applies them directly to the main window if the window has already been opened. This is called by the browser. """ if self.started and base.win: # If we've already got a window, this must be a # resize/reposition request. wp = WindowProperties() if x or y or windowType == 'embedded': wp.setOrigin(x, y) if width or height: wp.setSize(width, height) if windowType == 'embedded': wp.setParentWindow(parent) wp.setFullscreen(False) base.win.requestProperties(wp) self.windowProperties = wp return # If we haven't got a window already, start 'er up. Apply the # requested setting to the prc file, and to the default # WindowProperties structure. self.__clearWindowProperties() if windowType == 'hidden': data = 'window-type none\n' else: data = 'window-type onscreen\n' wp = WindowProperties.getDefault() wp.clearParentWindow() wp.clearOrigin() wp.clearSize() wp.setFullscreen(False) if windowType == 'fullscreen': wp.setFullscreen(True) if windowType == 'embedded': wp.setParentWindow(parent) if x or y or windowType == 'embedded': wp.setOrigin(x, y) if width or height: wp.setSize(width, height) self.windowProperties = wp self.windowPrc = loadPrcFileData("setupWindow", data) WindowProperties.setDefault(wp) self.gotWindow = True # Send this call to the main thread; don't call it directly. messenger.send('AppRunner_startIfReady', taskChain='default') def setRequestFunc(self, func): """ This method is called by the browser at startup to supply a function that can be used to deliver requests upstream, to the core API, and thereby to the browser. """ self.requestFunc = func def sendRequest(self, request, *args): """ Delivers a request to the browser via self.requestFunc. This low-level function is not intended to be called directly by user code. """ assert self.requestFunc return self.requestFunc(self.instanceId, request, args) def __windowEvent(self, win): """ This method is called when we get a window event. We listen for this to detect when the window has been successfully opened. """ if not self.windowOpened: self.windowOpened = True # Now that the window is open, we don't need to keep those # prc settings around any more. self.__clearWindowProperties() # Inform the plugin and browser. self.notifyRequest('onwindowopen') def notifyRequest(self, message): """ Delivers a notify request to the browser. This is a "this happened" type notification; it also triggers some JavaScript code execution, if indicated in the HTML tags, and may also trigger some internal automatic actions. (For instance, the plugin takes down the splash window when it sees the onwindowopen notification. """ self.sendRequest('notify', message.lower()) def evalScript(self, expression, needsResponse=False): """ Evaluates an arbitrary JavaScript expression in the global DOM space. This may be deferred if necessary if needsResponse is False and self.dom has not yet been assigned. If needsResponse is true, this waits for the value and returns it, which means it cannot be deferred. """ if not self.dom: # Defer the expression. assert not needsResponse self.deferredEvals.append(expression) else: # Evaluate it now. return self.scriptRequest('eval', self.dom, value=expression, needsResponse=needsResponse) def scriptRequest(self, operation, object, propertyName='', value=None, needsResponse=True): """ Issues a new script request to the browser. This queries or modifies one of the browser's DOM properties. This is a low-level method that user code should not call directly; instead, just operate on the Python wrapper objects that shadow the DOM objects, beginning with appRunner.dom. operation may be one of [ 'get_property', 'set_property', 'call', 'evaluate' ]. object is the browser object to manipulate, or the scope in which to evaluate the expression. propertyName is the name of the property to manipulate, if relevant (set to None for the default method name). value is the new value to assign to the property for set_property, or the parameter list for call, or the string expression for evaluate. If needsResponse is true, this method will block until the return value is received from the browser, and then it returns that value. Otherwise, it returns None immediately, without waiting for the browser to process the request. """ uniqueId = self.nextScriptId self.nextScriptId = (self.nextScriptId + 1) % 0xffffffff self.sendRequest('script', operation, object, propertyName, value, needsResponse, uniqueId) if needsResponse: # Now wait for the response to come in. result = self.sendRequest('wait_script_response', uniqueId) return result def dropObject(self, objectId): """ Inform the parent process that we no longer have an interest in the P3D_object corresponding to the indicated objectId. Not intended to be called by user code. """ self.sendRequest('drop_p3dobj', objectId)
frame = wx.Frame(None, title="Injector", size=(640, 400), style=wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX | wx.MINIMIZE_BOX) panel = wx.Panel(frame) button = wx.Button(parent=panel, id=-1, label="Inject", size=(50, 20), pos=(295, 0)) global textbox textbox = wx.TextCtrl(parent=panel, id=-1, pos=(20, 22), size=(600, 340), style=wx.TE_MULTILINE) frame.Bind(wx.EVT_BUTTON, __inject_wx, button) frame.Show() app.SetTopWindow(frame) textbox.AppendText(defaultText) threading.Thread(target=app.MainLoop).start() openInjector_wx() from direct.directnotify.DirectNotifyGlobal import directNotify notify = directNotify.newCategory('ToontownStart') notify.setInfo(True) if __debug__: # The VirtualFileSystem, which has already initialized, doesn't see the mount # directives in the config(s) yet. We have to force it to load those manually: from panda3d.core import VirtualFileSystem, ConfigVariableList, Filename vfs = VirtualFileSystem.getGlobalPtr() mounts = ConfigVariableList('vfs-mount') for mount in mounts: mountfile, mountpoint = (mount.split(' ', 2) + [None, None, None])[:2] vfs.mount(Filename(mountfile), Filename(mountpoint), 0) from otp.settings.Settings import Settings from otp.otpbase import OTPGlobals
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): """ Args: parentId: any distributed object id. zoneId: 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: 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 ToonHead(Actor.Actor): notify = directNotify.newCategory('ToonHead') if metadata.PROCESS == 'client': EyesOpen = 'phase_3/maps/eyes.mat' EyesClosed = 'phase_3/maps/eyesClosed.mat' EyesOpenSad = 'phase_3/maps/eyesSad.mat' EyesClosedSad = 'phase_3/maps/eyesSadClosed.mat' def __init__(self, cr = None): Actor.Actor.__init__(self) self.__eyelashOpened = None self.__eyelashClosed = None self.__eyes = None self.__lpupil = None self.__rpupil = None self.pupils = [] self.eyeTarget = None self.newTarget = False def getPupilDistance(self): """ Returns the average distance of the two pupils from center. """ left, right = self.getPupils() return (left.getPos().length() + right.getPos().length()) / 2.0 def getLeftPupil(self): return self.__lpupil def getRightPupil(self): return self.__rpupil def getPupils(self): return [self.__lpupil, self.__rpupil] def generateHead(self, gender, head, headType, forGui = 0): def stashMuzzles(length, stashNeutral=0): if stashNeutral: if length == "short": self.findAllMatches('**/muzzle-long-neutral;+s').stash() elif length == "long": self.findAllMatches('**/muzzle-short-neutral;+s').stash() else: if length == "short": if self.find('**/muzzle-long-neutral;+s').isStashed(): self.find('**/muzzle-long-neutral;+s').unstash() elif length == "long": if self.find('**/muzzle-short-neutral;+s').isStashed(): self.find('**/muzzle-short-neutral;+s').unstash() self.findAllMatches('**/muzzle-' + length + '-s*;+s').stash() self.findAllMatches('**/muzzle-' + length + '-laugh;+s').stash() self.findAllMatches('**/muzzle-' + length + '-angry;+s').stash() def stashParts(length): for part in self.findAllMatches('**/*' + length + '*;+s'): part.stash() self.gender = gender self.animal = head self.head = headType _modelDetail = "1000" if head != "dog": self.loadModel("phase_3/models/char/%s-heads-%s.bam" % (head, _modelDetail), 'head') else: self.loadModel("phase_3/models/char/tt_a_chr_%s_head_%s.bam" % (headType, _modelDetail), 'head') partAnimations = {} # Load the body part animations. for animName in ToonGlobals.ANIMATIONS: animationData = list(ToonGlobals.ANIMATIONS[animName]) animPath = None if len(animationData) == 2: animPhase = animationData[0] animFile = animationData[1] # Let's create the path for the animation. animPath = ToonGlobals.BASE_MODEL % (animPhase, headType, '', 'head', animFile) if '_-' in animPath: animPath = animPath.replace('_-', '-') if '__' in animPath: animPath = animPath.replace('__', '_') partAnimations[animName] = animPath self.loadAnims(partAnimations, 'head') _pupilL = self.findAllMatches('**/def_left_pupil') _pupilR = self.findAllMatches('**/def_right_pupil') if headType == "1": stashParts("long") stashMuzzles("long", stashNeutral=0) stashMuzzles("short", stashNeutral=1) _pupilL = self.findAllMatches('**/joint_pupilL_short') _pupilR = self.findAllMatches('**/joint_pupilR_short') elif headType == "2": if head == "mouse": stashParts("short") stashMuzzles("short", stashNeutral=1) stashMuzzles("long", stashNeutral=0) _pupilL = self.findAllMatches('**/joint_pupilL_long') _pupilR = self.findAllMatches('**/joint_pupilR_long') else: stashParts("long") stashMuzzles("short", stashNeutral=0) stashMuzzles("long", stashNeutral=1) _pupilL = self.findAllMatches('**/joint_pupilL_short') _pupilR = self.findAllMatches('**/joint_pupilR_short') if head == "rabbit": self.findAllMatches('**/head-long').unstash() self.findAllMatches('**/head-front-long').unstash() #self.findAllMatches('**/head-front-short').stash() #self.findAllMatches('**/head-short').stash() elif headType == "3": stashParts("short") stashMuzzles("long", stashNeutral=0) stashMuzzles("short", stashNeutral=1) _pupilL = self.findAllMatches('**/joint_pupilL_long') _pupilR = self.findAllMatches('**/joint_pupilR_long') if head == "rabbit": self.findAllMatches('**/head-long').stash() self.findAllMatches('**/head-front-long').stash() self.findAllMatches('**/head-front-short').unstash() self.findAllMatches('**/head-short').unstash() elif headType == "4": stashParts("short") stashMuzzles("short", stashNeutral=0) stashMuzzles("long", stashNeutral=1) _pupilL = self.findAllMatches('**/joint_pupilL_long') _pupilR = self.findAllMatches('**/joint_pupilR_long') self.pupils.append(_pupilL) self.pupils.append(_pupilR) self.fixEyes() if self.gender == "girl": self.setupEyelashes() if not forGui: taskMgr.add(self.__eyesLookTask, "toonHeadEyesLook-" + str(id(self))) return def hasPupils(self): return CIGlobals.isNodePathOk(self.__lpupil) and CIGlobals.isNodePathOk(self.__rpupil) def setEyeTarget(self, target): self.eyeTarget = target self.newTarget = True def hasEyeTarget(self): if isinstance(self.eyeTarget, NodePath): return CIGlobals.isNodePathOk(self.eyeTarget) return self.eyeTarget is not None def setEyeState(self, state): if state == ToonGlobals.EyeStateClosed: self.closeEyes() elif state == ToonGlobals.EyeStateOpened: self.openEyes() def lookEyesAt(self, pos): self.setEyeTarget(Point3(*pos)) def lookEyesAtObject(self, doId): if not hasattr(base, 'cr'): return do = base.cr.doId2do.get(doId) if do and isinstance(do, NodePath): if (hasattr(base, 'localAvatar') and do.doId == base.localAvatar.doId and base.localAvatar.isFirstPerson()): self.setEyeTarget(camera) else: self.setEyeTarget(do) def lookEyesAtTarget(self, eyeTarget): if isinstance(eyeTarget, NodePath): if hasattr(eyeTarget, 'getHeight'): self.lookPupilsAt(eyeTarget, Point3(0, 0, eyeTarget.getHeight() * 0.85)) else: self.lookPupilsAt(eyeTarget, Point3(0)) else: self.lookPupilsAt(None, eyeTarget) def __eyesLookTask(self, task): if (hasattr(base, 'localAvatar') and self == base.localAvatar and base.localAvatar.isFirstPerson()) or not self.hasPupils(): return task.cont if self.hasEyeTarget(): if self.newTarget or isinstance(self.eyeTarget, NodePath): self.lookEyesAtTarget(self.eyeTarget) self.newTarget = False return task.cont def fixEyes(self): mode = -3 self.drawInFront("eyes*", "head-front*", mode) if not self.find('**/joint_pupil*').isEmpty(): self.drawInFront('joint_pupil*', 'eyes*', -1) else: self.drawInFront('def_*_pupil', 'eyes*', -1) self.__eyes = self.find('**/eyes*') if not self.__eyes.isEmpty(): self.__eyes.setColorOff() self.__lpupil = None self.__rpupil = None lp = self.pupils[0] rp = self.pupils[1] # ModelNodes don't get flattened leye = self.__eyes.attachNewNode(ModelNode('leye')) reye = self.__eyes.attachNewNode(ModelNode('reye')) lmat = Mat4(0.802174, 0.59709, 0, 0, -0.586191, 0.787531, 0.190197, 0, 0.113565, -0.152571, 0.981746, 0, -0.233634, 0.418062, 0.0196875, 1) leye.setMat(lmat) rmat = Mat4(0.786788, -0.617224, 0, 0, 0.602836, 0.768447, 0.214658, 0, -0.132492, -0.16889, 0.976689, 0, 0.233634, 0.418062, 0.0196875, 1) reye.setMat(rmat) self.__lpupil = leye.attachNewNode('lpupil') self.__rpupil = reye.attachNewNode('rpupil') lpt = self.__eyes.attachNewNode('') rpt = self.__eyes.attachNewNode('') lpt.wrtReparentTo(self.__lpupil) rpt.wrtReparentTo(self.__rpupil) lp.reparentTo(lpt) rp.reparentTo(rpt) self.__lpupil.adjustAllPriorities(1) self.__rpupil.adjustAllPriorities(1) if self.animal != 'dog': self.__lpupil.flattenStrong() self.__rpupil.flattenStrong() # Make sure pupils don't clip through the head or eyes self.__lpupil.setDepthOffset(4, 1) self.__rpupil.setDepthOffset(4, 1) def setPupilDirection(self, x, y): left = Vec3() right = Vec3() CIOLib.setPupilDirection(x, y, left, right) self.__lpupil.setPos(left) self.__rpupil.setPos(right) def lookPupilsAt(self, node, point): if not node: node = NodePath() xy = CIOLib.lookPupilsAt(node, point, self.__eyes) self.setPupilDirection(xy[0], xy[1]) def lookPupilsMiddle(self): self.__lpupil.setPos(0, 0, 0) self.__rpupil.setPos(0, 0, 0) def setupEyelashes(self): head = self.getPart('head') lashes = loader.loadModel("phase_3/models/char/%s-lashes.bam" % self.animal) openString = "open-short" closedString = "closed-short" if self.head == "mouse": if self.head == "1": openString = "open-short" closedString = "closed-short" elif self.head == "2": openString = "open-long" closedString = "closed-long" else: if self.head == "1": openString = "open-short" closedString = "closed-short" elif self.head == "2": openString = "open-short" closedString = "closed-short" elif self.head == "3": openString = "open-long" closedString = "closed-long" elif self.head == "4": openString = "open-long" closedString = "closed-long" self.__eyelashOpened = lashes.find('**/' + openString).copyTo(head) self.__eyelashClosed = lashes.find('**/' + closedString).copyTo(head) self.__eyelashClosed.hide() def closeEyes(self): if self.gender == "girl": self.__eyelashOpened.hide() self.__eyelashClosed.show() for pupil in self.pupils: pupil.hide() if hasattr(self, 'getHealth'): if self.getHealth() > 1: try: self.findAllMatches('**/eyes*').setBSPMaterial(self.EyesClosed, 1) except: pass else: try: self.findAllMatches('**/eyes*').setBSPMaterial(self.EyesClosedSad, 1) except: pass else: try: self.findAllMatches('**/eyes*').setBSPMaterial(self.EyesClosed, 1) except: pass def openEyes(self): if self.gender == "girl": self.__eyelashOpened.show() self.__eyelashClosed.hide() for pupil in self.pupils: pupil.show() if hasattr(self, 'getHealth'): if self.getHealth() > 0: try: self.findAllMatches('**/eyes*').setBSPMaterial(self.EyesOpen, 1) except: pass else: try: self.findAllMatches('**/eyes*').setBSPMaterial(self.EyesOpenSad, 1) except: pass else: try: self.findAllMatches('**/eyes*').setBSPMaterial(self.EyesOpen, 1) except: pass def startLookAround(self): pass def getEyes(self): return self.find("**/eyes*") def lerpLookAtNode(self, node): head = self.getPart("head") startHpr = head.getHpr() head.lookAt(node, Point3(0, 0, -2)) endHpr = head.getHpr() head.setHpr(startHpr) self.lerpLookAt(head, endHpr) def lerpLookAt(self, head, hpr, time = 1.0): self.lookAtTrack = Parallel(Sequence(LerpHprInterval(head, time, hpr, blendType='easeInOut'))) self.lookAtTrack.start() return 1 def setHeadColor(self, color = None): if color is None: color = self.headcolor self.findAllMatches('**/head*').setColor(color) if (self.animal == "rabbit" or self.animal == "cat" or self.animal == "bear" or self.animal == "pig" or self.animal == "mouse"): self.findAllMatches('**/ears*').setColor(color) def delete(self): try: self.ToonHead_deleted except: self.ToonHead_deleted = 1 taskMgr.remove("toonHeadEyesLook-" + str(id(self))) if self.__lpupil: self.__lpupil.removeNode() self.__lpupil = None if self.__rpupil: self.__rpupil.removeNode() self.__rpupil = None if self.__eyes: self.__eyes.removeNode() self.__eyes = None if self.__eyelashClosed: self.__eyelashClosed.removeNode() self.__eyelashClosed = None if self.__eyelashOpened: self.__eyelashOpened.removeNode() self.__eyelashOpened = None for pupilC in self.pupils: if not pupilC.isEmpty(): for pupil in pupilC: pupil.removeNode() self.pupils = None self.eyeTarget = None self.newTarget = None self.gender = None self.animal = None self.head = None
class DistributedShipBroadside(DistributedWeapon, DistributedShippart): notify = directNotify.newCategory('DistributedShipBroadside') localFireSfx = [] distFireSfx = [] def __init__(self, cr): DistributedWeapon.__init__(self, cr) DistributedShippart.__init__(self, cr) self.baseVel = Vec3(0) self.setPos(0, 0, 0) self.ball = None self.baseTeam = 0 self.prop = None self.av = None self.ammoType = 0 self.secondaryAmmoType = 0 self.secondaryAmount = 0.0 self.shotNum = 0 self.leftBroadside = [] self.rightBroadside = [] self.leftBroadsideConfig = [] self.rightBroadsideConfig = [] self.rightPlayShots = [] self.leftPlayShots = [] self.collisionLists = {} self.listening = False if not self.localFireSfx: for file in localFireSfxNames: self.localFireSfx.append(file) if not self.distFireSfx: for file in distFireSfxNames: self.distFireSfx.append(file) self.aimAITrack = None if __dev__ and config.GetBool('want-broadside-assist', 0): self.tracker = loader.loadModel('models/effects/explosion_sphere') self.tracker.reparentTo(render) self.tracker.setScale(30) return def disable(self): self.ignoreAll() self.av = None self.effectNode.removeNode() self.effectNode = None taskMgr.remove(self.getTrailTaskName()) if self.aimAITrack: self.aimAITrack.pause() self.aimAITrack = None DistributedWeapon.disable(self) DistributedShippart.disable(self) return def generate(self): DistributedWeapon.generate(self) DistributedShippart.generate(self) def announceGenerate(self): self.effectNode = NodePath( ModelNode('broadside-%d-effects' % self.doId)) self.effectNode.reparentTo(self) DistributedWeapon.announceGenerate(self) DistributedShippart.announceGenerate(self) def delete(self): for i in self.leftPlayShots: i.pause() del self.leftPlayShots for i in self.rightPlayShots: i.pause() del self.rightPlayShots del self.rightBroadside del self.leftBroadside del self.rightBroadsideConfig del self.leftBroadsideConfig self.ignoreAll() self.removeNode() if self.ship.broadside: self.ship.broadside[1] = None DistributedWeapon.delete(self) DistributedShippart.delete(self) return def load(self): bs = self.ship.broadside if bs: if bs[0]: self.leftBroadside = bs[0][0] self.rightBroadside = bs[0][1] self.ship.broadside[1] = self return self.loadModel() self.addPropToShip() self.ship.broadside = [[self.leftBroadside, self.rightBroadside], self] def loadModel(self): for i in range(len(self.leftBroadsideConfig)): if self.leftBroadsideConfig[i] > 0: cannon = CannonPort.CannonPort(self.leftBroadsideConfig[i], 0, i) self.leftBroadside.append(cannon) else: self.leftBroadside.append(None) for i in range(len(self.rightBroadsideConfig)): if self.rightBroadsideConfig[i] > 0: cannon = CannonPort.CannonPort(self.rightBroadsideConfig[i], 1, i) self.rightBroadside.append(cannon) else: self.rightBroadside.append(None) return def fireBroadside(self, side, targetId=0, aimCode=0): zoneId = 0 target = self.cr.doId2do.get(targetId) if target: if isinstance(target, DistributedSimpleShip): targetPos = target.getTransNode().getPos(render) else: targetPos = target.getPos(render) zoneId = self.cr.activeWorld.worldGrid.getZoneFromXYZ(targetPos) zonePos = self.cr.activeWorld.worldGrid.getZoneCellOrigin(zoneId) shipPos = self.ship.getPos(render) cannonList = [] if side == 0: if self.leftBroadside and self.leftBroadsideConfig: cannonList = self.leftBroadside elif side == 1: if self.rightBroadside and self.rightBroadsideConfig: cannonList = self.rightBroadside spread = 1 flightTime = 0 if target and self.ship: dist = self.ship.getDistance(target) spread = max(0.5, dist / 1000.0) flightTime = dist / CannonGlobals.CLIENT_BROADSIDE_FIRE_VELOCITY broadsideMaxDelay = ShipGlobals.getBroadsideMaxDelay( self.ship.modelClass) targetShipVel = 0 if target: fvel = target.smoother.getSmoothForwardVelocity() faxis = target.smoother.getForwardAxis() targetShipVel = faxis * fvel * (flightTime + broadsideMaxDelay / 2.0) if targetShipVel: targetPos = Vec3(targetPos[0] + targetShipVel[0], targetPos[1] + targetShipVel[1], targetPos[2] + targetShipVel[2]) if __dev__ and config.GetBool('want-broadside-assist', 0): self.tracker.setPos(render, targetPos) delays = [] hitPosList = [] if side == 0: for i in range(len(self.leftBroadsideConfig)): if cannonList[i]: delays.append(random.uniform(0, broadsideMaxDelay)) if target: randX = random.gauss(0.0, 5.0 * spread) randY = random.gauss(0.0, 5.0 * spread) randZ = random.gauss(2.0, 3.0 * spread) cannonPos = cannonList[i].locator.getPos(render) diffX = shipPos[0] - cannonPos[0] diffY = shipPos[1] - cannonPos[1] hitPosList.append( (targetPos[0] - zonePos[0] + randX - diffX, targetPos[1] - zonePos[1] + randY - diffY, targetPos[2] - zonePos[2] + randZ)) else: hitPosList.append((100, 100, 100)) else: delays.append(0) hitPosList.append((100, 100, 100)) elif side == 1: for i in range(len(self.rightBroadsideConfig)): if cannonList[i]: delays.append(random.uniform(0, broadsideMaxDelay)) if target: randX = random.gauss(0.0, 5.0 * spread) randY = random.gauss(0.0, 5.0 * spread) randZ = random.gauss(2.0, 3.0 * spread) cannonPos = cannonList[i].locator.getPos(render) diffX = shipPos[0] - cannonPos[0] diffY = shipPos[1] - cannonPos[1] hitPosList.append( (targetPos[0] - zonePos[0] + randX - diffX, targetPos[1] - zonePos[1] + randY - diffY, targetPos[2] - zonePos[2] + randZ)) else: hitPosList.append((100, 100, 100)) else: delays.append(0) hitPosList.append((100, 100, 100)) delays[0] = 0.0 if len(delays) > 4: delays[1] = 0.0 random.shuffle(delays) self.doBroadside(side, delays, hitPosList, zoneId, flightTime, clientFire=1) self.sendUpdate('requestBroadside', [side, delays, hitPosList, zoneId, flightTime]) def doBroadside(self, side, delays, hitPosList, zoneId, flightTime, timestamp=None, clientFire=0): if not clientFire: if self.localAvatarUsingWeapon: return if not (self.cr.activeWorld and self.cr.activeWorld.worldGrid): return if not self.ship.showCannonsFiring(broadsides=True): return if timestamp == None: ts = 0.0 else: ts = globalClockDelta.localElapsedTime(timestamp) zonePos = self.cr.activeWorld.worldGrid.getZoneCellOrigin(zoneId) if side == 0: for i in self.leftPlayShots: i.pause() self.leftPlayShots = [] for index in range(len(self.leftBroadside)): if self.leftBroadside[index]: if random.random() < self.secondaryAmount: fireSecondary = True else: fireSecondary = False targetPos = 0 if hitPosList[index] != (100, 100, 100): tPos = hitPosList[index] targetPos = Vec3(tPos[0] + zonePos[0], tPos[1] + zonePos[1], tPos[2] + zonePos[2]) if __dev__ and config.GetBool( 'want-broadside-assistAI', 0): self.tracker.setPos(render, targetPos) playShot = Sequence( Wait(delays[index]), Func(self.setCannonAnim, index, 0, 0), Wait(0.5), Func(self.__requestAttack, index, side, targetPos, flightTime, fireSecondary=fireSecondary), Wait(3.0), Func(self.setCannonAnim, index, 0, 1)) playShot.start(ts) self.leftPlayShots.append(playShot) elif side == 1: for i in self.rightPlayShots: i.pause() self.rightPlayShots = [] for index in range(len(self.rightBroadside)): if self.rightBroadside[index]: if random.random() < self.secondaryAmount: fireSecondary = True else: fireSecondary = False targetPos = 0 if hitPosList[index] != (100, 100, 100): tPos = hitPosList[index] targetPos = Vec3(tPos[0] + zonePos[0], tPos[1] + zonePos[1], tPos[2] + zonePos[2]) if __dev__ and config.GetBool( 'want-broadside-assistAI', 0): self.tracker.setPos(render, targetPos) playShot = Sequence( Wait(delays[index]), Func(self.setCannonAnim, index, 1, 0), Wait(0.5), Func(self.__requestAttack, index, side, targetPos, flightTime, fireSecondary=fireSecondary), Wait(3.0), Func(self.setCannonAnim, index, 1, 1)) playShot.start(ts) self.rightPlayShots.append(playShot) return def __requestAttack(self, index, side, targetPos, flightTime, fireSecondary=False): if side == 0: if self.leftBroadside and self.leftBroadsideConfig: cannonList = self.leftBroadside cannonConfig = self.leftBroadsideConfig skillId = EnemySkills.LEFT_BROADSIDE elif side == 1: if self.rightBroadside and self.rightBroadsideConfig: cannonList = self.rightBroadside cannonConfig = self.rightBroadsideConfig skillId = EnemySkills.RIGHT_BROADSIDE ammoSkillId = self.ammoType if fireSecondary: ammoSkillId = self.secondaryAmmoType if cannonList and cannonConfig: cannonList[index].playFire() cballSpawnPoint = cannonList[index].locator cballSpawnPoint.setP(render, 0) self.playFireEffect(cballSpawnPoint, ammoSkillId) if not WeaponGlobals.isProjectileSkill(skillId, ammoSkillId): return pos = cballSpawnPoint.getPos(render) + Vec3(0, -25, 0) if targetPos: self.playAttack(skillId, ammoSkillId, pos, targetPos=targetPos, flightTime=flightTime) else: m = cballSpawnPoint.getMat(self) power = WeaponGlobals.getAttackProjectilePower( ammoSkillId) * CannonGlobals.BROADSIDE_POWERMOD if side == 1: startVel = m.xformVec(Vec3(0, -power, 90)) else: startVel = m.xformVec(Vec3(0, -power, 90)) self.playAttack(skillId, ammoSkillId, pos, startVel) def getProjectile(self, skillId, projectileHitEvent, buffs): cannonball = CannonballProjectile(self.cr, skillId, projectileHitEvent, buffs) cannonball.reparentTo(render) return cannonball def playAttack(self, skillId, ammoSkillId, pos=0, startVel=0, targetPos=None, flightTime=0): if not WeaponGlobals.isProjectileSkill(skillId, ammoSkillId): if self.localAvatarUsingWeapon: localAvatar.composeRequestTargetedSkill(skillId, ammoSkillId) return self.ammoSequence = self.ammoSequence + 1 & 255 buffs = [] if self.av: buffs = self.av.getSkillEffects() ammo = self.getProjectile(ammoSkillId, self.projectileHitEvent, buffs) ammo.setPos(pos) if skillId == EnemySkills.LEFT_BROADSIDE: ammo.setH(render, self.ship.getH(render) + 90) elif skillId == EnemySkills.RIGHT_BROADSIDE: ammo.setH(render, self.ship.getH(render) - 90) collNode = ammo.getCollNode() collNode.reparentTo(render) ammo.setTag('ammoSequence', str(self.ammoSequence)) ammo.setTag('skillId', str(int(skillId))) ammo.setTag('ammoSkillId', str(int(ammoSkillId))) if self.av: ammo.setTag('attackerId', str(self.av.doId)) startPos = pos endPlaneZ = -10 if self.ship: ammo.setTag('shipId', str(self.ship.doId)) self.shotNum += 1 if self.shotNum > 100000: self.shotNum = 0 ammo.setTag('shotNum', str(self.shotNum)) self.baseVel = self.ship.worldVelocity if startPos[2] < endPlaneZ: self.notify.warning('bad startPos Z: %s' % startPos[2]) return if targetPos is None: startVel += self.baseVel pi = ProjectileInterval(ammo, startPos=startPos, startVel=startVel, endZ=endPlaneZ, gravityMult=2.0, collNode=collNode) else: if self.ship.getNPCship(): pi = ProjectileInterval(ammo, endZ=endPlaneZ, startPos=startPos, wayPoint=targetPos, timeToWayPoint=flightTime, gravityMult=0.5, collNode=collNode) else: pi = ProjectileInterval(ammo, endZ=endPlaneZ, startPos=startPos, wayPoint=targetPos, timeToWayPoint=flightTime, gravityMult=1.2, collNode=collNode) if base.cr.cannonballCollisionDebug == 1 or base.cr.cannonballCollisionDebug == 2: addFunc = Func(base.cTrav.addCollider, collNode, ammo.collHandler) delFunc = Func(base.cTrav.removeCollider, collNode) addFunc = Wait(0) delFunc = Wait(0) s = Parallel(Sequence(Wait(0.1), addFunc), Sequence(pi, Func(ammo.destroy), delFunc)) ammo.setIval(s, start=True) return def playFireEffect(self, spawnNode, ammoSkillId): if self.localAvatarUsingWeapon: boomSfx = random.choice(self.localFireSfx) else: boomSfx = random.choice(self.distFireSfx) base.playSfx(boomSfx, node=spawnNode, cutoff=3000) scaleMult = 0.75 + random.random() if base.options.getSpecialEffectsSetting( ) >= base.options.SpecialEffectsMedium: muzzleFlameEffect = MuzzleFlame.getEffect() if muzzleFlameEffect: muzzleFlameEffect.reparentTo(self.ship.getTransNode()) muzzleFlameEffect.setHpr(spawnNode, 0, 90, 0) muzzleFlameEffect.setPos(spawnNode, 0, -6, 0) muzzleFlameEffect.setScale(2.0 * scaleMult) muzzleFlameEffect.play() if base.options.getSpecialEffectsSetting( ) == base.options.SpecialEffectsMedium: cannonSmokeEffect = CannonSmokeSimple.getEffect() if cannonSmokeEffect: cannonSmokeEffect.reparentTo(self.ship.getTransNode()) cannonSmokeEffect.effectModel.unstash() cannonSmokeEffect.setHpr(spawnNode, 0, 90, 0) cannonSmokeEffect.setPos(spawnNode, 0, -15, 0) cannonSmokeEffect.setScale(2.2 * scaleMult) cannonSmokeEffect.play() if base.options.getSpecialEffectsSetting( ) >= base.options.SpecialEffectsHigh: cannonSmokeEffect = CannonBlastSmoke.getEffect() if cannonSmokeEffect: cannonSmokeEffect.reparentTo(self.ship.getTransNode()) cannonSmokeEffect.particleDummy.reparentTo( self.ship.getTransNode()) cannonSmokeEffect.setPosHpr(spawnNode, 0, -3, 0, 180, 0, 0) cannonSmokeEffect.particleDummy.setHpr(spawnNode, 180, 0, 0) cannonSmokeEffect.setEffectScale(1.8 * scaleMult) cannonSmokeEffect.play() if base.options.getSpecialEffectsSetting( ) >= base.options.SpecialEffectsLow: if ammoSkillId == InventoryType.CannonGrapeShot: effect = GrapeshotEffect.getEffect() if effect: effect.reparentTo(self.ship.getTransNode()) effect.setPosHpr(spawnNode, 0, 0, 0, -90, 0, 0) effect.time = 2.0 effect.play() def getTrailTaskName(self): return self.uniqueName('cannonTrail') def b_setCannonAnim(self, index, side, anim, callback=None, extraArgs=[]): self.setCannonAnim(index, side, anim, None, callback, extraArgs) self.d_setCannonAnim(index, side, anim) return def d_setCannonAnim(self, index, side, anim): timestamp = globalClockDelta.getFrameNetworkTime() self.sendUpdate('setCannonAnim', [index, side, anim, timestamp]) def setCannonAnim(self, index, side, anim, timestamp=None, callback=None, extraArgs=[]): if timestamp == None: ts = 0.0 else: ts = globalClockDelta.localElapsedTime(timestamp) if side == 0: if anim == 0: self.leftBroadside[index].playOpen(ts) elif anim == 1: self.leftBroadside[index].playClosed(ts) elif side == 1: if anim == 0: self.rightBroadside[index].playOpen(ts) elif anim == 1: self.rightBroadside[index].playClosed(ts) return def setLeftBroadside(self, val): self.leftBroadsideConfig = val def setRightBroadside(self, val): self.rightBroadsideConfig = val def setBaseTeam(self, val): self.baseTeam = val def setAmmoType(self, val): self.ammoType = val def setSecondaryAmmoType(self, val): self.secondaryAmmoType = val def setSecondaryAmount(self, val): self.secondaryAmount = val def setZoneLevel(self, level): pass def loadZoneLevel(self, level): pass def unloadZoneLevel(self, level): pass def addPropToShip(self): self.effectNode.reparentTo(self.ship.getTransNode()) def completeCannonCheck(self): for colList in self.collisionLists.values(): colList.sort() ammo = colList[0][1].getFromNodePath().getPythonTag('ammo') if not ammo or ammo.destroyed: continue for entryData in colList: DistributedWeapon.projectileHitObject(self, entryData[1]) if ammo.destroyed: break self.collisionLists = {} self.listening = False def projectileHitObject(self, entry): shot = int(entry.getFromNodePath().getNetTag('shotNum')) if not self.collisionLists.get(shot): self.collisionLists[shot] = [] y = entry.getSurfacePoint(entry.getIntoNodePath())[1] self.collisionLists[shot].append((y, entry)) if not self.listening: self.listening = True self.acceptOnce('event-loop-done', self.completeCannonCheck)
from pandac.PandaModules import getConfigShowbase from direct.directnotify.DirectNotifyGlobal import directNotify from direct.showbase.PythonUtil import fastRepr from exceptions import Exception import sys import types import traceback notify = directNotify.newCategory("ExceptionVarDump") config = getConfigShowbase() 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 TTHoodAI(HoodAI): notify = directNotify.newCategory('TTHoodAI') def __init__(self, air): HoodAI.__init__(self, air, ToontownGlobals.ToontownCentral) self.fishingSpots = {} self.doors = {} def createObjects(self): HoodAI.createObjects(self) if simbase.config.GetBool('want-fishing-spots', False): self.createFishingSpots() if simbase.config.GetBool('want-doors', False): self.createDoors() def createClassicChars(self): self.classicChar = DistributedMickeyAI(self.air) self.classicChar.generateWithRequired(self.zoneId) def createFishingSpots(self): self.fishingSpots[0] = DistributedFishingSpotAI(self.air) self.fishingSpots[0].setPosHpr(-77.5097, 47.3772, -3.2852, 176.818, 0, 0) self.fishingSpots[0].generateWithRequired(self.zoneId) self.fishingSpots[1] = DistributedFishingSpotAI(self.air) self.fishingSpots[1].setPosHpr(-90.7111, 43.1895, -3.30975, -133.237, 0, 0) self.fishingSpots[1].generateWithRequired(self.zoneId) self.fishingSpots[2] = DistributedFishingSpotAI(self.air) self.fishingSpots[2].setPosHpr(-83.6628, -42.4479, -3.95932, -29.2345, 0, 0) self.fishingSpots[2].generateWithRequired(self.zoneId) def createDoors(self): blockNumber = 20 self.interiorZone = self.zoneId - self.zoneId % 100 + 500 + blockNumber self.interior = DistributedHQInteriorAI(self.air, self.interiorZone, blockNumber) self.interior.generateWithRequired(self.interiorZone) self.doors[0] = DistributedDoorAI(self.air, self.interior.blockNumber, DoorTypes.EXT_HQ, doorIndex=0) self.doors[0].generateWithRequired(self.zoneId) self.doors[1] = DistributedDoorAI(self.air, self.interior.blockNumber, DoorTypes.EXT_HQ, doorIndex=1) self.doors[1].generateWithRequired(self.zoneId) self.doors[2] = DistributedDoorAI(self.air, self.interior.blockNumber, DoorTypes.INT_HQ, doorIndex=0) self.doors[2].generateWithRequired(self.interiorZone) self.doors[3] = DistributedDoorAI(self.air, self.interior.blockNumber, DoorTypes.INT_HQ, doorIndex=1) self.doors[3].generateWithRequired(self.interiorZone) self.doors[0].setOtherDoor(self.doors[2]) self.doors[1].setOtherDoor(self.doors[3]) self.doors[2].setOtherDoor(self.doors[0]) self.doors[3].setOtherDoor(self.doors[1])
""" CInterval-extensions module: contains methods to extend functionality of the CInterval class """ from direct.directnotify.DirectNotifyGlobal import directNotify notify = directNotify.newCategory("Interval") def setT(self, t): # Overridden from the C++ function to call privPostEvent # afterward. We do this by renaming the C++ function in # FFIRename. self.__cSetT(t) self.privPostEvent() 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) def stop(self): self.notify.error("using deprecated CInterval.stop() interface") self.finish() def setFinalT(self): self.notify.error("using deprecated CInterval.setFinalT() interface") self.finish()
class DistributedObjectAI(DistributedObjectBase): notify = directNotify.newCategory("DistributedObjectAI") QuietZone = 1 def __init__(self, air): try: self.DistributedObjectAI_initialized except: self.DistributedObjectAI_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._DOAI_requestedDelete = False # These are used to implement beginBarrier(). self.__nextBarrierContext = 0 self.__barriers = {} self.__generated = False # reference count for multiple inheritance self.__generates = 0 self._zoneData = None # 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 or deleted """ spaces = ' ' * (indent + 2) try: print("%s%s:" % (' ' * indent, self.__class__.__name__)) flags = [] if self.__generated: flags.append("generated") if self.air == None: flags.append("deleted") flagStr = "" if len(flags): flagStr = " (%s)" % (" ".join(flags)) print("%sfrom DistributedObject doId:%s, parent:%s, zone:%s%s" % ( spaces, self.doId, self.parentId, self.zoneId, flagStr)) except Exception as e: print("%serror printing status %s" % (spaces, e)) def getDeleteEvent(self): # this is sent just before we get deleted if hasattr(self, 'doId'): return 'distObjDelete-%s' % self.doId return None def sendDeleteEvent(self): # this is called just before we get deleted delEvent = self.getDeleteEvent() if delEvent: messenger.send(delEvent) def getCacheable(self): """ This method exists only to mirror the similar method on DistributedObject. AI objects aren't cacheable. """ return False def deleteOrDelay(self): """ This method exists only to mirror the similar method on DistributedObject. AI objects don't have delayDelete, they just get deleted immediately. """ self.delete() def getDelayDeleteCount(self): return 0 def delete(self): """ Inheritors should redefine this to take appropriate action on delete Note that this may be called multiple times if a class inherits from DistributedObjectAI more than once. """ self.__generates -= 1 if self.__generates < 0: self.notify.debug('DistributedObjectAI: delete() called more times than generate()') if self.__generates == 0: # prevent this code from executing multiple times if self.air is not None: # self.doId may not exist. The __dict__ syntax works around that. assert self.notify.debug('delete(): %s' % (self.__dict__.get("doId"))) if not self._DOAI_requestedDelete: # this logs every delete that was not requested by us. # TODO: this currently prints warnings for deletes of objects # that we did not create. We need to add a 'locally created' # flag to every object to filter these out. """ DistributedObjectAI.notify.warning( 'delete() called but requestDelete never called for %s: %s' % (self.__dict__.get('doId'), self.__class__.__name__)) """ """ # print a stack trace so we can detect whether this is the # result of a network msg. # this is slow. from direct.showbase.PythonUtil import StackTrace DistributedObjectAI.notify.warning( 'stack trace: %s' % StackTrace()) """ self._DOAI_requestedDelete = False self.releaseZoneData() # Clean up all the pending barriers. for barrier in self.__barriers.values(): barrier.cleanup() self.__barriers = {} # DCR: I've re-enabled this block of code so that Toontown's # AI won't leak channels. # Let me know if it causes trouble. ### Asad: As per Roger's suggestion, turn off the following ### block until a solution is thought out of how to prevent ### this delete message or to handle this message better # TODO: do we still need this check? if not getattr(self, "doNotDeallocateChannel", False): if self.air: self.air.deallocateChannel(self.doId) self.air = None self.parentId = None self.zoneId = None self.__generated = False def isDeleted(self): """ Returns true if the object has been deleted, or if it is brand new and hasnt yet been generated. """ return self.air == None def isGenerated(self): """ Returns true if the object has been generated """ return self.__generated def getDoId(self): """ Return the distributed object id """ return self.doId def preAllocateDoId(self): """ objects that need to have a doId before they are generated can call this to pre-allocate a doId for the object """ assert not self.__preallocDoId self.doId = self.air.allocateChannel() self.__preallocDoId = 1 def announceGenerate(self): """ Called after the object has been generated and all of its required fields filled in. Overwrite when needed. """ pass def b_setLocation(self, parentId, zoneId): self.d_setLocation(parentId, zoneId) self.setLocation(parentId, zoneId) def d_setLocation(self, parentId, zoneId): self.air.sendSetLocation(self, parentId, zoneId) def setLocation(self, parentId, zoneId): # Prevent Duplicate SetLocations for being Called if (self.parentId == parentId) and (self.zoneId == zoneId): return oldParentId = self.parentId oldZoneId = self.zoneId self.air.storeObjectLocation(self, parentId, zoneId) if ((oldParentId != parentId) or (oldZoneId != zoneId)): self.releaseZoneData() messenger.send(self.getZoneChangeEvent(), [zoneId, oldZoneId]) # if we are not going into the quiet zone, send a 'logical' zone # change message if zoneId != DistributedObjectAI.QuietZone: lastLogicalZone = oldZoneId if oldZoneId == DistributedObjectAI.QuietZone: lastLogicalZone = self.lastNonQuietZone self.handleLogicalZoneChange(zoneId, lastLogicalZone) self.lastNonQuietZone = zoneId def getLocation(self): try: if self.parentId <= 0 and self.zoneId <= 0: return None # This is a -1 stuffed into a uint32 if self.parentId == 0xffffffff and self.zoneId == 0xffffffff: return None return (self.parentId, self.zoneId) except AttributeError: return None def postGenerateMessage(self): self.__generated = True messenger.send(self.uniqueName("generate"), [self]) def updateRequiredFields(self, dclass, di): dclass.receiveUpdateBroadcastRequired(self, di) self.announceGenerate() self.postGenerateMessage() def updateAllRequiredFields(self, dclass, di): dclass.receiveUpdateAllRequired(self, di) self.announceGenerate() self.postGenerateMessage() def updateRequiredOtherFields(self, dclass, di): dclass.receiveUpdateBroadcastRequired(self, di) # Announce generate after updating all the required fields, # but before we update the non-required fields. self.announceGenerate() self.postGenerateMessage() dclass.receiveUpdateOther(self, di) def updateAllRequiredOtherFields(self, dclass, di): dclass.receiveUpdateAllRequired(self, di) # Announce generate after updating all the required fields, # but before we update the non-required fields. self.announceGenerate() self.postGenerateMessage() dclass.receiveUpdateOther(self, di) def startMessageBundle(self, name): self.air.startMessageBundle(name) def sendMessageBundle(self): self.air.sendMessageBundle(self.doId) def getZoneChangeEvent(self): # this event is generated whenever this object changes zones. # arguments are newZoneId, oldZoneId # includes the quiet zone. return DistributedObjectAI.staticGetZoneChangeEvent(self.doId) def getLogicalZoneChangeEvent(self): # this event is generated whenever this object changes to a # non-quiet-zone zone. # arguments are newZoneId, oldZoneId # does not include the quiet zone. return DistributedObjectAI.staticGetLogicalZoneChangeEvent(self.doId) @staticmethod def staticGetZoneChangeEvent(doId): return 'DOChangeZone-%s' % doId @staticmethod def staticGetLogicalZoneChangeEvent(doId): return 'DOLogicalChangeZone-%s' % doId def handleLogicalZoneChange(self, newZoneId, oldZoneId): """this function gets called as if we never go through the quiet zone. Note that it is called once you reach the newZone, and not at the time that you leave the oldZone.""" messenger.send(self.getLogicalZoneChangeEvent(), [newZoneId, oldZoneId]) def getZoneData(self): # Call this to get an AIZoneData object for the current zone. # This class will hold onto it as self._zoneData # setLocation destroys self._zoneData if we move away to # a different zone if self._zoneData is None: from otp.ai.AIZoneData import AIZoneData self._zoneData = AIZoneData(self.air, self.parentId, self.zoneId) return self._zoneData def releaseZoneData(self): # You can call this to release any AIZoneData object that we might be # holding onto. If we're the last one for the current zone, the data # will be destroyed (render, collision traverser, etc.) # Note that the AIZoneData object that we're holding will be destroyed # automatically when we move away or are destroyed. if self._zoneData is not None: self._zoneData.destroy() self._zoneData = None def getRender(self): # note that this will return a different node if we change zones #return self.air.getRender(self.zoneId) return self.getZoneData().getRender() def getNonCollidableParent(self): return self.getZoneData().getNonCollidableParent() def getParentMgr(self): #return self.air.getParentMgr(self.zoneId) return self.getZoneData().getParentMgr() def getCollTrav(self, *args, **kArgs): return self.getZoneData().getCollTrav(*args, **kArgs) def sendUpdate(self, fieldName, args = []): assert self.notify.debugStateCall(self) if self.air: self.air.sendUpdate(self, fieldName, args) def GetPuppetConnectionChannel(self, doId): return doId + (1001 << 32) def GetAccountConnectionChannel(self, doId): return doId + (1003 << 32) def GetAccountIDFromChannelCode(self, channel): return channel >> 32 def GetAvatarIDFromChannelCode(self, channel): return channel & 0xffffffff def sendUpdateToAvatarId(self, avId, fieldName, args): assert self.notify.debugStateCall(self) channelId = self.GetPuppetConnectionChannel(avId) self.sendUpdateToChannel(channelId, fieldName, args) def sendUpdateToAccountId(self, accountId, fieldName, args): assert self.notify.debugStateCall(self) channelId = self.GetAccountConnectionChannel(accountId) self.sendUpdateToChannel(channelId, fieldName, args) def sendUpdateToChannel(self, channelId, fieldName, args): assert self.notify.debugStateCall(self) if self.air: self.air.sendUpdateToChannel(self, channelId, fieldName, args) def generateWithRequired(self, zoneId, optionalFields=[]): assert self.notify.debugStateCall(self) # have we already allocated a doId? if self.__preallocDoId: self.__preallocDoId = 0 return self.generateWithRequiredAndId( self.doId, zoneId, optionalFields) # The repository is the one that really does the work parentId = self.air.districtId self.air.generateWithRequired(self, parentId, zoneId, optionalFields) self.generate() self.announceGenerate() self.postGenerateMessage() # this is a special generate used for estates, or anything else that # needs to have a hard coded doId as assigned by the server def generateWithRequiredAndId(self, doId, parentId, zoneId, optionalFields=[]): assert self.notify.debugStateCall(self) # have we already allocated a doId? if self.__preallocDoId: assert doId == self.doId self.__preallocDoId = 0 # The repository is the one that really does the work self.air.generateWithRequiredAndId(self, doId, parentId, zoneId, optionalFields) self.generate() self.announceGenerate() self.postGenerateMessage() def generateOtpObject(self, parentId, zoneId, optionalFields=[], doId=None): assert self.notify.debugStateCall(self) # have we already allocated a doId? if self.__preallocDoId: assert doId is None or doId == self.doId doId=self.doId self.__preallocDoId = 0 # Assign it an id if doId is None: self.doId = self.air.allocateChannel() else: self.doId = doId # Put the new DO in the dictionaries self.air.addDOToTables(self, location=(parentId, zoneId)) # Send a generate message self.sendGenerateWithRequired(self.air, parentId, zoneId, optionalFields) self.generate() self.announceGenerate() self.postGenerateMessage() def generate(self): """ Inheritors should put functions that require self.zoneId or other networked info in this function. """ assert self.notify.debugStateCall(self) self.__generates += 1 def generateInit(self, repository=None): """ First generate (not from cache). """ assert self.notify.debugStateCall(self) def generateTargetChannel(self, repository): """ Who to send this to for generate messages """ if hasattr(self, "dbObject"): return self.doId return repository.serverId def sendGenerateWithRequired(self, repository, parentId, zoneId, optionalFields=[]): assert self.notify.debugStateCall(self) dg = self.dclass.aiFormatGenerate( self, self.doId, parentId, zoneId, #repository.serverId, self.generateTargetChannel(repository), repository.ourChannel, optionalFields) repository.send(dg) def initFromServerResponse(self, valDict): assert self.notify.debugStateCall(self) # This is a special method used for estates, etc., which get # their fields set from the database indirectly by way of the # AI. The input parameter is a dictionary of field names to # datagrams that describes the initial field values from the # database. dclass = self.dclass for key, value in valDict.items(): # Update the field dclass.directUpdate(self, key, value) def requestDelete(self): assert self.notify.debugStateCall(self) if not self.air: doId = "none" if hasattr(self, "doId"): doId = self.doId self.notify.warning( "Tried to delete a %s (doId %s) that is already deleted" % (self.__class__, doId)) return self.air.requestDelete(self) self._DOAI_requestedDelete = True def taskName(self, taskString): return ("%s-%s" % (taskString, self.doId)) def uniqueName(self, idString): return ("%s-%s" % (idString, self.doId)) def validate(self, avId, bool, msg): if not bool: self.air.writeServerEvent('suspicious', avId, msg) self.notify.warning('validate error: avId: %s -- %s' % (avId, msg)) return bool def beginBarrier(self, name, avIds, timeout, callback): # Begins waiting for a set of avatars. When all avatars in # the list have reported back in or the callback has expired, # calls the indicated callback with the list of avatars that # made it through. There may be multiple barriers waiting # simultaneously on different lists of avatars, although they # should have different names. from otp.ai import Barrier context = self.__nextBarrierContext # We assume the context number is passed as a uint16. self.__nextBarrierContext = (self.__nextBarrierContext + 1) & 0xffff assert self.notify.debug('beginBarrier(%s, %s, %s, %s)' % (context, name, avIds, timeout)) if avIds: barrier = Barrier.Barrier( name, self.uniqueName(name), avIds, timeout, doneFunc = PythonUtil.Functor( self.__barrierCallback, context, callback)) self.__barriers[context] = barrier # Send the context number to each involved client. self.sendUpdate("setBarrierData", [self.getBarrierData()]) else: # No avatars; just call the callback immediately. callback(avIds) return context def getBarrierData(self): # Returns the barrier data formatted for sending to the # clients. This lists all of the current outstanding barriers # and the avIds waiting for them. data = [] for context, barrier in self.__barriers.items(): avatars = barrier.pendingAvatars if avatars: data.append((context, barrier.name, avatars)) return data def ignoreBarrier(self, context): # Aborts a previously-set barrier. The context is the return # value from the previous call to beginBarrier(). barrier = self.__barriers.get(context) if barrier: barrier.cleanup() del self.__barriers[context] def setBarrierReady(self, context): # Generated by the clients to check in after a beginBarrier() # call. avId = self.air.getAvatarIdFromSender() assert self.notify.debug('setBarrierReady(%s, %s)' % (context, avId)) barrier = self.__barriers.get(context) if barrier == None: # This may be None if a client was slow and missed an # earlier timeout. Too bad. return barrier.clear(avId) def __barrierCallback(self, context, callback, avIds): assert self.notify.debug('barrierCallback(%s, %s)' % (context, avIds)) # The callback that is generated when a barrier is completed. barrier = self.__barriers.get(context) if barrier: barrier.cleanup() del self.__barriers[context] callback(avIds) else: self.notify.warning("Unexpected completion from barrier %s" % (context)) def isGridParent(self): # If this distributed object is a DistributedGrid return 1. 0 by default return 0 def execCommand(self, string, mwMgrId, avId, zoneId): pass def _retrieveCachedData(self): """ This is a no-op on the AI. """ pass def setAI(self, aiChannel): self.air.setAI(self.doId, aiChannel)
from direct.extensions_native import VBase4_extensions from direct.extensions_native import NodePath_extensions from panda3d.core import loadPrcFile if __debug__: loadPrcFile('config/general.prc') loadPrcFile('config/release/dev.prc') from direct.directnotify.DirectNotifyGlobal import directNotify notify = directNotify.newCategory('Toontown Planet Client Start') notify.setInfo(True) from otp.settings.Settings import Settings preferencesFilename = ConfigVariableString( 'preferences-filename', 'preferences.json').getValue() notify.info('Reading %s...' % preferencesFilename) __builtin__.settings = Settings(preferencesFilename) if 'fullscreen' not in settings: settings['fullscreen'] = False if 'music' not in settings: settings['music'] = True if 'sfx' not in settings:
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 builtins.__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 hasattr(self._func, '__call__') builtins.globalProfileSessionFunc = self._func builtins.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].values(): if ct > maxTime: maxTime = ct self._duration = maxTime # clean up the RAM file support _removeProfileCustomFuncs(filename) # clean up the globals result = globalProfileSessionResult[0] del builtins.__dict__['globalProfileSessionFunc'] del builtins.__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