from panda3d.core import * from direct.directnotify.DirectNotifyGlobal import directNotify from direct.distributed.DistributedObjectAI import DistributedObjectAI from direct.showbase.DirectObject import DirectObject from direct.task import Task from otp.distributed import OtpDoGlobals from toontown.coderedemption import TTCodeRedemptionConsts import random import string class TTCRMAIRetryMgr(DirectObject): notify = directNotify.newCategory('TTCodeRedemptionMgrAI') MinRetryPeriod = 5 RetryGrowMult = 1.1 def __init__(self, air, codeRedemptionMgr): self.air = air self._codeRedemptionMgr = codeRedemptionMgr self._serialGen = SerialNumGen() self._retryPeriod = self.MinRetryPeriod self._redemptions = {} def addRedemption(self, avId, context, code): assert self.notify.debugCall() serial = self._serialGen.next() self._redemptions[serial] = ScratchPad(avId=avId, context=context, code=code, attemptNum=0) self._doRedemption(serial, True) def resolveRedemption(self, serial, context, avId, result, awardMgrResult): assert self.notify.debugCall() if serial not in self._redemptions: self.notify.warning('unexpected redemption resolution: %s, %s, %s, %s, %s' % ( serial, context, avId, result, awardMgrResult)) return info = self._redemptions.pop(serial) info.doLater.remove() self._retryPeriod = self.MinRetryPeriod if hasattr(self, '_stressTestInfo'): if (avId, context) in self._stressTestInfo.redemptions: code = self._stressTestInfo.redemptions.pop((avId, context)) self._stressTestInfo.numCodesResolved += 1 if result: if not TTCodeRedemptionMgrAI.RandomizeStressTestCode: self.notify.info('stress test redemption failed for %s (%s): %s, %s' % ( avId, code, result, awardMgrResult)) else: self.notify.debug('stress test redemption succeeded for %s (%s)' % ( avId, code)) now = globalClock.getRealTime() if (now - self._stressTestInfo.lastLogT) > 10: duration = now - self._stressTestInfo.startT self._stressTestInfo.lastLogT = now self.notify.info('stress test progress: %s codes resolved, %s codes/sec' % ( self._stressTestInfo.numCodesResolved, (self._stressTestInfo.numCodesResolved / duration))) self._checkCleanupStressTest() self._codeRedemptionMgr.sendUpdateToAvatarId(avId, 'redeemCodeResult', [context, result, awardMgrResult]) def _doRedemption(self, serial, directCall, task=None): info = self._redemptions.get(serial) info.attemptNum += 1 if info.attemptNum > 1: self._retryPeriod = max(self.MinRetryPeriod, self._retryPeriod * self.RetryGrowMult) self.notify.info('code redemption retry #%s for %s: %s' % ((info.attemptNum-1), info.avId, info.code)) self.air.sendUpdateToDoId('TTCodeRedemptionMgr', 'redeemCodeAiToUd', OtpDoGlobals.OTP_DO_ID_TOONTOWN_CODE_REDEMPTION_MANAGER, [serial, self._codeRedemptionMgr.doId, info.context, info.code, info.avId] ) info.doLater = self.doMethodLater(self._retryPeriod, Functor(self._doRedemption, serial), uniqueName('CodeRedemptionRetry')) return Task.done # stress test API def startStressTest(self): assert not hasattr(self, '_stressTestClosed') self._stressTestInfo = ScratchPad() self._stressTestInfo.redemptions = {} self._stressTestInfo.numCodesSubmitted = 0 self._stressTestInfo.numCodesResolved = 0 self._stressTestInfo.startT = globalClock.getRealTime() self._stressTestInfo.lastLogT = globalClock.getRealTime() self._stressTestInfo.closed = False def finishStressTestSubmission(self): self._stressTestInfo.closed = True self._checkCleanupStressTest() def _checkCleanupStressTest(self): if self._stressTestInfo.closed and ( self._stressTestInfo.numCodesSubmitted == self._stressTestInfo.numCodesResolved): duration = globalClock.getRealTime() - self._stressTestInfo.startT self.notify.info('stress test resolution completed: %s codes resolved, %s codes/sec' % ( self._stressTestInfo.numCodesResolved, self._stressTestInfo.numCodesResolved / duration)) del self._stressTestInfo def addStressTestRedemption(self, avId, context, code): self._stressTestInfo.redemptions[(avId, context)] = code self._stressTestInfo.numCodesSubmitted += 1 self.addRedemption(avId, context, code) class TTCodeRedemptionMgrAI(DistributedObjectAI): notify = directNotify.newCategory('TTCodeRedemptionMgrAI') WantStressTest = ConfigVariableBool('stress-test-code-redemption', False).getValue() StressTestRate = ConfigVariableDouble('stress-test-code-redemption-rate', 3.).getValue() RandomizeStressTestCode = ConfigVariableBool('randomize-code-redemption-stress-test-code', False).getValue() def __init__(self, air): DistributedObjectAI.__init__(self, air) self._retryMgr = TTCRMAIRetryMgr(self.air, self) self.air.codeRedemptionManager = self if self.WantStressTest: printStack() taskMgr.doMethodLater(10., self._doStressTest, uniqueName('codeRedemptionStartStressTest')) def announceGenerate(self): DistributedObjectAI.announceGenerate(self) def redeemCode(self, context, code): assert self.notify.debugCall() # pass it on to the UD w/out checking the content of the parameters; offload the # CPU work to the code redemption UD avId = self.air.getAvatarIdFromSender() self._retryMgr.addRedemption(avId, context, code) def redeemCodeResultUdToAi(self, serial, context, avId, result, awardMgrResult): assert self.notify.debugCall() # pass it back to the toon self._retryMgr.resolveRedemption(serial, context, avId, result, awardMgrResult) def _doStressTest(self, task): self._stressTestCode = 'stresstest' fPath = 'toontown/coderedemption/StressTestAvIds.txt' self._stressTestFile = open(fPath) self._stressTestLastSendT = globalClock.getFrameTime() self._stressTestStartTime = globalClock.getRealTime() self._stressTestAvCount = 0 self._stressTestLogT = globalClock.getRealTime() self.notify.info('starting stress test') self._retryMgr.startStressTest() taskMgr.add(self._stressTest, uniqueName('codeRedemptionStressTest')) return task.done def _stressTest(self, task): now = globalClock.getFrameTime() dt = now - self._stressTestLastSendT numCodes = max(0, int(dt * self.StressTestRate)) self._stressTestLastSendT += (numCodes / float(self.StressTestRate)) done = False while numCodes: try: line = self._stressTestFile.readline() except: raise done = True #print line if not done: try: avId = int(line) except ValueError: done = True if done: rate = self._stressTestAvCount / (globalClock.getRealTime() - self._stressTestStartTime) self.notify.info('stress test submission complete: %s codes submitted, %s codes/sec' % ( self._stressTestAvCount, rate)) self._retryMgr.finishStressTestSubmission() return task.done if self.RandomizeStressTestCode: len = random.randrange(1, 20) code = '' while len: code += random.choice(string.letters) len -= 1 else: code = self._stressTestCode self._retryMgr.addStressTestRedemption(avId, 0, code) numCodes -= 1 self._stressTestAvCount += 1 now = globalClock.getRealTime() if (now - self._stressTestLogT) > 10: self._stressTestLogT = now rate = self._stressTestAvCount / (globalClock.getRealTime() - self._stressTestStartTime) self.notify.info('stress test progress: %s codes submitted, %s codes/sec' % ( self._stressTestAvCount, rate)) return task.cont