oldschool-toontown/otp/distributed/OTPClientRepository.py
2019-11-17 18:51:35 -05:00

2509 lines
103 KiB
Python

import sys
import time
import string
import types
import random
import gc
import os
from pandac.PandaModules import *
from pandac.PandaModules import *
from direct.gui.DirectGui import *
from otp.distributed.OtpDoGlobals import *
from direct.interval.IntervalGlobal import ivalMgr
from direct.directnotify.DirectNotifyGlobal import directNotify
from direct.distributed.ClientRepositoryBase import ClientRepositoryBase
from direct.fsm.ClassicFSM import ClassicFSM
from direct.fsm.State import State
from direct.task import Task
from direct.distributed import DistributedSmoothNode
from direct.showbase import PythonUtil, GarbageReport, BulletinBoardWatcher
from direct.showbase.ContainerLeakDetector import ContainerLeakDetector
from direct.showbase import MessengerLeakDetector
from direct.showbase.GarbageReportScheduler import GarbageReportScheduler
from direct.showbase import LeakDetectors
from direct.distributed.PyDatagram import PyDatagram
from direct.distributed.PyDatagramIterator import PyDatagramIterator
from otp.avatar import Avatar
from otp.avatar.DistributedPlayer import DistributedPlayer
from otp.login import TTAccount
from otp.login import LoginTTSpecificDevAccount
from otp.login import AccountServerConstants
from otp.login.CreateAccountScreen import CreateAccountScreen
from otp.login import LoginScreen
from otp.login import AstronLoginScreen
from otp.otpgui import OTPDialog
from otp.avatar import DistributedAvatar
from otp.otpbase import OTPLocalizer
from otp.login import LoginGSAccount
from otp.login import LoginGoAccount
from otp.login.LoginWebPlayTokenAccount import LoginWebPlayTokenAccount
from otp.login.LoginDISLTokenAccount import LoginDISLTokenAccount
from otp.login import LoginTTAccount
from otp.login import LoginAstronAccount
from otp.login import HTTPUtil
from otp.otpbase import OTPGlobals
from otp.otpbase import OTPLauncherGlobals
from otp.uberdog import OtpAvatarManager
from otp.distributed import OtpDoGlobals
from otp.distributed.TelemetryLimiter import TelemetryLimiter
from otp.ai.GarbageLeakServerEventAggregator import GarbageLeakServerEventAggregator
from PotentialAvatar import PotentialAvatar
class OTPClientRepository(ClientRepositoryBase):
notify = directNotify.newCategory('OTPClientRepository')
avatarLimit = 6
WishNameResult = Enum(['Failure',
'PendingApproval',
'Approved',
'Rejected'])
def __init__(self, serverVersion, launcher = None, playGame = None):
ClientRepositoryBase.__init__(self)
self.handler = None
self.launcher = launcher
base.launcher = launcher
self.__currentAvId = 0
self.productName = config.GetString('product-name', 'DisneyOnline-US')
self.createAvatarClass = None
self.systemMessageSfx = None
reg_deployment = ''
if self.productName == 'DisneyOnline-US':
if self.launcher:
if self.launcher.isDummy():
reg_deployment = self.launcher.getDeployment()
else:
reg_deployment = self.launcher.getRegistry('DEPLOYMENT')
if reg_deployment != 'UK' and reg_deployment != 'AP':
reg_deployment = self.launcher.getRegistry('GAME_DEPLOYMENT')
self.notify.info('reg_deployment=%s' % reg_deployment)
if reg_deployment == 'UK':
self.productName = 'DisneyOnline-UK'
elif reg_deployment == 'AP':
self.productName = 'DisneyOnline-AP'
self.blue = None
if self.launcher:
self.blue = self.launcher.getBlue()
fakeBlue = config.GetString('fake-blue', '')
if fakeBlue:
self.blue = fakeBlue
self.playToken = None
if self.launcher:
self.playToken = self.launcher.getPlayToken()
fakePlayToken = config.GetString('fake-playtoken', '')
if fakePlayToken:
self.playToken = fakePlayToken
self.DISLToken = None
if self.launcher:
self.DISLToken = self.launcher.getDISLToken()
fakeDISLToken = config.GetString('fake-DISLToken', '')
fakeDISLPlayerName = config.GetString('fake-DISL-PlayerName', '')
if fakeDISLToken:
self.DISLToken = fakeDISLToken
elif fakeDISLPlayerName:
defaultId = 42
defaultNumAvatars = 4
defaultNumAvatarSlots = 4
defaultNumConcur = 1
subCount = config.GetInt('fake-DISL-NumSubscriptions', 1)
playerAccountId = config.GetInt('fake-DISL-PlayerAccountId', defaultId)
self.DISLToken = ('ACCOUNT_NAME=%s' % fakeDISLPlayerName +
'&ACCOUNT_NUMBER=%s' % playerAccountId +
'&ACCOUNT_NAME_APPROVAL=%s' % config.GetString('fake-DISL-PlayerNameApproved', 'YES') +
'&SWID=%s' % config.GetString('fake-DISL-SWID', '{1763AC36-D73F-41C2-A54A-B579E58B69C8}') +
'&FAMILY_NUMBER=%s' % config.GetString('fake-DISL-FamilyAccountId', '-1') +
'&familyAdmin=%s' % config.GetString('fake-DISL-FamilyAdmin', '1') +
'&PIRATES_ACCESS=%s' % config.GetString('fake-DISL-PiratesAccess', 'FULL') +
'&PIRATES_MAX_NUM_AVATARS=%s' % config.GetInt('fake-DISL-MaxAvatars', defaultNumAvatars) +
'&PIRATES_NUM_AVATAR_SLOTS=%s' % config.GetInt('fake-DISL-MaxAvatarSlots', defaultNumAvatarSlots) +
'&expires=%s' % config.GetString('fake-DISL-expire', '1577898000') +
'&OPEN_CHAT_ENABLED=%s' % config.GetString('fake-DISL-OpenChatEnabled', 'YES') +
'&CREATE_FRIENDS_WITH_CHAT=%s' % config.GetString('fake-DISL-CreateFriendsWithChat', 'YES') +
'&CHAT_CODE_CREATION_RULE=%s' % config.GetString('fake-DISL-ChatCodeCreation', 'YES') +
'&FAMILY_MEMBERS=%s' % config.GetString('fake-DISL-FamilyMembers') + '&PIRATES_SUB_COUNT=%s' % subCount)
for i in range(subCount):
self.DISLToken += ('&PIRATES_SUB_%s_ACCESS=%s' % (i, config.GetString('fake-DISL-Sub-%s-Access' % i, 'FULL')) +
'&PIRATES_SUB_%s_ACTIVE=%s' % (i, config.GetString('fake-DISL-Sub-%s-Active' % i, 'YES')) +
'&PIRATES_SUB_%s_ID=%s' % (i, config.GetInt('fake-DISL-Sub-%s-Id' % i, playerAccountId) + config.GetInt('fake-DISL-Sub-Id-Offset', 0)) +
'&PIRATES_SUB_%s_LEVEL=%s' % (i, config.GetInt('fake-DISL-Sub-%s-Level' % i, 3)) +
'&PIRATES_SUB_%s_NAME=%s' % (i, config.GetString('fake-DISL-Sub-%s-Name' % i, fakeDISLPlayerName)) +
'&PIRATES_SUB_%s_NUM_AVATARS=%s' % (i, config.GetInt('fake-DISL-Sub-%s-NumAvatars' % i, defaultNumAvatars)) +
'&PIRATES_SUB_%s_NUM_CONCUR=%s' % (i, config.GetInt('fake-DISL-Sub-%s-NumConcur' % i, defaultNumConcur)) +
'&PIRATES_SUB_%s_OWNERID=%s' % (i, config.GetInt('fake-DISL-Sub-%s-OwnerId' % i, playerAccountId)) +
'&PIRATES_SUB_%s_FOUNDER=%s' % (i, config.GetString('fake-DISL-Sub-%s-Founder' % i, 'YES')))
self.DISLToken += ('&WL_CHAT_ENABLED=%s' % config.GetString('fake-DISL-WLChatEnabled', 'YES') +
'&valid=true')
if base.logPrivateInfo:
print self.DISLToken
self.requiredLogin = config.GetString('required-login', 'auto')
if self.requiredLogin == 'auto':
self.notify.info('required-login auto.')
elif self.requiredLogin == 'green':
self.notify.error('The green code is out of date')
elif self.requiredLogin == 'blue':
if not self.blue:
self.notify.error('The tcr does not have the required blue login')
elif self.requiredLogin == 'playToken':
if not self.playToken:
self.notify.error('The tcr does not have the required playToken login')
elif self.requiredLogin == 'DISLToken':
if not self.DISLToken:
self.notify.error('The tcr does not have the required DISL token login')
elif self.requiredLogin == 'gameServer':
self.notify.info('Using game server name/password.')
self.DISLToken = None
else:
self.notify.error('The required-login was not recognized.')
self.computeValidateDownload()
self.wantMagicWords = base.config.GetString('want-magic-words', '')
if self.launcher and hasattr(self.launcher, 'http'):
self.http = self.launcher.http
else:
self.http = HTTPClient()
#self.allocateDcFile()
self.accountOldAuth = config.GetBool('account-old-auth', 0)
self.accountOldAuth = config.GetBool('%s-account-old-auth' % game.name,
self.accountOldAuth)
self.useNewTTDevLogin = base.config.GetBool('use-tt-specific-dev-login', False)
self.astronSupport = config.GetBool('astron-support', True)
if self.astronSupport:
self.loginInterface = LoginAstronAccount.LoginAstronAccount(self)
self.notify.info('loginInterface: LoginAstronAccount')
elif self.useNewTTDevLogin:
self.loginInterface = LoginTTSpecificDevAccount.LoginTTSpecificDevAccount(self)
self.notify.info('loginInterface: LoginTTSpecificDevAccount')
elif self.accountOldAuth:
self.loginInterface = LoginGSAccount.LoginGSAccount(self)
self.notify.info('loginInterface: LoginGSAccount')
elif self.blue:
self.loginInterface = LoginGoAccount.LoginGoAccount(self)
self.notify.info('loginInterface: LoginGoAccount')
elif self.playToken:
self.loginInterface = LoginWebPlayTokenAccount(self)
self.notify.info('loginInterface: LoginWebPlayTokenAccount')
elif self.DISLToken:
self.loginInterface = LoginDISLTokenAccount(self)
self.notify.info('loginInterface: LoginDISLTokenAccount')
else:
self.loginInterface = LoginTTAccount.LoginTTAccount(self)
self.notify.info('loginInterface: LoginTTAccount')
self.secretChatAllowed = base.config.GetBool('allow-secret-chat', 0)
self.openChatAllowed = base.config.GetBool('allow-open-chat', 0)
self.secretChatNeedsParentPassword = base.config.GetBool('secret-chat-needs-parent-password', 0) or (self.launcher and self.launcher.getNeedPwForSecretKey())
self.parentPasswordSet = base.config.GetBool('parent-password-set', 0) or (self.launcher and self.launcher.getParentPasswordSet())
self.userSignature = base.config.GetString('signature', 'none')
self.freeTimeExpiresAt = -1
self.__isPaid = 0
self.periodTimerExpired = 0
self.periodTimerStarted = None
self.periodTimerSecondsRemaining = None
self.parentMgr.registerParent(OTPGlobals.SPRender, base.render)
self.parentMgr.registerParent(OTPGlobals.SPHidden, NodePath())
self.timeManager = None
if config.GetBool('detect-leaks', 0) or config.GetBool('client-detect-leaks', 0):
self.startLeakDetector()
if config.GetBool('detect-messenger-leaks', 0) or config.GetBool('ai-detect-messenger-leaks', 0):
self.messengerLeakDetector = MessengerLeakDetector.MessengerLeakDetector('client messenger leak detector')
if config.GetBool('leak-messages', 0):
MessengerLeakDetector._leakMessengerObject()
if config.GetBool('run-garbage-reports', 0) or config.GetBool('client-run-garbage-reports', 0):
noneValue = -1.0
reportWait = config.GetFloat('garbage-report-wait', noneValue)
reportWaitScale = config.GetFloat('garbage-report-wait-scale', noneValue)
if reportWait == noneValue:
reportWait = 60.0 * 2.0
if reportWaitScale == noneValue:
reportWaitScale = None
self.garbageReportScheduler = GarbageReportScheduler(waitBetween=reportWait,
waitScale=reportWaitScale)
self._proactiveLeakChecks = config.GetBool('proactive-leak-checks', 1) or config.GetBool('client-proactive-leak-checks', 1)
self._crashOnProactiveLeakDetect = config.GetBool('crash-on-proactive-leak-detect', 1)
self.activeDistrictMap = {}
self.telemetryLimiter = TelemetryLimiter()
self.serverVersion = serverVersion
self.waitingForDatabase = None
self.loginFSM = ClassicFSM('loginFSM', [
State('loginOff',
self.enterLoginOff,
self.exitLoginOff, [
'connect']),
State('connect',
self.enterConnect,
self.exitConnect, [
'login',
'failedToConnect',
'failedToGetServerConstants']),
State('login',
self.enterLogin,
self.exitLogin, [
'noConnection',
'waitForGameList',
'createAccount',
'reject',
'failedToConnect',
'shutdown']),
State('createAccount',
self.enterCreateAccount,
self.exitCreateAccount, [
'noConnection',
'waitForGameList',
'login',
'reject',
'failedToConnect',
'shutdown']),
State('failedToConnect',
self.enterFailedToConnect,
self.exitFailedToConnect, [
'connect',
'shutdown']),
State('failedToGetServerConstants',
self.enterFailedToGetServerConstants,
self.exitFailedToGetServerConstants, [
'connect',
'shutdown',
'noConnection']),
State('shutdown',
self.enterShutdown,
self.exitShutdown, [
'loginOff']),
State('waitForGameList',
self.enterWaitForGameList,
self.exitWaitForGameList, [
'noConnection',
'waitForShardList',
'missingGameRootObject']),
State('missingGameRootObject',
self.enterMissingGameRootObject,
self.exitMissingGameRootObject, [
'waitForGameList',
'shutdown']),
State('waitForShardList',
self.enterWaitForShardList,
self.exitWaitForShardList, [
'noConnection',
'waitForAvatarList',
'noShards']),
State('noShards',
self.enterNoShards,
self.exitNoShards, [
'noConnection',
'noShardsWait',
'shutdown']),
State('noShardsWait',
self.enterNoShardsWait,
self.exitNoShardsWait, [
'noConnection',
'waitForShardList',
'shutdown']),
State('reject',
self.enterReject,
self.exitReject, []),
State('noConnection',
self.enterNoConnection,
self.exitNoConnection, [
'login',
'connect',
'shutdown']),
State('afkTimeout',
self.enterAfkTimeout,
self.exitAfkTimeout, [
'waitForAvatarList',
'shutdown']),
State('periodTimeout',
self.enterPeriodTimeout,
self.exitPeriodTimeout, [
'shutdown']),
State('waitForAvatarList',
self.enterWaitForAvatarList,
self.exitWaitForAvatarList, [
'noConnection',
'chooseAvatar',
'shutdown']),
State('chooseAvatar',
self.enterChooseAvatar,
self.exitChooseAvatar, [
'noConnection',
'createAvatar',
'waitForAvatarList',
'waitForSetAvatarResponse',
'waitForDeleteAvatarResponse',
'shutdown',
'login']),
State('createAvatar',
self.enterCreateAvatar,
self.exitCreateAvatar, [
'noConnection',
'chooseAvatar',
'waitForSetAvatarResponse',
'shutdown']),
State('waitForDeleteAvatarResponse',
self.enterWaitForDeleteAvatarResponse,
self.exitWaitForDeleteAvatarResponse, [
'noConnection',
'chooseAvatar',
'shutdown']),
State('rejectRemoveAvatar',
self.enterRejectRemoveAvatar,
self.exitRejectRemoveAvatar, [
'noConnection',
'chooseAvatar',
'shutdown']),
State('waitForSetAvatarResponse',
self.enterWaitForSetAvatarResponse,
self.exitWaitForSetAvatarResponse, [
'noConnection',
'playingGame',
'shutdown']),
State('playingGame',
self.enterPlayingGame,
self.exitPlayingGame, [
'noConnection',
'waitForAvatarList',
'login',
'shutdown',
'afkTimeout',
'periodTimeout',
'noShards'])],
'loginOff', 'loginOff')
self.gameFSM = ClassicFSM('gameFSM', [
State('gameOff',
self.enterGameOff,
self.exitGameOff, [
'waitOnEnterResponses']),
State('waitOnEnterResponses',
self.enterWaitOnEnterResponses,
self.exitWaitOnEnterResponses, [
'playGame',
'tutorialQuestion',
'gameOff']),
State('tutorialQuestion',
self.enterTutorialQuestion,
self.exitTutorialQuestion, [
'playGame',
'gameOff']),
State('playGame',
self.enterPlayGame,
self.exitPlayGame, [
'gameOff',
'closeShard',
'switchShards']),
State('switchShards',
self.enterSwitchShards,
self.exitSwitchShards, [
'gameOff',
'waitOnEnterResponses']),
State('closeShard',
self.enterCloseShard,
self.exitCloseShard, [
'gameOff',
'waitOnEnterResponses'])],
'gameOff', 'gameOff')
self.loginFSM.getStateNamed('playingGame').addChild(self.gameFSM)
self.loginFSM.enterInitialState()
self.loginScreen = None
self.music = None
self.gameDoneEvent = 'playGameDone'
self.playGame = playGame(self.gameFSM, self.gameDoneEvent)
self.shardListHandle = None
self.uberZoneInterest = None
self.wantSwitchboard = config.GetBool('want-switchboard', 0)
self.wantSwitchboardHacks = base.config.GetBool('want-switchboard-hacks', 0)
self.__pendingGenerates = {}
self.__pendingMessages = {}
self.__doId2pendingInterest = {}
self.centralLogger = self.generateGlobalObject(OtpDoGlobals.OTP_DO_ID_CENTRAL_LOGGER, 'CentralLogger')
if self.astronSupport:
self.astronLoginManager = self.generateGlobalObject(OtpDoGlobals.OTP_DO_ID_ASTRON_LOGIN_MANAGER, 'AstronLoginManager')
def startLeakDetector(self):
if hasattr(self, 'leakDetector'):
return False
firstCheckDelay = config.GetFloat('leak-detector-first-check-delay', 2 * 60.0)
self.leakDetector = ContainerLeakDetector('client container leak detector', firstCheckDelay=firstCheckDelay)
self.objectTypesLeakDetector = LeakDetectors.ObjectTypesLeakDetector()
self.garbageLeakDetector = LeakDetectors.GarbageLeakDetector()
self.renderLeakDetector = LeakDetectors.SceneGraphLeakDetector(render)
self.hiddenLeakDetector = LeakDetectors.SceneGraphLeakDetector(hidden)
self.cppMemoryUsageLeakDetector = LeakDetectors.CppMemoryUsage()
self.taskLeakDetector = LeakDetectors.TaskLeakDetector()
self.messageListenerTypesLeakDetector = LeakDetectors.MessageListenerTypesLeakDetector()
return True
def getGameDoId(self):
return self.GameGlobalsId
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def enterLoginOff(self):
self.handler = self.handleMessageType
self.shardListHandle = None
return
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def exitLoginOff(self):
self.handler = None
return
def computeValidateDownload(self):
if self.launcher:
hash = HashVal()
hash.mergeWith(launcher.launcherFileDbHash)
hash.mergeWith(launcher.serverDbFileHash)
self.validateDownload = hash.asHex()
else:
self.validateDownload = ''
basePath = os.path.expandvars('$TOONTOWN') or './toontown'
downloadParFilename = Filename.expandFrom(basePath + '/src/configfiles/download.par')
if downloadParFilename.exists():
downloadPar = open(downloadParFilename.toOsSpecific())
for line in downloadPar.readlines():
i = string.find(line, 'VALIDATE_DOWNLOAD=')
if i != -1:
self.validateDownload = string.strip(line[i + 18:])
break
def getServerVersion(self):
return self.serverVersion
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def enterConnect(self, serverList):
self.serverList = serverList
dialogClass = OTPGlobals.getGlobalDialogClass()
self.connectingBox = dialogClass(message=OTPLocalizer.CRConnecting)
self.connectingBox.show()
self.renderFrame()
self.handler = self.handleMessageType
self.connect(self.serverList, successCallback=self._handleConnected, failureCallback=self.failedToConnect)
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def failedToConnect(self, statusCode, statusString):
self.loginFSM.request('failedToConnect', [statusCode, statusString])
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def exitConnect(self):
self.connectingBox.cleanup()
del self.connectingBox
def handleSystemMessage(self, di):
message = ClientRepositoryBase.handleSystemMessage(self, di)
whisper = WhisperPopup(message, OTPGlobals.getInterfaceFont(), WhisperPopup.WTSystem)
whisper.manage(base.marginManager)
if not self.systemMessageSfx:
self.systemMessageSfx = base.loadSfx('phase_3/audio/sfx/clock03.mp3')
if self.systemMessageSfx:
base.playSfx(self.systemMessageSfx)
def getConnectedEvent(self):
return 'OTPClientRepository-connected'
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def _handleConnected(self):
self.launcher.setDisconnectDetailsNormal()
messenger.send(self.getConnectedEvent())
self.gotoFirstScreen()
def gotoFirstScreen(self):
try:
self.accountServerConstants = AccountServerConstants.AccountServerConstants(self)
except TTAccount.TTAccountException, e:
self.notify.debug(str(e))
self.loginFSM.request('failedToGetServerConstants', [e])
return
self.startReaderPollTask()
if not self.astronSupport:
self.startHeartbeat()
newInstall = launcher.getIsNewInstallation()
newInstall = base.config.GetBool('new-installation', newInstall)
if newInstall:
self.notify.warning('new installation')
self.loginFSM.request('login')
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def enterLogin(self):
self.sendSetAvatarIdMsg(0)
self.loginDoneEvent = 'loginDone'
if self.astronSupport:
self.loginScreen = AstronLoginScreen.AstronLoginScreen(self, self.loginDoneEvent)
else:
self.loginScreen = LoginScreen.LoginScreen(self, self.loginDoneEvent)
self.accept(self.loginDoneEvent, self.__handleLoginDone)
self.loginScreen.load()
self.loginScreen.enter()
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def __handleLoginDone(self, doneStatus):
mode = doneStatus['mode']
if mode == 'success':
self.setIsNotNewInstallation()
self.loginFSM.request('waitForGameList')
elif mode == 'getChatPassword':
self.loginFSM.request('parentPassword')
elif mode == 'freeTimeExpired':
self.loginFSM.request('freeTimeInform')
elif mode == 'createAccount':
self.loginFSM.request('createAccount', [{'back': 'login',
'backArgs': []}])
elif mode == 'reject':
self.loginFSM.request('reject')
elif mode == 'quit':
self.loginFSM.request('shutdown')
elif mode == 'failure':
self.loginFSM.request('failedToConnect', [-1, '?'])
else:
self.notify.error('Invalid doneStatus mode from loginScreen: ' + str(mode))
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def exitLogin(self):
if self.loginScreen:
self.loginScreen.exit()
self.loginScreen.unload()
self.loginScreen = None
self.renderFrame()
self.ignore(self.loginDoneEvent)
del self.loginDoneEvent
self.handler = None
return
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def enterCreateAccount(self, createAccountDoneData = {'back': 'login',
'backArgs': []}):
self.createAccountDoneData = createAccountDoneData
self.createAccountDoneEvent = 'createAccountDone'
self.createAccountScreen = None
self.createAccountScreen = CreateAccountScreen(self, self.createAccountDoneEvent)
self.accept(self.createAccountDoneEvent, self.__handleCreateAccountDone)
self.createAccountScreen.load()
self.createAccountScreen.enter()
return
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def __handleCreateAccountDone(self, doneStatus):
mode = doneStatus['mode']
if mode == 'success':
self.setIsNotNewInstallation()
self.loginFSM.request('waitForGameList')
elif mode == 'reject':
self.loginFSM.request('reject')
elif mode == 'cancel':
self.loginFSM.request(self.createAccountDoneData['back'], self.createAccountDoneData['backArgs'])
elif mode == 'failure':
self.loginFSM.request(self.createAccountDoneData['back'], self.createAccountDoneData['backArgs'])
elif mode == 'quit':
self.loginFSM.request('shutdown')
else:
self.notify.error('Invalid doneStatus mode from CreateAccountScreen: ' + str(mode))
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def exitCreateAccount(self):
if self.createAccountScreen:
self.createAccountScreen.exit()
self.createAccountScreen.unload()
self.createAccountScreen = None
self.renderFrame()
self.ignore(self.createAccountDoneEvent)
del self.createAccountDoneEvent
self.handler = None
return
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def enterFailedToConnect(self, statusCode, statusString):
self.handler = self.handleMessageType
messenger.send('connectionIssue')
url = self.serverList[0]
self.notify.warning('Failed to connect to %s (%s %s). Notifying user.' % (url.cStr(), statusCode, statusString))
if statusCode == 1403 or statusCode == 1405 or statusCode == 1400:
message = OTPLocalizer.CRNoConnectProxyNoPort % (url.getServer(), url.getPort(), url.getPort())
style = OTPDialog.CancelOnly
else:
message = OTPLocalizer.CRNoConnectTryAgain % (url.getServer(), url.getPort())
style = OTPDialog.TwoChoice
dialogClass = OTPGlobals.getGlobalDialogClass()
self.failedToConnectBox = dialogClass(message=message, doneEvent='failedToConnectAck', text_wordwrap=18, style=style)
self.failedToConnectBox.show()
self.notify.info(message)
self.accept('failedToConnectAck', self.__handleFailedToConnectAck)
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def __handleFailedToConnectAck(self):
doneStatus = self.failedToConnectBox.doneStatus
if doneStatus == 'ok':
self.loginFSM.request('connect', [self.serverList])
messenger.send('connectionRetrying')
elif doneStatus == 'cancel':
self.loginFSM.request('shutdown')
else:
self.notify.error('Unrecognized doneStatus: ' + str(doneStatus))
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def exitFailedToConnect(self):
self.handler = None
self.ignore('failedToConnectAck')
self.failedToConnectBox.cleanup()
del self.failedToConnectBox
return
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def enterFailedToGetServerConstants(self, e):
self.handler = self.handleMessageType
messenger.send('connectionIssue')
url = AccountServerConstants.AccountServerConstants.getServerURL()
statusCode = 0
if isinstance(e, HTTPUtil.ConnectionError):
statusCode = e.statusCode
self.notify.warning('Got status code %s from connection to %s.' % (statusCode, url.cStr()))
else:
self.notify.warning("Didn't get status code from connection to %s." % url.cStr())
if statusCode == 1403 or statusCode == 1400:
message = OTPLocalizer.CRServerConstantsProxyNoPort % (url.cStr(), url.getPort())
style = OTPDialog.CancelOnly
elif statusCode == 1405:
message = OTPLocalizer.CRServerConstantsProxyNoCONNECT % url.cStr()
style = OTPDialog.CancelOnly
else:
message = OTPLocalizer.CRServerConstantsTryAgain % url.cStr()
style = OTPDialog.TwoChoice
dialogClass = OTPGlobals.getGlobalDialogClass()
self.failedToGetConstantsBox = dialogClass(message=message, doneEvent='failedToGetConstantsAck', text_wordwrap=18, style=style)
self.failedToGetConstantsBox.show()
self.accept('failedToGetConstantsAck', self.__handleFailedToGetConstantsAck)
self.notify.warning('Failed to get account server constants. Notifying user.')
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def __handleFailedToGetConstantsAck(self):
doneStatus = self.failedToGetConstantsBox.doneStatus
if doneStatus == 'ok':
self.loginFSM.request('connect', [self.serverList])
messenger.send('connectionRetrying')
elif doneStatus == 'cancel':
self.loginFSM.request('shutdown')
else:
self.notify.error('Unrecognized doneStatus: ' + str(doneStatus))
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def exitFailedToGetServerConstants(self):
self.handler = None
self.ignore('failedToGetConstantsAck')
self.failedToGetConstantsBox.cleanup()
del self.failedToGetConstantsBox
return
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def enterShutdown(self, errorCode = None):
self.handler = self.handleMessageType
self.sendDisconnect()
self.notify.info('Exiting cleanly')
base.exitShow(errorCode)
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def exitShutdown(self):
if hasattr(self, 'garbageWatcher'):
self.garbageWatcher.destroy()
del self.garbageWatcher
self.handler = None
return
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def enterWaitForGameList(self):
self.gameDoDirectory = self.addTaggedInterest(self.GameGlobalsId, OTP_ZONE_ID_MANAGEMENT, self.ITAG_PERM, 'game directory', event='GameList_Complete')
self.acceptOnce('GameList_Complete', self.waitForGetGameListResponse)
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def waitForGetGameListResponse(self):
if self.isGameListCorrect():
if base.config.GetBool('game-server-tests', 0):
from otp.distributed import GameServerTestSuite
GameServerTestSuite.GameServerTestSuite(self)
self.loginFSM.request('waitForShardList')
else:
self.loginFSM.request('missingGameRootObject')
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def isGameListCorrect(self):
return 1
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def exitWaitForGameList(self):
self.handler = None
return
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def enterMissingGameRootObject(self):
self.notify.warning('missing some game root objects.')
self.handler = self.handleMessageType
dialogClass = OTPGlobals.getGlobalDialogClass()
self.missingGameRootObjectBox = dialogClass(message=OTPLocalizer.CRMissingGameRootObject, doneEvent='missingGameRootObjectBoxAck', style=OTPDialog.TwoChoice)
self.missingGameRootObjectBox.show()
self.accept('missingGameRootObjectBoxAck', self.__handleMissingGameRootObjectAck)
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def __handleMissingGameRootObjectAck(self):
doneStatus = self.missingGameRootObjectBox.doneStatus
if doneStatus == 'ok':
self.loginFSM.request('waitForGameList')
elif doneStatus == 'cancel':
self.loginFSM.request('shutdown')
else:
self.notify.error('Unrecognized doneStatus: ' + str(doneStatus))
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def exitMissingGameRootObject(self):
self.handler = None
self.ignore('missingGameRootObjectBoxAck')
self.missingGameRootObjectBox.cleanup()
del self.missingGameRootObjectBox
return
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def enterWaitForShardList(self):
if not self.isValidInterestHandle(self.shardListHandle):
self.shardListHandle = self.addTaggedInterest(self.GameGlobalsId, OTP_ZONE_ID_DISTRICTS, self.ITAG_PERM, 'LocalShardList', event='ShardList_Complete')
self.acceptOnce('ShardList_Complete', self._wantShardListComplete)
else:
self._wantShardListComplete()
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def _wantShardListComplete(self):
if self._shardsAreReady():
self.loginFSM.request('waitForAvatarList')
else:
self.loginFSM.request('noShards')
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def _shardsAreReady(self):
for shard in self.activeDistrictMap.values():
if shard.available:
return True
else:
return False
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def exitWaitForShardList(self):
self.ignore('ShardList_Complete')
self.handler = None
return
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def enterNoShards(self):
messenger.send('connectionIssue')
self.handler = self.handleMessageType
dialogClass = OTPGlobals.getGlobalDialogClass()
self.noShardsBox = dialogClass(message=OTPLocalizer.CRNoDistrictsTryAgain, doneEvent='noShardsAck', style=OTPDialog.TwoChoice)
self.noShardsBox.show()
self.accept('noShardsAck', self.__handleNoShardsAck)
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def __handleNoShardsAck(self):
doneStatus = self.noShardsBox.doneStatus
if doneStatus == 'ok':
messenger.send('connectionRetrying')
self.loginFSM.request('noShardsWait')
elif doneStatus == 'cancel':
self.loginFSM.request('shutdown')
else:
self.notify.error('Unrecognized doneStatus: ' + str(doneStatus))
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def exitNoShards(self):
self.handler = None
self.ignore('noShardsAck')
self.noShardsBox.cleanup()
del self.noShardsBox
return
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def enterNoShardsWait(self):
dialogClass = OTPGlobals.getGlobalDialogClass()
self.connectingBox = dialogClass(message=OTPLocalizer.CRConnecting)
self.connectingBox.show()
self.renderFrame()
self.noShardsWaitTaskName = 'noShardsWait'
def doneWait(task, self = self):
self.loginFSM.request('waitForShardList')
if __dev__:
delay = 0.0
else:
delay = 6.5 + random.random() * 2.0
taskMgr.doMethodLater(delay, doneWait, self.noShardsWaitTaskName)
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def exitNoShardsWait(self):
taskMgr.remove(self.noShardsWaitTaskName)
del self.noShardsWaitTaskName
self.connectingBox.cleanup()
del self.connectingBox
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def enterReject(self):
self.handler = self.handleMessageType
self.notify.warning('Connection Rejected')
launcher.setPandaErrorCode(13)
sys.exit()
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def exitReject(self):
self.handler = None
return
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def enterNoConnection(self):
messenger.send('connectionIssue')
self.resetInterestStateForConnectionLoss()
self.shardListHandle = None
self.handler = self.handleMessageType
self.__currentAvId = 0
self.stopHeartbeat()
self.stopReaderPollTask()
gameUsername = launcher.getValue('GAME_USERNAME', base.cr.userName)
if self.bootedIndex != None and OTPLocalizer.CRBootedReasons.has_key(self.bootedIndex):
message = OTPLocalizer.CRBootedReasons[self.bootedIndex] % {'name': gameUsername}
elif self.bootedText != None:
message = OTPLocalizer.CRBootedReasonUnknownCode % self.bootedIndex
else:
message = OTPLocalizer.CRLostConnection
reconnect = 1
if self.bootedIndex in (152, 127):
reconnect = 0
self.launcher.setDisconnectDetails(self.bootedIndex, message)
style = OTPDialog.Acknowledge
if reconnect and self.loginInterface.supportsRelogin():
message += OTPLocalizer.CRTryConnectAgain
style = OTPDialog.TwoChoice
dialogClass = OTPGlobals.getGlobalDialogClass()
self.lostConnectionBox = dialogClass(doneEvent='lostConnectionAck', message=message, text_wordwrap=18, style=style)
self.lostConnectionBox.show()
self.accept('lostConnectionAck', self.__handleLostConnectionAck)
self.notify.warning('Lost connection to server. Notifying user.')
return
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def __handleLostConnectionAck(self):
if self.lostConnectionBox.doneStatus == 'ok' and self.loginInterface.supportsRelogin():
self.loginFSM.request('connect', [self.serverList])
else:
self.loginFSM.request('shutdown')
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def exitNoConnection(self):
self.handler = None
self.ignore('lostConnectionAck')
self.lostConnectionBox.cleanup()
messenger.send('connectionRetrying')
return
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def enterAfkTimeout(self):
self.sendSetAvatarIdMsg(0)
msg = OTPLocalizer.AfkForceAcknowledgeMessage
dialogClass = OTPGlobals.getDialogClass()
self.afkDialog = dialogClass(text=msg, command=self.__handleAfkOk, style=OTPDialog.Acknowledge)
self.handler = self.handleMessageType
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def __handleAfkOk(self, value):
self.loginFSM.request('waitForAvatarList')
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def exitAfkTimeout(self):
if self.afkDialog:
self.afkDialog.cleanup()
self.afkDialog = None
self.handler = None
return
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def enterPeriodTimeout(self):
self.sendSetAvatarIdMsg(0)
self.sendDisconnect()
msg = OTPLocalizer.PeriodForceAcknowledgeMessage
dialogClass = OTPGlobals.getDialogClass()
self.periodDialog = dialogClass(text=msg, command=self.__handlePeriodOk, style=OTPDialog.Acknowledge)
self.handler = self.handleMessageType
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def __handlePeriodOk(self, value):
base.exitShow()
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def exitPeriodTimeout(self):
if self.periodDialog:
self.periodDialog.cleanup()
self.periodDialog = None
self.handler = None
return
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def enterWaitForAvatarList(self):
if not self.astronSupport:
self.handler = self.handleWaitForAvatarList
self._requestAvatarList()
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def _requestAvatarList(self):
self.sendGetAvatarsMsg()
self.waitForDatabaseTimeout(requestName='WaitForAvatarList')
self.acceptOnce(OtpAvatarManager.OtpAvatarManager.OnlineEvent, self._requestAvatarList)
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def sendGetAvatarsMsg(self):
if self.astronSupport:
self.astronLoginManager.sendRequestAvatarList()
else:
datagram = PyDatagram()
datagram.addUint16(CLIENT_GET_AVATARS)
self.send(datagram)
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def exitWaitForAvatarList(self):
self.cleanupWaitingForDatabase()
self.ignore(OtpAvatarManager.OtpAvatarManager.OnlineEvent)
self.handler = None
return
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def handleWaitForAvatarList(self, msgType, di):
if msgType == CLIENT_GET_AVATARS_RESP:
self.handleGetAvatarsRespMsg(di)
elif msgType == CLIENT_GET_AVATARS_RESP2:
pass
else:
self.handleMessageType(msgType, di)
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def handleGetAvatarsRespMsg(self, di):
returnCode = di.getUint8()
if returnCode == 0:
avatarTotal = di.getUint16()
avList = []
for i in range(0, avatarTotal):
avNum = di.getUint32()
avNames = ['',
'',
'',
'']
avNames[0] = di.getString()
avNames[1] = di.getString()
avNames[2] = di.getString()
avNames[3] = di.getString()
avDNA = di.getString()
avPosition = di.getUint8()
aname = di.getUint8()
potAv = PotentialAvatar(avNum, avNames, avDNA, avPosition, aname)
avList.append(potAv)
self.avList = avList
self.loginFSM.request('chooseAvatar', [self.avList])
else:
self.notify.error('Bad avatar list return code: ' + str(returnCode))
self.loginFSM.request('shutdown')
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def handleGetAvatarsResp2Msg(self, di):
returnCode = di.getUint8()
if returnCode == 0:
avatarTotal = di.getUint16()
avList = []
for i in range(0, avatarTotal):
avNum = di.getUint32()
avNames = ['',
'',
'',
'']
avNames[0] = di.getString()
avDNA = None
avPosition = di.getUint8()
aname = None
potAv = PotentialAvatar(avNum, avNames, avDNA, avPosition, aname)
avList.append(potAv)
self.avList = avList
self.loginFSM.request('chooseAvatar', [self.avList])
else:
self.notify.error('Bad avatar list return code: ' + str(returnCode))
self.loginFSM.request('shutdown')
return
if config.GetBool('astron-support', True):
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def handleAvatarListResponse(self, avatarList):
avList = []
for avNum, avName, avDNA, avPosition, nameState in avatarList:
avNames = ['',
'',
'',
'']
avNames[0] = avName
if nameState == 2: # Pending
avNames[1] = avName
elif nameState == 3: # Approved
avNames[2] = avName
elif nameState == 4: # Rejected
avNames[3] = avName
aname = int(nameState == 1)
potAv = PotentialAvatar(avNum, avNames, avDNA, avPosition, aname)
avList.append(potAv)
self.avList = avList
self.loginFSM.request('chooseAvatar', [self.avList])
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def enterChooseAvatar(self, avList):
pass
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def exitChooseAvatar(self):
pass
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def enterCreateAvatar(self, avList, index, newDNA = None):
pass
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def exitCreateAvatar(self):
pass
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def sendCreateAvatarMsg(self, avDNA, avName, avPosition):
if self.astronSupport:
self.astronLoginManager.sendCreateAvatar(avDNA, avName, avPosition)
else:
datagram = PyDatagram()
datagram.addUint16(CLIENT_CREATE_AVATAR)
datagram.addUint16(0)
datagram.addString(avDNA.makeNetString())
datagram.addUint8(avPosition)
self.newName = avName
self.newDNA = avDNA
self.newPosition = avPosition
self.send(datagram)
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def sendCreateAvatar2Msg(self, avClass, avDNA, avName, avPosition):
className = avClass.__name__
dclass = self.dclassesByName[className]
datagram = PyDatagram()
datagram.addUint16(CLIENT_CREATE_AVATAR2)
datagram.addUint16(0)
datagram.addUint8(avPosition)
datagram.addUint16(dclass.getNumber())
self.newName = avName
self.newDNA = avDNA
self.newPosition = avPosition
self.send(datagram)
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def enterWaitForDeleteAvatarResponse(self, potAv):
if not self.astronSupport:
self.handler = self.handleWaitForDeleteAvatarResponse
self.sendDeleteAvatarMsg(potAv.id)
self.waitForDatabaseTimeout(requestName='WaitForDeleteAvatarResponse')
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def sendDeleteAvatarMsg(self, avId):
if self.astronSupport:
self.astronLoginManager.sendRequestRemoveAvatar(avId)
else:
datagram = PyDatagram()
datagram.addUint16(CLIENT_DELETE_AVATAR)
datagram.addUint32(avId)
self.send(datagram)
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def exitWaitForDeleteAvatarResponse(self):
self.cleanupWaitingForDatabase()
self.handler = None
return
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def handleWaitForDeleteAvatarResponse(self, msgType, di):
if msgType == CLIENT_DELETE_AVATAR_RESP:
self.handleGetAvatarsRespMsg(di)
else:
self.handleMessageType(msgType, di)
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def enterRejectRemoveAvatar(self, reasonCode):
self.notify.warning('Rejected removed avatar. (%s)' % (reasonCode,))
self.handler = self.handleMessageType
dialogClass = OTPGlobals.getGlobalDialogClass()
self.rejectRemoveAvatarBox = dialogClass(message='%s\n(%s)' % (OTPLocalizer.CRRejectRemoveAvatar, reasonCode), doneEvent='rejectRemoveAvatarAck', style=OTPDialog.Acknowledge)
self.rejectRemoveAvatarBox.show()
self.accept('rejectRemoveAvatarAck', self.__handleRejectRemoveAvatar)
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def __handleRejectRemoveAvatar(self):
self.loginFSM.request('chooseAvatar')
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def exitRejectRemoveAvatar(self):
self.handler = None
self.ignore('rejectRemoveAvatarAck')
self.rejectRemoveAvatarBox.cleanup()
del self.rejectRemoveAvatarBox
return
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def enterWaitForSetAvatarResponse(self, potAv):
if not self.astronSupport:
self.handler = self.handleWaitForSetAvatarResponse
self.sendSetAvatarMsg(potAv)
self.waitForDatabaseTimeout(requestName='WaitForSetAvatarResponse')
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def exitWaitForSetAvatarResponse(self):
self.cleanupWaitingForDatabase()
self.handler = None
return
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def sendSetAvatarMsg(self, potAv):
self.sendSetAvatarIdMsg(potAv.id)
self.avData = potAv
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def sendSetAvatarIdMsg(self, avId):
if avId != self.__currentAvId:
self.__currentAvId = avId
if self.astronSupport:
self.astronLoginManager.sendRequestPlayAvatar(avId)
else:
datagram = PyDatagram()
datagram.addUint16(CLIENT_SET_AVATAR)
datagram.addUint32(avId)
self.send(datagram)
if avId == 0:
self.stopPeriodTimer()
else:
self.startPeriodTimer()
if not config.GetBool('astron-support', True):
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def handleAvatarResponseMsg(self, di):
pass
else:
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def handleAvatarResponseMsg(self, avatarId, di):
pass
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def handleWaitForSetAvatarResponse(self, msgType, di):
if msgType == CLIENT_GET_AVATAR_DETAILS_RESP:
self.handleAvatarResponseMsg(di)
elif msgType == CLIENT_GET_PET_DETAILS_RESP:
self.handleAvatarResponseMsg(di)
elif msgType == CLIENT_GET_FRIEND_LIST_RESP:
self.handleGetFriendsList(di)
elif msgType == CLIENT_GET_FRIEND_LIST_EXTENDED_RESP:
self.handleGetFriendsListExtended(di)
elif msgType == CLIENT_FRIEND_ONLINE:
self.handleFriendOnline(di)
elif msgType == CLIENT_FRIEND_OFFLINE:
self.handleFriendOffline(di)
else:
self.handleMessageType(msgType, di)
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def enterPlayingGame(self):
pass
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def exitPlayingGame(self):
self.notify.info('sending clientLogout')
messenger.send('clientLogout')
@report(types=['args'], dConfigParam='teleport')
def detectLeaks(self, okTasks = None, okEvents = None):
if not __dev__ or configIsToday('allow-unclean-exit'):
return
leakedTasks = self.detectLeakedTasks(okTasks)
leakedEvents = self.detectLeakedEvents(okEvents)
leakedIvals = self.detectLeakedIntervals()
leakedGarbage = self.detectLeakedGarbage()
if leakedTasks or leakedEvents or leakedIvals or leakedGarbage:
errorCode = base.getExitErrorCode()
if errorCode >= OTPLauncherGlobals.NonErrorExitStateStart and errorCode <= OTPLauncherGlobals.NonErrorExitStateEnd:
logFunc = self.notify.warning
allowExit = True
elif __debug__ and not PythonUtil.configIsToday('temp-disable-leak-detection'):
logFunc = self.notify.error
allowExit = False
else:
logFunc = self.notify.warning
allowExit = False
if base.config.GetBool('direct-gui-edit', 0):
logFunc('There are leaks: %s tasks, %s events, %s ivals, %s garbage cycles\nLeaked Events may be due to direct gui editing' % (leakedTasks,
leakedEvents,
leakedIvals,
leakedGarbage))
else:
logFunc('There are leaks: %s tasks, %s events, %s ivals, %s garbage cycles' % (leakedTasks,
leakedEvents,
leakedIvals,
leakedGarbage))
if allowExit:
self.notify.info('Allowing client to leave, panda error code %s' % errorCode)
else:
base.userExit()
else:
self.notify.info('There are no leaks detected.')
def detectLeakedGarbage(self, callback = None):
if not __debug__:
return 0
self.notify.info('checking for leaked garbage...')
if gc.garbage:
self.notify.warning('garbage already contains %d items' % len(gc.garbage))
report = GarbageReport.GarbageReport('logout', verbose=True)
numCycles = report.getNumCycles()
if numCycles:
msg = "You can't leave until you take out your garbage. See report above & base.garbage"
self.notify.info(msg)
report.destroy()
return numCycles
def detectLeakedTasks(self, extraTasks = None):
allowedTasks = ['dataLoop',
'resetPrevTransform',
'doLaterProcessor',
'eventManager',
'readerPollTask',
'heartBeat',
'gridZoneLoop',
'igLoop',
'audioLoop',
'asyncLoad',
'collisionLoop',
'shadowCollisionLoop',
'ivalLoop',
'downloadSequence',
'patchAndHash',
'launcher-download',
'launcher-download-multifile',
'launcher-decompressFile',
'launcher-decompressMultifile',
'launcher-extract',
'launcher-patch',
'slowCloseShardCallback',
'tkLoop',
'manager-update',
'downloadStallTask',
'clientSleep',
jobMgr.TaskName,
self.GarbageCollectTaskName,
'RedownloadNewsTask',
TelemetryLimiter.TaskName]
if extraTasks is not None:
allowedTasks.extend(extraTasks)
problems = []
for task in taskMgr.getTasks():
if not hasattr(task, 'name'):
continue
if task.name in allowedTasks:
continue
else:
if hasattr(task, 'debugInitTraceback'):
print task.debugInitTraceback
problems.append(task.name)
if problems:
print taskMgr
msg = "You can't leave until you clean up your tasks: {"
for task in problems:
msg += '\n ' + task
msg += '}\n'
self.notify.info(msg)
return len(problems)
else:
return 0
return
def detectLeakedEvents(self, extraHooks = None):
allowedHooks = ['destroy-DownloadWatcherBar',
'destroy-DownloadWatcherText',
'destroy-fade',
'f9',
'control-f9',
'launcherAllPhasesComplete',
'launcherPercentPhaseComplete',
'newDistributedDirectory',
'page_down',
'page_up',
'panda3d-render-error',
'PandaPaused',
'PandaRestarted',
'phaseComplete-3',
'press-mouse2-fade',
'print-fade',
'release-mouse2-fade',
'resetClock',
'window-event',
'TCRSetZoneDone',
'aspectRatioChanged',
'newDistributedDirectory',
CConnectionRepository.getOverflowEventName(),
self._getLostConnectionEvent(),
'render-texture-targets-changed',
'gotExtraFriendHandles']
if hasattr(loader, 'hook'):
allowedHooks.append(loader.hook)
if extraHooks is not None:
allowedHooks.extend(extraHooks)
problems = []
for hook in messenger.getEvents():
if hook not in allowedHooks:
problems.append(hook)
if problems:
msg = "You can't leave until you clean up your messenger hooks: {"
for hook in problems:
whoAccepts = messenger.whoAccepts(hook)
msg += '\n %s' % hook
for obj in whoAccepts:
msg += '\n OBJECT:%s, %s %s' % (obj, obj.__class__, whoAccepts[obj])
if hasattr(obj, 'getCreationStackTraceCompactStr'):
msg += '\n CREATIONSTACKTRACE:%s' % obj.getCreationStackTraceCompactStr()
else:
try:
value = whoAccepts[obj]
callback = value[0]
guiObj = callback.im_self
if hasattr(guiObj, 'getCreationStackTraceCompactStr'):
msg += '\n CREATIONSTACKTRACE:%s' % guiObj.getCreationStackTraceCompactStr()
except:
pass
msg += '\n}\n'
self.notify.warning(msg)
return len(problems)
else:
return 0
return
def detectLeakedIntervals(self):
numIvals = ivalMgr.getNumIntervals()
if numIvals > 0:
print "You can't leave until you clean up your intervals: {"
for i in range(ivalMgr.getMaxIndex()):
ival = None
if i < len(ivalMgr.ivals):
ival = ivalMgr.ivals[i]
if ival == None:
ival = ivalMgr.getCInterval(i)
if ival:
print ival
if hasattr(ival, 'debugName'):
print ival.debugName
if hasattr(ival, 'debugInitTraceback'):
print ival.debugInitTraceback
print '}'
self.notify.info("You can't leave until you clean up your intervals.")
return numIvals
else:
return 0
return
def _abandonShard(self):
self.notify.error('%s must override _abandonShard' % self.__class__.__name__)
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def enterGameOff(self):
self.uberZoneInterest = None
if not hasattr(self, 'cleanGameExit'):
self.cleanGameExit = True
if self.cleanGameExit:
if self.isShardInterestOpen():
self.notify.error('enterGameOff: shard interest is still open')
elif self.isShardInterestOpen():
self.notify.warning('unclean exit, abandoning shard')
self._abandonShard()
self.cleanupWaitAllInterestsComplete()
del self.cleanGameExit
self.cache.flush()
self.doDataCache.flush()
self.handler = self.handleMessageType
return
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def exitGameOff(self):
self.handler = None
return
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def enterWaitOnEnterResponses(self, shardId, hoodId, zoneId, avId):
self.cleanGameExit = False
if not self.astronSupport:
self.handler = self.handleWaitOnEnterResponses
self.handlerArgs = {'hoodId': hoodId,
'zoneId': zoneId,
'avId': avId}
if shardId is not None:
district = self.activeDistrictMap.get(shardId)
else:
district = None
if not district:
self.distributedDistrict = self.getStartingDistrict()
if self.distributedDistrict is None:
self.loginFSM.request('noShards')
return
shardId = self.distributedDistrict.doId
else:
self.distributedDistrict = district
self.notify.info('Entering shard %s' % shardId)
localAvatar.setLocation(shardId, zoneId)
base.localAvatar.defaultShard = shardId
self.waitForDatabaseTimeout(requestName='WaitOnEnterResponses')
self.handleSetShardComplete()
return
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def handleWaitOnEnterResponses(self, msgType, di):
if msgType == CLIENT_GET_FRIEND_LIST_RESP:
self.handleGetFriendsList(di)
elif msgType == CLIENT_GET_FRIEND_LIST_EXTENDED_RESP:
self.handleGetFriendsListExtended(di)
elif msgType == CLIENT_FRIEND_ONLINE:
self.handleFriendOnline(di)
elif msgType == CLIENT_FRIEND_OFFLINE:
self.handleFriendOffline(di)
elif msgType == CLIENT_GET_PET_DETAILS_RESP:
self.handleGetAvatarDetailsResp(di)
else:
self.handleMessageType(msgType, di)
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def handleSetShardComplete(self):
hoodId = self.handlerArgs['hoodId']
zoneId = self.handlerArgs['zoneId']
avId = self.handlerArgs['avId']
self.uberZoneInterest = self.addInterest(base.localAvatar.defaultShard, OTPGlobals.UberZone, 'uberZone', 'uberZoneInterestComplete')
self.acceptOnce('uberZoneInterestComplete', self.uberZoneInterestComplete)
self.waitForDatabaseTimeout(20, requestName='waitingForUberZone')
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def uberZoneInterestComplete(self):
self.__gotTimeSync = 0
self.cleanupWaitingForDatabase()
if self.timeManager == None:
self.notify.info('TimeManager is not present.')
DistributedSmoothNode.globalActivateSmoothing(0, 0)
self.gotTimeSync()
else:
DistributedSmoothNode.globalActivateSmoothing(1, 0)
h = HashVal()
hashPrcVariables(h)
pyc = HashVal()
if not __dev__:
self.hashFiles(pyc)
self.timeManager.d_setSignature(self.userSignature, h.asBin(), pyc.asBin())
self.timeManager.sendCpuInfo()
if self.timeManager.synchronize('startup'):
self.accept('gotTimeSync', self.gotTimeSync)
self.waitForDatabaseTimeout(requestName='uberZoneInterest-timeSync')
else:
self.notify.info('No sync from TimeManager.')
self.gotTimeSync()
return
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def exitWaitOnEnterResponses(self):
self.ignore('uberZoneInterestComplete')
self.cleanupWaitingForDatabase()
self.handler = None
self.handlerArgs = None
return
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def enterCloseShard(self, loginState = None):
self.notify.info('Exiting shard')
if loginState is None:
loginState = 'waitForAvatarList'
self._closeShardLoginState = loginState
base.cr.setNoNewInterests(True)
return
def _removeLocalAvFromStateServer(self):
self.sendSetAvatarIdMsg(0)
self._removeAllOV()
callback = Functor(self.loginFSM.request, self._closeShardLoginState)
if base.slowCloseShard:
taskMgr.doMethodLater(base.slowCloseShardDelay * 0.5, Functor(self.removeShardInterest, callback), 'slowCloseShard')
else:
self.removeShardInterest(callback)
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def _removeAllOV(self):
ownerDoIds = self.doId2ownerView.keys()
for doId in ownerDoIds:
self.disableDoId(doId, ownerView=True)
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def isShardInterestOpen(self):
self.notify.error('%s must override isShardInterestOpen' % self.__class__.__name__)
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def removeShardInterest(self, callback, task = None):
self._removeCurrentShardInterest(Functor(self._removeShardInterestComplete, callback))
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def _removeShardInterestComplete(self, callback):
self.cleanGameExit = True
self.cache.flush()
self.doDataCache.flush()
if base.slowCloseShard:
taskMgr.doMethodLater(base.slowCloseShardDelay * 0.5, Functor(self._callRemoveShardInterestCallback, callback), 'slowCloseShardCallback')
else:
self._callRemoveShardInterestCallback(callback, None)
return
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def _callRemoveShardInterestCallback(self, callback, task):
callback()
return Task.done
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def _removeCurrentShardInterest(self, callback):
self.notify.error('%s must override _removeCurrentShardInterest' % self.__class__.__name__)
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def exitCloseShard(self):
del self._closeShardLoginState
base.cr.setNoNewInterests(False)
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def enterTutorialQuestion(self, hoodId, zoneId, avId):
pass
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def exitTutorialQuestion(self):
pass
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def enterPlayGame(self, hoodId, zoneId, avId):
if self.music:
self.music.stop()
self.music = None
self.garbageLeakLogger = GarbageLeakServerEventAggregator(self)
self.handler = self.handlePlayGame
self.accept(self.gameDoneEvent, self.handleGameDone)
base.transitions.noFade()
self.playGame.load()
try:
loader.endBulkLoad('localAvatarPlayGame')
except:
pass
self.playGame.enter(hoodId, zoneId, avId)
def checkScale(task):
return Task.cont
return
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def handleGameDone(self):
if self.timeManager:
self.timeManager.setDisconnectReason(OTPGlobals.DisconnectSwitchShards)
doneStatus = self.playGame.getDoneStatus()
how = doneStatus['how']
shardId = doneStatus['shardId']
hoodId = doneStatus['hoodId']
zoneId = doneStatus['zoneId']
avId = doneStatus['avId']
if how == 'teleportIn':
self.gameFSM.request('switchShards', [shardId,
hoodId,
zoneId,
avId])
else:
self.notify.error('Exited shard with unexpected mode %s' % how)
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def exitPlayGame(self):
taskMgr.remove('globalScaleCheck')
self.handler = None
self.playGame.exit()
self.playGame.unload()
self.ignore(self.gameDoneEvent)
self.garbageLeakLogger.destroy()
del self.garbageLeakLogger
return
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def gotTimeSync(self):
self.notify.info('gotTimeSync')
self.ignore('gotTimeSync')
self.__gotTimeSync = 1
self.moveOnFromUberZone()
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def moveOnFromUberZone(self):
if not self.__gotTimeSync:
self.notify.info('Waiting for time sync.')
return
hoodId = self.handlerArgs['hoodId']
zoneId = self.handlerArgs['zoneId']
avId = self.handlerArgs['avId']
if not self.SupportTutorial or base.localAvatar.tutorialAck:
self.gameFSM.request('playGame', [hoodId, zoneId, avId])
elif base.config.GetBool('force-tutorial', 1):
if hasattr(self, 'skipTutorialRequest') and self.skipTutorialRequest:
self.gameFSM.request('playGame', [hoodId, zoneId, avId])
self.gameFSM.request('skipTutorialRequest', [hoodId, zoneId, avId])
else:
self.gameFSM.request('tutorialQuestion', [hoodId, zoneId, avId])
else:
self.gameFSM.request('playGame', [hoodId, zoneId, avId])
if not config.GetBool('astron-support', True):
def handlePlayGame(self, msgType, di):
if self.notify.getDebug():
self.notify.debug('handle play game got message type: ' + `msgType`)
if msgType == CLIENT_CREATE_OBJECT_REQUIRED:
self.handleGenerateWithRequired(di)
elif msgType == CLIENT_CREATE_OBJECT_REQUIRED_OTHER:
self.handleGenerateWithRequiredOther(di)
elif msgType == CLIENT_OBJECT_UPDATE_FIELD:
self.handleUpdateField(di)
elif msgType == CLIENT_OBJECT_DISABLE_RESP:
self.handleDisable(di)
elif msgType == CLIENT_OBJECT_DELETE_RESP:
self.handleDelete(di)
elif msgType == CLIENT_GET_FRIEND_LIST_RESP:
self.handleGetFriendsList(di)
elif msgType == CLIENT_GET_FRIEND_LIST_EXTENDED_RESP:
self.handleGetFriendsListExtended(di)
elif msgType == CLIENT_FRIEND_ONLINE:
self.handleFriendOnline(di)
elif msgType == CLIENT_FRIEND_OFFLINE:
self.handleFriendOffline(di)
elif msgType == CLIENT_GET_AVATAR_DETAILS_RESP:
self.handleGetAvatarDetailsResp(di)
elif msgType == CLIENT_GET_PET_DETAILS_RESP:
self.handleGetAvatarDetailsResp(di)
else:
self.handleMessageType(msgType, di)
else:
def handlePlayGame(self, msgType, di):
if self.notify.getDebug():
self.notify.debug('handle play game got message type: ' + `msgType`)
if self.__recordObjectMessage(msgType, di):
return
if msgType == CLIENT_ENTER_OBJECT_REQUIRED:
self.handleGenerateWithRequired(di)
elif msgType == CLIENT_ENTER_OBJECT_REQUIRED_OTHER:
self.handleGenerateWithRequired(di, other=True)
elif msgType == CLIENT_OBJECT_SET_FIELD:
self.handleUpdateField(di)
elif msgType == CLIENT_OBJECT_LEAVING:
self.handleDelete(di)
else:
self.handleMessageType(msgType, di)
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def enterSwitchShards(self, shardId, hoodId, zoneId, avId):
self._switchShardParams = [shardId,
hoodId,
zoneId,
avId]
localAvatar.setLeftDistrict()
self.removeShardInterest(self._handleOldShardGone)
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def _handleOldShardGone(self):
self.gameFSM.request('waitOnEnterResponses', self._switchShardParams)
@report(types=['args', 'deltaStamp'], dConfigParam='teleport')
def exitSwitchShards(self):
pass
def isFreeTimeExpired(self):
if self.accountOldAuth:
return 0
if base.config.GetBool('free-time-expired', 0):
return 1
if base.config.GetBool('unlimited-free-time', 0):
return 0
if self.freeTimeExpiresAt == -1:
return 0
if self.freeTimeExpiresAt == 0:
return 1
if self.freeTimeExpiresAt < -1:
self.notify.warning('freeTimeExpiresAt is less than -1 (%s)' % self.freeTimeExpiresAt)
if self.freeTimeExpiresAt < time.time():
return 1
else:
return 0
def freeTimeLeft(self):
if self.freeTimeExpiresAt == -1 or self.freeTimeExpiresAt == 0:
return 0
secsLeft = self.freeTimeExpiresAt - time.time()
return max(0, secsLeft)
def isWebPlayToken(self):
return self.playToken != None
def isBlue(self):
return self.blue != None
def isPaid(self):
paidStatus = base.config.GetString('force-paid-status', '')
if not paidStatus:
return self.__isPaid
elif paidStatus == 'paid':
return 1
elif paidStatus == 'unpaid':
return 0
elif paidStatus == 'FULL':
return OTPGlobals.AccessFull
elif paidStatus == 'VELVET':
return OTPGlobals.AccessVelvetRope
else:
return 0
def setIsPaid(self, isPaid):
self.__isPaid = isPaid
def allowFreeNames(self):
return base.config.GetInt('allow-free-names', 1)
def allowSecretChat(self):
return self.secretChatAllowed or self.productName == 'Terra-DMC' and self.isBlue() and self.secretChatAllowed
def allowWhiteListChat(self):
if hasattr(self, 'whiteListChatEnabled') and self.whiteListChatEnabled:
return True
else:
return False
def allowAnyTypedChat(self):
if self.allowSecretChat() or self.allowWhiteListChat() or self.allowOpenChat():
return True
else:
return False
def allowOpenChat(self):
return self.openChatAllowed
def isParentPasswordSet(self):
return self.parentPasswordSet
def needParentPasswordForSecretChat(self):
return self.isPaid() and self.secretChatNeedsParentPassword or self.productName == 'Terra-DMC' and self.isBlue() and self.secretChatNeedsParentPassword
def logAccountInfo(self):
self.notify.info('*** ACCOUNT INFO ***')
self.notify.info('username: %s' % self.userName)
if base.logPrivateInfo:
if self.blue:
self.notify.info('paid: %s (blue)' % self.isPaid())
else:
self.notify.info('paid: %s' % self.isPaid())
if not self.isPaid():
if self.isFreeTimeExpired():
self.notify.info('free time is expired')
else:
secs = self.freeTimeLeft()
self.notify.info('free time left: %s' % PythonUtil.formatElapsedSeconds(secs))
if self.periodTimerSecondsRemaining != None:
self.notify.info('period time left: %s' % PythonUtil.formatElapsedSeconds(self.periodTimerSecondsRemaining))
return
def getStartingDistrict(self):
district = None
if len(self.activeDistrictMap.keys()) == 0:
self.notify.info('no shards')
return
if base.fillShardsToIdealPop:
lowPop, midPop, highPop = base.getShardPopLimits()
self.notify.debug('low: %s mid: %s high: %s' % (lowPop, midPop, highPop))
for s in self.activeDistrictMap.values():
if s.available and s.avatarCount < lowPop:
self.notify.debug('%s: pop %s' % (s.name, s.avatarCount))
if district is None:
district = s
elif s.avatarCount > district.avatarCount or s.avatarCount == district.avatarCount and s.name > district.name:
district = s
if district is None:
self.notify.debug('all shards over cutoff, picking lowest-population shard')
for s in self.activeDistrictMap.values():
if s.available:
self.notify.debug('%s: pop %s' % (s.name, s.avatarCount))
if district is None or s.avatarCount < district.avatarCount:
district = s
if district is not None:
self.notify.debug('chose %s: pop %s' % (district.name, district.avatarCount))
return district
def getShardName(self, shardId):
try:
return self.activeDistrictMap[shardId].name
except:
return None
return None
def isShardAvailable(self, shardId):
try:
return self.activeDistrictMap[shardId].available
except:
return 0
def listActiveShards(self):
list = []
for s in self.activeDistrictMap.values():
if s.available:
list.append((s.doId,
s.name,
s.avatarCount,
s.newAvatarCount))
return list
def getPlayerAvatars(self):
return [ i for i in self.doId2do.values() if isinstance(i, DistributedPlayer) ]
def queryObjectField(self, dclassName, fieldName, doId, context = 0):
dclass = self.dclassesByName.get(dclassName)
if dclass is not None:
fieldId = dclass.getFieldByName(fieldName).getNumber()
self.queryObjectFieldId(doId, fieldId, context)
return
def allocateDcFile(self):
dcName = 'Shard %s cannot be found.'
hash = HashVal()
hash.hashString(dcName)
self.http.setClientCertificatePassphrase(hash.asHex())
def lostConnection(self):
ClientRepositoryBase.lostConnection(self)
self.loginFSM.request('noConnection')
def waitForDatabaseTimeout(self, extraTimeout = 0, requestName = 'unknown'):
OTPClientRepository.notify.debug('waiting for database timeout %s at %s' % (requestName, globalClock.getFrameTime()))
self.cleanupWaitingForDatabase()
globalClock.tick()
taskMgr.doMethodLater((OTPGlobals.DatabaseDialogTimeout + extraTimeout) * choice(__dev__, 10, 1), self.__showWaitingForDatabase, 'waitingForDatabase', extraArgs=[requestName])
def cleanupWaitingForDatabase(self):
if self.waitingForDatabase:
self.waitingForDatabase.hide()
self.waitingForDatabase.cleanup()
self.waitingForDatabase = None
taskMgr.remove('waitingForDatabase')
return
def __showWaitingForDatabase(self, requestName):
messenger.send('connectionIssue')
OTPClientRepository.notify.info('timed out waiting for %s at %s' % (requestName, globalClock.getFrameTime()))
dialogClass = OTPGlobals.getDialogClass()
self.waitingForDatabase = dialogClass(text=OTPLocalizer.CRToontownUnavailable, dialogName='WaitingForDatabase', buttonTextList=[OTPLocalizer.CRToontownUnavailableCancel], style=OTPDialog.CancelOnly, command=self.__handleCancelWaiting)
self.waitingForDatabase.show()
taskMgr.remove('waitingForDatabase')
taskMgr.doMethodLater(OTPGlobals.DatabaseGiveupTimeout, self.__giveUpWaitingForDatabase, 'waitingForDatabase', extraArgs=[requestName])
return Task.done
def __giveUpWaitingForDatabase(self, requestName):
OTPClientRepository.notify.info('giving up waiting for %s at %s' % (requestName, globalClock.getFrameTime()))
self.cleanupWaitingForDatabase()
self.loginFSM.request('noConnection')
return Task.done
def __handleCancelWaiting(self, value):
self.loginFSM.request('shutdown')
def setIsNotNewInstallation(self):
launcher.setIsNotNewInstallation()
def renderFrame(self):
gsg = base.win.getGsg()
if gsg:
render2d.prepareScene(gsg)
base.graphicsEngine.renderFrame()
def refreshAccountServerDate(self, forceRefresh = 0):
try:
self.accountServerDate.grabDate(force=forceRefresh)
except TTAccount.TTAccountException, e:
self.notify.debug(str(e))
return 1
def resetPeriodTimer(self, secondsRemaining):
self.periodTimerExpired = 0
self.periodTimerSecondsRemaining = secondsRemaining
def recordPeriodTimer(self, task):
freq = 60.0
elapsed = globalClock.getRealTime() - self.periodTimerStarted
self.runningPeriodTimeRemaining = self.periodTimerSecondsRemaining - elapsed
self.notify.debug('periodTimeRemaining: %s' % self.runningPeriodTimeRemaining)
launcher.recordPeriodTimeRemaining(self.runningPeriodTimeRemaining)
taskMgr.doMethodLater(freq, self.recordPeriodTimer, 'periodTimerRecorder')
return Task.done
def startPeriodTimer(self):
if self.periodTimerStarted == None and self.periodTimerSecondsRemaining != None:
self.periodTimerStarted = globalClock.getRealTime()
taskMgr.doMethodLater(self.periodTimerSecondsRemaining, self.__periodTimerExpired, 'periodTimerCountdown')
for warning in OTPGlobals.PeriodTimerWarningTime:
if self.periodTimerSecondsRemaining > warning:
taskMgr.doMethodLater(self.periodTimerSecondsRemaining - warning, self.__periodTimerWarning, 'periodTimerCountdown')
self.runningPeriodTimeRemaining = self.periodTimerSecondsRemaining
self.recordPeriodTimer(None)
return
def stopPeriodTimer(self):
if self.periodTimerStarted != None:
elapsed = globalClock.getRealTime() - self.periodTimerStarted
self.periodTimerSecondsRemaining -= elapsed
self.periodTimerStarted = None
taskMgr.remove('periodTimerCountdown')
taskMgr.remove('periodTimerRecorder')
return
def __periodTimerWarning(self, task):
base.localAvatar.setSystemMessage(0, OTPLocalizer.PeriodTimerWarning)
return Task.done
def __periodTimerExpired(self, task):
self.notify.info("User's period timer has just expired!")
self.stopPeriodTimer()
self.periodTimerExpired = 1
self.periodTimerStarted = None
self.periodTimerSecondsRemaining = None
messenger.send('periodTimerExpired')
return Task.done
if not config.GetBool('astron-support', True):
def handleMessageType(self, msgType, di):
if msgType == CLIENT_GO_GET_LOST:
self.handleGoGetLost(di)
elif msgType == CLIENT_HEARTBEAT:
self.handleServerHeartbeat(di)
elif msgType == CLIENT_SYSTEM_MESSAGE:
self.handleSystemMessage(di)
elif msgType == CLIENT_SYSTEMMESSAGE_AKNOWLEDGE:
self.handleSystemMessageAknowledge(di)
elif msgType == CLIENT_CREATE_OBJECT_REQUIRED:
self.handleGenerateWithRequired(di)
elif msgType == CLIENT_CREATE_OBJECT_REQUIRED_OTHER:
self.handleGenerateWithRequiredOther(di)
elif msgType == CLIENT_CREATE_OBJECT_REQUIRED_OTHER_OWNER:
self.handleGenerateWithRequiredOtherOwner(di)
elif msgType == CLIENT_OBJECT_UPDATE_FIELD:
self.handleUpdateField(di)
elif msgType == CLIENT_OBJECT_DISABLE:
self.handleDisable(di)
elif msgType == CLIENT_OBJECT_DISABLE_OWNER:
self.handleDisable(di, ownerView=True)
elif msgType == CLIENT_OBJECT_DELETE_RESP:
self.handleDelete(di)
elif msgType == CLIENT_DONE_INTEREST_RESP:
self.gotInterestDoneMessage(di)
elif msgType == CLIENT_GET_STATE_RESP:
pass
elif msgType == CLIENT_OBJECT_LOCATION:
self.gotObjectLocationMessage(di)
elif msgType == CLIENT_SET_WISHNAME_RESP:
self.gotWishnameResponse(di)
else:
currentLoginState = self.loginFSM.getCurrentState()
if currentLoginState:
currentLoginStateName = currentLoginState.getName()
else:
currentLoginStateName = 'None'
currentGameState = self.gameFSM.getCurrentState()
if currentGameState:
currentGameStateName = currentGameState.getName()
else:
currentGameStateName = 'None'
else:
def handleMessageType(self, msgType, di):
if self.__recordObjectMessage(msgType, di):
return
if msgType == CLIENT_EJECT:
self.handleGoGetLost(di)
elif msgType == CLIENT_HEARTBEAT:
self.handleServerHeartbeat(di)
elif msgType == CLIENT_ENTER_OBJECT_REQUIRED:
self.handleGenerateWithRequired(di)
elif msgType == CLIENT_ENTER_OBJECT_REQUIRED_OTHER:
self.handleGenerateWithRequired(di, other=True)
elif msgType == CLIENT_ENTER_OBJECT_REQUIRED_OTHER_OWNER:
self.handleGenerateWithRequiredOtherOwner(di)
elif msgType == CLIENT_OBJECT_SET_FIELD:
self.handleUpdateField(di)
elif msgType == CLIENT_OBJECT_LEAVING:
self.handleDisable(di)
elif msgType == CLIENT_OBJECT_LEAVING_OWNER:
self.handleDisable(di, ownerView=True)
elif msgType == CLIENT_DONE_INTEREST_RESP:
self.gotInterestDoneMessage(di)
elif msgType == CLIENT_OBJECT_LOCATION:
self.gotObjectLocationMessage(di)
else:
currentLoginState = self.loginFSM.getCurrentState()
if currentLoginState:
currentLoginStateName = currentLoginState.getName()
else:
currentLoginStateName = 'None'
currentGameState = self.gameFSM.getCurrentState()
if currentGameState:
currentGameStateName = currentGameState.getName()
else:
currentGameStateName = 'None'
def gotInterestDoneMessage(self, di):
if self.deferredGenerates:
dg = Datagram(di.getDatagram())
di = DatagramIterator(dg, di.getCurrentIndex())
self.deferredGenerates.append((CLIENT_DONE_INTEREST_RESP, (dg, di)))
else:
if self.astronSupport:
# Play back generates, if necessary.
# First, create a new DatagramIterator using
# the Datagram from DatagramIterator di, and
# the current index of DatagramIterator di:
di2 = DatagramIterator(di.getDatagram(), di.getCurrentIndex())
# Get the context. This is never actually used,
# however none of this will work unless we get it.
ctx = di2.getUint32()
# Now, get the handle:
handle = di2.getUint16()
# Finally, play back the generates:
self.__playBackGenerates(handle)
self.handleInterestDoneMessage(di)
def gotObjectLocationMessage(self, di):
if self.deferredGenerates:
dg = Datagram(di.getDatagram())
di = DatagramIterator(dg, di.getCurrentIndex())
di2 = DatagramIterator(dg, di.getCurrentIndex())
doId = di2.getUint32()
if doId in self.deferredDoIds:
self.deferredDoIds[doId][3].append((CLIENT_OBJECT_LOCATION, (dg, di)))
else:
self.handleObjectLocation(di)
else:
self.handleObjectLocation(di)
def sendWishName(self, avId, name):
datagram = PyDatagram()
datagram.addUint16(CLIENT_SET_WISHNAME)
datagram.addUint32(avId)
datagram.addString(name)
self.send(datagram)
def sendWishNameAnonymous(self, name):
self.sendWishName(0, name)
def getWishNameResultMsg(self):
return 'OTPCR.wishNameResult'
def gotWishnameResponse(self, di):
avId = di.getUint32()
returnCode = di.getUint16()
pendingName = ''
approvedName = ''
rejectedName = ''
if returnCode == 0:
pendingName = di.getString()
approvedName = di.getString()
rejectedName = di.getString()
if approvedName:
name = approvedName
elif pendingName:
name = pendingName
elif rejectedName:
name = rejectedName
else:
name = ''
WNR = self.WishNameResult
if returnCode:
result = WNR.Failure
elif rejectedName:
result = WNR.Rejected
elif pendingName:
result = WNR.PendingApproval
elif approvedName:
result = WNR.Approved
messenger.send(self.getWishNameResultMsg(), [result, avId, name])
def replayDeferredGenerate(self, msgType, extra):
if msgType == CLIENT_DONE_INTEREST_RESP:
dg, di = extra
self.handleInterestDoneMessage(di)
elif msgType == CLIENT_OBJECT_LOCATION:
dg, di = extra
self.handleObjectLocation(di)
else:
ClientRepositoryBase.replayDeferredGenerate(self, msgType, extra)
@exceptionLogged(append=False)
def handleDatagram(self, di):
if self.notify.getDebug():
print 'ClientRepository received datagram:'
di.getDatagram().dumpHex(ostream)
msgType = self.getMsgType()
if msgType == 65535:
self.lostConnection()
return
if self.handler == None:
self.handleMessageType(msgType, di)
else:
self.handler(msgType, di)
self.considerHeartbeat()
return
def askAvatarKnown(self, avId):
return 0
def hashFiles(self, pyc):
for dir in sys.path:
if dir == '':
dir = '.'
if os.path.isdir(dir):
for filename in os.listdir(dir):
if filename.endswith('.pyo') or filename.endswith('.pyc') or filename.endswith('.py') or filename == 'library.zip':
pathname = Filename.fromOsSpecific(os.path.join(dir, filename))
hv = HashVal()
hv.hashFile(pathname)
pyc.mergeWith(hv)
def queueRequestAvatarInfo(self, avId):
pass
def identifyFriend(self, doId):
pass
def identifyPlayer(self, playerId):
pass
def identifyAvatar(self, doId):
info = self.doId2do.get(doId)
if info:
return info
else:
info = self.identifyFriend(doId)
return info
def sendDisconnect(self):
if self.isConnected():
datagram = PyDatagram()
datagram.addUint16(CLIENT_DISCONNECT)
self.send(datagram)
self.notify.info('Sent disconnect message to server')
self.disconnect()
self.stopHeartbeat()
def _isPlayerDclass(self, dclass):
return False
def _isValidPlayerLocation(self, parentId, zoneId):
return True
def _isInvalidPlayerAvatarGenerate(self, doId, dclass, parentId, zoneId):
if self._isPlayerDclass(dclass):
if not self._isValidPlayerLocation(parentId, zoneId):
base.cr.centralLogger.writeClientEvent('got generate for player avatar %s in invalid location (%s, %s)' % (doId, parentId, zoneId))
return True
return False
if not config.GetBool('astron-support', True):
def handleGenerateWithRequired(self, di):
parentId = di.getUint32()
zoneId = di.getUint32()
classId = di.getUint16()
doId = di.getUint32()
dclass = self.dclassesByNumber[classId]
if self._isInvalidPlayerAvatarGenerate(doId, dclass, parentId, zoneId):
return
dclass.startGenerate()
distObj = self.generateWithRequiredFields(dclass, doId, di, parentId, zoneId)
dclass.stopGenerate()
def handleGenerateWithRequiredOther(self, di):
parentId = di.getUint32()
zoneId = di.getUint32()
classId = di.getUint16()
doId = di.getUint32()
dclass = self.dclassesByNumber[classId]
if self._isInvalidPlayerAvatarGenerate(doId, dclass, parentId, zoneId):
return
deferrable = getattr(dclass.getClassDef(), 'deferrable', False)
if not self.deferInterval or self.noDefer:
deferrable = False
now = globalClock.getFrameTime()
if self.deferredGenerates or deferrable:
if self.deferredGenerates or now - self.lastGenerate < self.deferInterval:
self.deferredGenerates.append((CLIENT_CREATE_OBJECT_REQUIRED_OTHER, doId))
dg = Datagram(di.getDatagram())
di = DatagramIterator(dg, di.getCurrentIndex())
self.deferredDoIds[doId] = ((parentId,
zoneId,
classId,
doId,
di),
deferrable,
dg,
[])
if len(self.deferredGenerates) == 1:
taskMgr.remove('deferredGenerate')
taskMgr.doMethodLater(self.deferInterval, self.doDeferredGenerate, 'deferredGenerate')
else:
self.lastGenerate = now
self.doGenerate(parentId, zoneId, classId, doId, di)
else:
self.doGenerate(parentId, zoneId, classId, doId, di)
def handleGenerateWithRequiredOtherOwner(self, di):
classId = di.getUint16()
doId = di.getUint32()
parentId = di.getUint32()
zoneId = di.getUint32()
dclass = self.dclassesByNumber[classId]
dclass.startGenerate()
distObj = self.generateWithRequiredOtherFieldsOwner(dclass, doId, di)
dclass.stopGenerate()
def handleQuietZoneGenerateWithRequired(self, di):
parentId = di.getUint32()
zoneId = di.getUint32()
classId = di.getUint16()
doId = di.getUint32()
dclass = self.dclassesByNumber[classId]
dclass.startGenerate()
distObj = self.generateWithRequiredFields(dclass, doId, di, parentId, zoneId)
dclass.stopGenerate()
def handleQuietZoneGenerateWithRequiredOther(self, di):
parentId = di.getUint32()
zoneId = di.getUint32()
classId = di.getUint16()
doId = di.getUint32()
dclass = self.dclassesByNumber[classId]
dclass.startGenerate()
distObj = self.generateWithRequiredOtherFields(dclass, doId, di, parentId, zoneId)
dclass.stopGenerate()
else:
def handleGenerateWithRequired(self, di, other=False):
doId = di.getUint32()
parentId = di.getUint32()
zoneId = di.getUint32()
classId = di.getUint16()
# Determine whether or not we should add this generate
# to the pending generates, or just generate it right away.
for handle, interest in self._interests.items():
if parentId != interest.parentId:
continue
if isinstance(interest.zoneIdList, list):
if zoneId not in interest.zoneIdList:
continue
else:
if zoneId != interest.zoneIdList:
continue
break
else:
self.notify.warning('Received generate for %d from %d:%d, which is not a part of any existing interests!' % (doId, parentId, zoneId))
interest = None
if not interest or not interest.events:
# Generate this object right away.
return self.__generateObject(doId, parentId, zoneId, classId, di, other)
# Wait on the interest events to complete before generating.
pending = self.__pendingGenerates.setdefault(handle, [])
pending.append((doId, parentId, zoneId, classId, Datagram(di.getDatagram()), other))
self.__doId2pendingInterest[doId] = handle
def __playBackGenerates(self, handle):
if handle not in self.__pendingGenerates:
# Nothing to play back!
return
# We will now play back this interest's pending generates.
# First, get a copy of the pending generates, and remove
# them from self.__pendingGenerates:
generates = self.__pendingGenerates[handle]
del self.__pendingGenerates[handle]
# Sort generates by classId:
generates.sort(key=lambda x: x[3])
# Generate the objects:
for doId, parentId, zoneId, classId, dg, other in generates:
# Set up a DatagramIterator using Datagram dg:
di = DatagramIterator(dg)
# Skip 16 bytes to move past the header.
# For the record: MsgType(2), zoneId, doId, parentId (3x4), classId (2)
di.skipBytes(16)
# Generate the object:
self.__generateObject(doId, parentId, zoneId, classId, di, other)
# Delete this object's doId from
# __doId2pendingInterest if it exists:
if doId in self.__doId2pendingInterest:
del self.__doId2pendingInterest[doId]
# Now that we have generated the object, if there
# are any messages to play back, do so now:
self.__playBackMessages(handle)
def __playBackMessages(self, handle):
if handle not in self.__pendingMessages:
# Nothing to play back!
return
# We will now play back any pending messages.
# First, loop through all the Datagram instances
# in __pendingMessages:
for dg in self.__pendingMessages[handle]:
# Set up a DatagramIterator using Datagram dg:
di = DatagramIterator(dg)
# Get the msgType:
msgType = di.getUint16()
# If self.handler is set, use that. Otherwise,
# use self.handleMessageType:
if self.handler:
self.handler(msgType, di)
else:
self.handleMessageType(msgType, di)
# We can now remove the handle from __pendingMessages:
del self.__pendingMessages[handle]
def __recordObjectMessage(self, msgType, di):
if msgType not in (CLIENT_OBJECT_SET_FIELD, CLIENT_OBJECT_LEAVING, CLIENT_OBJECT_LOCATION):
return False
di2 = DatagramIterator(di.getDatagram(), di.getCurrentIndex())
doId = di2.getUint32()
if doId not in self.__doId2pendingInterest:
return False
pending = self.__pendingMessages.setdefault(self.__doId2pendingInterest[doId], [])
pending.append(Datagram(di.getDatagram()))
return True
def __generateObject(self, doId, parentId, zoneId, classId, di, other):
# Get our dclass:
dclass = self.dclassesByNumber[classId]
# Is this an invalid player avatar generate?
if self._isInvalidPlayerAvatarGenerate(doId, dclass, parentId, zoneId):
# Yup, ignore this.
return
# Start the generation process:
dclass.startGenerate()
# Is this an other generate?
if other:
# Yup. In this case, we'll use generateWithRequiredOtherFields.
distObj = self.generateWithRequiredOtherFields(dclass, doId, di, parentId, zoneId)
else:
# Nah. In this case, we'll use generateWithRequiredFields.
distObj = self.generateWithRequiredFields(dclass, doId, di, parentId, zoneId)
# We're done.
dclass.stopGenerate()
def handleGenerateWithRequiredOtherOwner(self, di):
doId = di.getUint32()
parentId = di.getUint32()
zoneId = di.getUint32()
classId = di.getUint16()
dclass = self.dclassesByNumber[classId]
dclass.startGenerate()
distObj = self.generateWithRequiredOtherFieldsOwner(dclass, doId, di)
dclass.stopGenerate()
def handleQuietZoneGenerateWithRequired(self, di):
doId = di.getUint32()
parentId = di.getUint32()
zoneId = di.getUint32()
classId = di.getUint16()
dclass = self.dclassesByNumber[classId]
dclass.startGenerate()
distObj = self.generateWithRequiredFields(dclass, doId, di, parentId, zoneId)
dclass.stopGenerate()
def handleQuietZoneGenerateWithRequiredOther(self, di):
doId = di.getUint32()
parentId = di.getUint32()
zoneId = di.getUint32()
classId = di.getUint16()
dclass = self.dclassesByNumber[classId]
dclass.startGenerate()
distObj = self.generateWithRequiredOtherFields(dclass, doId, di, parentId, zoneId)
dclass.stopGenerate()
def handleDisable(self, di, ownerView = False):
doId = di.getUint32()
if not self.isLocalId(doId):
self.disableDoId(doId, ownerView)
def sendSetLocation(self, doId, parentId, zoneId):
datagram = PyDatagram()
datagram.addUint16(CLIENT_OBJECT_LOCATION)
datagram.addUint32(doId)
datagram.addUint32(parentId)
datagram.addUint32(zoneId)
self.send(datagram)
def sendHeartbeat(self):
datagram = PyDatagram()
datagram.addUint16(CLIENT_HEARTBEAT)
self.send(datagram)
self.lastHeartbeat = globalClock.getRealTime()
self.considerFlush()
def isLocalId(self, id):
try:
return localAvatar.doId == id
except:
self.notify.debug('In isLocalId(), localAvatar not created yet')
return False
ITAG_PERM = 'perm'
ITAG_AVATAR = 'avatar'
ITAG_SHARD = 'shard'
ITAG_WORLD = 'world'
ITAG_GAME = 'game'
def addTaggedInterest(self, parentId, zoneId, mainTag, desc, otherTags = [], event = None):
return self.addInterest(parentId, zoneId, desc, event)