Exemplo n.º 1
0
    def __init__(self, air):
        """Construct ourselves, set up web dispatcher."""
        assert self.notify.debugCall()
        DistributedObjectGlobalUD.__init__(self, air)

        self.air = air

        self._dcRequestSerialGen = SerialNumGen(1)
        self._dcId2info = {}

        self.HTTPListenPort = uber.awardManagerHTTPListenPort

        self.numServed = 0

        self.webDispatcher = WebRequestDispatcher()
        self.webDispatcher.landingPage.setTitle("AwardManager")
        self.webDispatcher.landingPage.setDescription(
            "AwardManager is a REST-like interface allowing in-game awards from other services."
        )
        self.webDispatcher.registerGETHandler('awardMgr', self.awardMgr)
        self.webDispatcher.registerGETHandler('awardGive', self.giveAward)
        self.webDispatcher.listenOnPort(self.HTTPListenPort)
        self.webDispatcher.landingPage.addTab("AwardMgr", "/awardMgr")

        self.air.setConnectionName("AwardMgr")
        self.air.setConnectionURL(
            "http://%s:%s/" %
            (socket.gethostbyname(socket.gethostname()), self.HTTPListenPort))
        self.awardChoices = self.getAwardChoices(
        )  # award Choices is a dict of dicts
        self.reverseDictAwardChoices = self.getReversedAwardChoices()
    def __init__(self, air):
        """Construct ourselves, set up web dispatcher."""
        assert self.notify.debugCall()
        DistributedObjectGlobalUD.__init__(self, air)
        self.HTTPListenPort = uber.inGameNewsMgrHTTPListenPort

        self.webDispatcher = WebRequestDispatcher()
        self.webDispatcher.landingPage.setTitle("InGameNewsMgr")
        self.webDispatcher.landingPage.setDescription(
            "InGameNews is update when a new issue of in-game-news is out.")
        self.webDispatcher.registerGETHandler('inGameNewsMgr',
                                              self.inGameNewsMgr)
        self.webDispatcher.registerGETHandler('inGameNewsNewIssue',
                                              self.inGameNewsNewIssue)
        self.webDispatcher.listenOnPort(self.HTTPListenPort)
        self.webDispatcher.landingPage.addTab("InGameNewsMgr",
                                              "/inGameNewsMgr")

        self.air.setConnectionName("InGameNewsMgr")
        self.air.setConnectionURL(
            "http://%s:%s/" %
            (socket.gethostbyname(socket.gethostname()), self.HTTPListenPort))

        self.filename = self.getFilename()
        self.latestIssue = datetime.datetime.now()
        self.latestIssue = self.loadRecords()
    def __init__(self, air):
        assert self.notify.debugCall()
        DistributedObjectGlobalUD.__init__(self, air)

        self.air = air

        self.HTTPListenPort = uber.RATManagerHTTPListenPort

        self.numServed = 0

        self.webDispatcher = WebRequestDispatcher()
        self.webDispatcher.landingPage.setTitle("RATManager")
        self.webDispatcher.landingPage.setDescription(
            "RATManager is a REST-like interface allowing in-game awards from other services."
        )
        self.webDispatcher.registerGETHandler("getToonList",
                                              self.handleHTTPGetToonList)
        self.webDispatcher.registerGETHandler("giveToonBeansRAT",
                                              self.handleHTTPGiveToonBeansRAT)
        self.webDispatcher.registerGETHandler("giveToonBeansCS",
                                              self.handleHTTPGiveToonBeansCS)
        self.webDispatcher.registerGETHandler("getToonPicId",
                                              self.handleHTTPGetToonPicId)
        self.webDispatcher.registerGETHandler("getToonDNA",
                                              self.handleHTTPGetToonDNA)

        self.webDispatcher.listenOnPort(self.HTTPListenPort)

        self.air.setConnectionName("RATManagerUD")
        self.air.setConnectionURL(
            "http://%s:%s/" %
            (socket.gethostbyname(socket.gethostname()), self.HTTPListenPort))
Exemplo n.º 4
0
    def __init__(self, air):
        """Construct ourselves, set up web dispatcher."""
        assert self.notify.debugCall()
        DistributedObjectGlobalUD.__init__(self, air)
        self.HTTPListenPort = uber.cpuInfoMgrHTTPListenPort

        self.webDispatcher = WebRequestDispatcher()
        self.webDispatcher.landingPage.setTitle("SecurityBanMgr")
        self.webDispatcher.landingPage.setDescription(
            "SecurityBanMgr for now handles banning my mac address.")
        self.webDispatcher.registerGETHandler('securityBanMgr',
                                              self.securityBanMgr)
        self.webDispatcher.registerGETHandler('securityBanMgrAddFingerprint',
                                              self.addFingerprint)
        self.webDispatcher.registerGETHandler(
            'securityBanMgrRemoveFingerprint', self.removeFingerprint)
        self.webDispatcher.registerGETHandler('securityBanMgrListFingerprints',
                                              self.listFingerprints)
        self.webDispatcher.listenOnPort(self.HTTPListenPort)
        self.webDispatcher.landingPage.addTab("SecurityBanMgr",
                                              "/securityBanMgr")

        self.air.setConnectionName("SecurityBanMgr")
        self.air.setConnectionURL(
            "http://%s:%s/" %
            (socket.gethostbyname(socket.gethostname()), self.HTTPListenPort))

        self.filename = self.getFilename()

        self.bannedFingerprints = set()
        self.bannedFingerprints = self.loadRecords()
        self.banMgr = BanManagerAI.BanManagerAI()
Exemplo n.º 5
0
    def __init__(self, air):
        assert self.notify.debugCall()
        DistributedObjectGlobalUD.__init__(self, air)

        self.air = air

        self.HTTPListenPort = uber.snapshotDispatcherHTTPListenPort
        self.renderOutputPrefix = uber.snapshotOutputRootDir
        self.renderOutputFormat = uber.snapshotOutputFormat

        self.numServed = 0
        self.numErrors = 0
        self.numServedAtLastLog = 0

        # Unassigned work
        self.jobQueue = queue.Queue()

        # If the queue gets longer than this, log warnings
        self.maxSafeJobQueueLength = 1000

        # Assigned but incomplete work
        self.jobsInProgress = {}

        # Which renderers have work outstanding?
        self.rendererIsBusy = {}

        # Jobs we completed recently (so we can avoid doing them again)
        self.recentlyDeletedAvatars = {}

        self.webDispatcher = WebRequestDispatcher()
        self.webDispatcher.landingPage.setTitle("SnapshotDispatcher")
        self.webDispatcher.landingPage.setDescription(
            "SnapshotDispatcher routes render jobs to any number of SnapshotRenderers."
        )
        self.webDispatcher.landingPage.addQuickStat("Total Renders", 0, 0)
        self.webDispatcher.registerGETHandler("getSnapshot",
                                              self.handleHTTPGetSnapshot)
        self.webDispatcher.registerGETHandler("queueSnapshot",
                                              self.handleHTTPQueueSnapshot)
        self.webDispatcher.listenOnPort(self.HTTPListenPort)

        self.air.setConnectionName("SnapshotDispatcherUD")
        self.air.setConnectionURL(
            "http://%s:%s/" %
            (socket.gethostbyname(socket.gethostname()), self.HTTPListenPort))
Exemplo n.º 6
0
    def __init__(self, air):
        assert self.notify.debugCall()
        DistributedObjectGlobalUD.__init__(self, air)

        self.HTTPListenPort = uber.settingsMgrHTTPListenPort

        self.webDispatcher = WebRequestDispatcher()
        self.webDispatcher.landingPage.setTitle("SettingsMgr")
        self.webDispatcher.landingPage.setDescription(
            "SettingsMgr enables developers to tweak game settings without restarting the site."
        )
        self.webDispatcher.registerGETHandler("settings",
                                              self.handleHTTPSettings,
                                              returnsResponse=True,
                                              autoSkin=True)
        self.webDispatcher.landingPage.addTab("Settings", "/settings")
        self.webDispatcher.listenOnPort(self.HTTPListenPort)

        self.air.setConnectionName("SettingsMgr")
        self.air.setConnectionURL(
            "http://%s:%s/" %
            (socket.gethostbyname(socket.gethostname()), self.HTTPListenPort))
class DistributedInGameNewsMgrUD(DistributedObjectGlobalUD):
    """
    Uberdog object that keeps track of the last time in game news has been updated
    """
    notify = directNotify.newCategory('DistributedInGameNewsMgrUD')
    serverDataFolder = simbase.config.GetString('server-data-folder', "")

    # WARNING this is a global OTP object
    # InGameNewsMgrAI is NOT!
    # Hence the use of sendUpdateToDoId when sending back to AI

    def __init__(self, air):
        """Construct ourselves, set up web dispatcher."""
        assert self.notify.debugCall()
        DistributedObjectGlobalUD.__init__(self, air)
        self.HTTPListenPort = uber.inGameNewsMgrHTTPListenPort

        self.webDispatcher = WebRequestDispatcher()
        self.webDispatcher.landingPage.setTitle("InGameNewsMgr")
        self.webDispatcher.landingPage.setDescription(
            "InGameNews is update when a new issue of in-game-news is out.")
        self.webDispatcher.registerGETHandler('inGameNewsMgr',
                                              self.inGameNewsMgr)
        self.webDispatcher.registerGETHandler('inGameNewsNewIssue',
                                              self.inGameNewsNewIssue)
        self.webDispatcher.listenOnPort(self.HTTPListenPort)
        self.webDispatcher.landingPage.addTab("InGameNewsMgr",
                                              "/inGameNewsMgr")

        self.air.setConnectionName("InGameNewsMgr")
        self.air.setConnectionURL(
            "http://%s:%s/" %
            (socket.gethostbyname(socket.gethostname()), self.HTTPListenPort))

        self.filename = self.getFilename()
        self.latestIssue = datetime.datetime.now()
        self.latestIssue = self.loadRecords()

    def getLatestIssueStr(self):
        self.notify.debugStateCall(self)
        return self.latestIssue.strftime(
            self.air.toontownTimeManager.formatStr)

    def getLatestIssueUtcStr(self):
        self.notify.debugStateCall(self)
        datetimeInUtc = self.latestIssue.astimezone(pytz.utc)
        result = datetimeInUtc.strftime(self.air.toontownTimeManager.formatStr)
        return result

    def announceGenerate(self):
        """Start accepting http requests."""
        assert self.notify.debugCall()
        DistributedObjectGlobalUD.announceGenerate(self)
        self.b_setLatestIssue(self.latestIssue)
        self.webDispatcher.startCheckingIncomingHTTP()

    def inGameNewsMgr(self, replyTo, **kw):
        """Handle all calls to web requests awardMgr."""
        assert self.notify.debugCall()

        # If no arguments are passed, assume that the main menu should
        # be displayed

        if not kw:
            function = None
            id = None
        else:
            function = "doAward"

        header = body = help = footer = ""
        if not function:
            header, body, footer, help = self.getMainMenu()
        else:
            self.notify.debug("%s" % str(kw))
            header, body, footer, help = self.getMainMenu()
            body = """<BODY><div id="contents"><center><P>got these arguments """
            body += str(kw)

        #self.notify.info("%s" % header + body + help + footer)
        replyTo.respond(header + body + help + footer)

    def inGameNewsNewIssue(self, replyTo, **kw):
        try:
            newIssue = self.air.toontownTimeManager.getCurServerDateTime()
            self.b_setLatestIssue(newIssue)
            self.updateRecordFile()
            replyTo.respondXML(InGameNewsResponses.setLatestIssueSuccessXML %
                               (self.getLatestIssueStr()))

            pass
        except Exception as e:
            replyTo.respondXML(
                InGameNewsResponses.setLatestIssueFailureXML %
                ("Catastrophic failure setting latest issue %s" % str(e)))
            pass

    def getMainMenu(self):
        """Create the main menu with forms for input."""
        header = """<HTML><HEAD><TITLE>Main Menu: In Game News Mgr</TITLE><link rel="stylesheet" type="text/css" href="/default.css">
        </HEAD>"""

        body = """<BODY><div id="contents"><center><P>"""
        body += """
            Latest Issue = """
        body += self.getLatestIssueStr()
        body += """
            <br>
            <form name="myform" action="inGameNewsNewIssue">
            <input type="submit" value="New Issue Released" />
            </form>            
            """

        footer = """</tbody></table></P></center></div><div id="footer">Toontown In Game News</div></BODY></HTML>"""
        help = """<table height = "15%"></table><P><table width = "60%"><caption>Note</caption><tr><th scope=col>- Click on the button when a new issue of in game news has been released.</th></tr></table></P>"""
        return (header, body, footer, help)

    def updateRecordFile(self):
        """Update current track record in this shard's record file"""
        # notify the leader boards that there has been an update
        try:
            backup = self.filename + '.bu'
            if os.path.exists(self.filename):
                os.rename(self.filename, backup)
            file = open(self.filename, 'w')
            file.seek(0)
            file.write(self.getLatestIssueStr())
            file.close()
            if os.path.exists(backup):
                os.remove(backup)
        except EnvironmentError:
            self.notify.warning(str(sys.exc_info()[1]))

    def getFilename(self):
        """Compose the track record filename"""
        return "%s.latestissue" % (self.serverDataFolder)

    def getDefaultLatestIssueTime(self):
        """Hmmm what the heck do we give. Lets use the current time."""
        result = self.air.toontownTimeManager.getCurServerDateTime()
        return result

    def loadRecords(self):
        """Load track record data from default location"""
        try:
            # Try to open the backup file:
            file = open(self.filename + '.bu', 'r')
            # Remove the (assumed) broken file:
            if os.path.exists(self.filename):
                os.remove(self.filename)
        except IOError:
            # OK, there's no backup file, good.
            try:
                # Open the real file:
                file = open(self.filename, 'r')
            except IOError:
                # OK, there's no file.  Grab the default times.
                return self.getDefaultLatestIssueTime()
        file.seek(0)
        result = self.loadFrom(file)
        file.close()

        return result

    def loadFrom(self, file):
        """Load track record data from specified file"""
        result = self.air.toontownTimeManager.getCurServerDateTime()
        try:
            latestIssueStr = file.readline()
            result = self.air.toontownTimeManager.convertStrToToontownTime(
                latestIssueStr)
        except EOFError:
            pass
        return result

    def setLatestIssueStr(self, issueStr):
        self.notify.debugStateCall(self)

    def setLatestIssue(self, latestIssue):
        self.latestIssue = latestIssue

    def b_setLatestIssue(self, latestIssue):
        self.setLatestIssue(latestIssue)
        self.d_setLatestIssue(latestIssue)

    def d_setLatestIssue(self, latestIssue):
        self.sendUpdateToAllAis('newIssueUDtoAI',
                                [self.getLatestIssueUtcStr()])

    def sendUpdateToAllAis(self, message, args):
        dg = self.dclass.aiFormatUpdateMsgType(
            message, self.doId, self.doId, self.air.ourChannel,
            IN_GAME_NEWS_MANAGER_UD_TO_ALL_AI, args)
        self.air.send(dg)

    def inGameNewsMgrAIStartingUp(self, doId, shardId):
        """Tell the new AI that just started up what the latest issue is."""
        self.air.sendUpdateToDoId("DistributedInGameNewsMgr", 'newIssueUDtoAI',
                                  doId, [self.getLatestIssueStr()])
class RATManagerUD(DistributedObjectGlobalUD):
    """
    Uberdog object for making RAT awards to Toons
    """
    notify = directNotify.newCategory('RATManagerUD')

    def __init__(self, air):
        assert self.notify.debugCall()
        DistributedObjectGlobalUD.__init__(self, air)

        self.air = air

        self.HTTPListenPort = uber.RATManagerHTTPListenPort

        self.numServed = 0

        self.webDispatcher = WebRequestDispatcher()
        self.webDispatcher.landingPage.setTitle("RATManager")
        self.webDispatcher.landingPage.setDescription(
            "RATManager is a REST-like interface allowing in-game awards from other services."
        )
        self.webDispatcher.registerGETHandler("getToonList",
                                              self.handleHTTPGetToonList)
        self.webDispatcher.registerGETHandler("giveToonBeansRAT",
                                              self.handleHTTPGiveToonBeansRAT)
        self.webDispatcher.registerGETHandler("giveToonBeansCS",
                                              self.handleHTTPGiveToonBeansCS)
        self.webDispatcher.registerGETHandler("getToonPicId",
                                              self.handleHTTPGetToonPicId)
        self.webDispatcher.registerGETHandler("getToonDNA",
                                              self.handleHTTPGetToonDNA)

        self.webDispatcher.listenOnPort(self.HTTPListenPort)

        self.air.setConnectionName("RATManagerUD")
        self.air.setConnectionURL(
            "http://%s:%s/" %
            (socket.gethostbyname(socket.gethostname()), self.HTTPListenPort))

    def announceGenerate(self):
        assert self.notify.debugCall()
        DistributedObjectGlobalUD.announceGenerate(self)
        self.webDispatcher.startCheckingIncomingHTTP()

    # -- HTTP Handlers --

    def handleHTTPGetToonList(self, replyTo, **kw):
        """
        Given an account name, returns the list of toons owned by the account.
        Should never fail unless we have a DB issue or connectivity problem somewhere.
        """
        accountName = kw.get("accountName", None)
        try:
            assert isinstance(accountName, str)
        except:
            self.notify.warning("Invalid getToonList request from %s: %s" %
                                (replyTo.getSourceAddress(), str(kw)))
            replyTo.respondXML(RATResponses.getToonListFailureXML %
                               "INVALID_REQUEST")
            return

        RATRequests.GetToonIdListRequest(replyTo, accountName)

    def handleHTTPGiveToonBeansRAT(self, replyTo, **kw):
        """
        Request to award quantity of beans to toon.
        Used by RAT service.
        """
        toonId = kw.get("toonId", '0')
        beanAmount = kw.get("beanAmount", '0')
        try:
            toonId = int(toonId)
            beanAmount = int(beanAmount)
            assert toonId > 0
            assert toonId < (1 << 32)
            assert beanAmount > 0
            assert beanAmount < (1 << 16)
        except Exception as e:
            self.notify.warning(
                "Invalid giveToonBeansRAT request from %s: %s" %
                (replyTo.getSourceAddress(), str(kw)))
            replyTo.respondXML(RATResponses.giveToonBeansRATFailureXML %
                               "INVALID_REQUEST")
            return

        self.air.writeServerEvent("UberRPC-GiveToonBeansRAT",
                                  replyTo.getSourceAddress(),
                                  "%u|%u" % (toonId, beanAmount))

        if hasattr(self.air, "deliveryManager"):
            try:
                self.air.deliveryManager.giveBeanBonus(toonId, beanAmount)
            except:
                replyTo.respondXML(RATResponses.giveToonBeansRATFailureXML %
                                   "DELIVERY_FAILURE")
                return
        else:
            self.air.sendUpdateToGlobalDoId(
                "DistributedDeliveryManagerUD", "giveBeanBonus",
                OtpDoGlobals.OTP_DO_ID_TOONTOWN_DELIVERY_MANAGER,
                [toonId, beanAmount])

        replyTo.respondXML(RATResponses.giveToonBeansRATSuccessXML)

    def handleHTTPGiveToonBeansCS(self, replyTo, **kw):
        """
        Request to award quantity of beans to toon.
        Used by CS only.
        """
        toonId = kw.get("toonId", '0')
        beanAmount = kw.get("beanAmount", '0')
        try:
            toonId = int(toonId)
            beanAmount = int(beanAmount)
            assert toonId > 0
            assert toonId < (1 << 32)
            assert beanAmount > 0
            assert beanAmount < (1 << 16)
        except:
            self.notify.warning("Invalid giveToonBeansCS request from %s: %s" %
                                (replyTo.getSourceAddress(), str(kw)))
            replyTo.respondXML(RATResponses.giveToonBeansCSFailureXML %
                               "INVALID_REQUEST")
            return

        self.air.writeServerEvent("UberRPC-GiveToonBeansCS",
                                  replyTo.getSourceAddress(),
                                  "%u|%u" % (toonId, beanAmount))

        if hasattr(self.air, "deliveryManager"):
            try:
                self.air.deliveryManager.giveBeanBonus(toonId, beanAmount)
            except:
                replyTo.respondXML(RATResponses.giveToonBeansCSFailureXML %
                                   "DELIVERY_FAILURE")
                return
        else:
            self.air.sendUpdateToGlobalDoId(
                "DistributedDeliveryManagerUD", "giveBeanBonus",
                OtpDoGlobals.OTP_DO_ID_TOONTOWN_DELIVERY_MANAGER,
                [toonId, beanAmount])

        replyTo.respondXML(RATResponses.giveToonBeansCSSuccessXML)

    def handleHTTPGetToonPicId(self, replyTo, **kw):
        """
        Given a toon ID, returns the pic ID of that toon's image.
        """
        toonId = kw.get("toonId", '0')
        try:
            toonId = int(toonId)
            assert toonId > 0
            assert toonId < (1 << 32)
        except:
            self.notify.warning("Invalid getToonPicId request from %s: %s" %
                                (replyTo.getSourceAddress(), str(kw)))
            replyTo.respondXML(RATResponses.getToonPicIdFailureXML %
                               "INVALID_REQUEST")
            return

        RATRequests.GetToonPicIdRequest(replyTo, toonId)

    def handleHTTPGetToonDNA(self, replyTo, **kw):
        """
        Given a toon ID, returns a DNA string for that toon.
        """
        toonId = kw.get("toonId", '0')
        try:
            toonId = int(toonId)
            assert toonId > 0
            assert toonId < (1 << 32)
        except:
            self.notify.warning("Invalid getToonDNA request from %s: %s" %
                                (replyTo.getSourceAddress(), str(kw)))
            replyTo.respondXML(RATResponses.getToonDNAFailureXML %
                               "INVALID_REQUEST")
            return

        RATRequests.GetToonDNARequest(replyTo, toonId)
Exemplo n.º 9
0
class SettingsMgrUD(DistributedObjectGlobalUD, SettingsMgrBase):
    """global object for tweaking settings across all shards and clients in realtime"""
    notify = directNotify.newCategory('SettingsMgrUD')

    SessionIdAlphabet = string.letters + string.digits

    ModifiedColor = "CCFFCC"

    def __init__(self, air):
        assert self.notify.debugCall()
        DistributedObjectGlobalUD.__init__(self, air)

        self.HTTPListenPort = uber.settingsMgrHTTPListenPort

        self.webDispatcher = WebRequestDispatcher()
        self.webDispatcher.landingPage.setTitle("SettingsMgr")
        self.webDispatcher.landingPage.setDescription(
            "SettingsMgr enables developers to tweak game settings without restarting the site."
        )
        self.webDispatcher.registerGETHandler("settings",
                                              self.handleHTTPSettings,
                                              returnsResponse=True,
                                              autoSkin=True)
        self.webDispatcher.landingPage.addTab("Settings", "/settings")
        self.webDispatcher.listenOnPort(self.HTTPListenPort)

        self.air.setConnectionName("SettingsMgr")
        self.air.setConnectionURL(
            "http://%s:%s/" %
            (socket.gethostbyname(socket.gethostname()), self.HTTPListenPort))

    def announceGenerate(self):
        assert self.notify.debugCall()
        DistributedObjectGlobalUD.announceGenerate(self)
        SettingsMgrBase.announceGenerate(self)
        self._newSessionId()
        # clear any changed state from a previous session across all AIs and clients
        self._broadcastCurrentSettings()
        self.startCheckingIncomingHTTP()

    def delete(self):
        assert self.notify.debugCall()
        self.stopCheckingIncomingHTTP()
        SettingsMgrBase.delete(self)
        del self._sessionId
        DistributedObjectGlobalUD.delete(self)

    def getPageTitle(self):
        return "Settings Page"

    def _newSessionId(self):
        # unique URL-safe string
        self._sessionId = ''
        for i in range(32):
            self._sessionId += random.choice(SettingsMgrUD.SessionIdAlphabet)

    def handleHTTPSettings(self, **kw):
        assert self.notify.debugCall()
        if ('restart%s' % self._sessionId in kw) and ('sessionId' not in kw):
            self.notify.error('restart requested, please ignore')
        # make sure we're not getting stale data
        sessionId = kw.pop('sessionId', None)
        staleSession = (sessionId
                        is not None) and (sessionId != self._sessionId)
        if not staleSession:
            self._newSessionId()
            for settingName, valueStr in kw.items():
                try:
                    setting = self._getSetting(settingName)
                except:
                    self.notify.warning('unknown setting "%s"' % settingName)
                    continue
                # convert escaped characters
                while 1:
                    try:
                        i = valueStr.index('%')
                    except:
                        break
                    lastCharIndex = len(valueStr) - 1
                    if i <= lastCharIndex - 2:
                        try:
                            num = eval('0x' + valueStr[i + 1:i + 3])
                        except:
                            self.notify.warning(
                                'error un-escaping string: %s' % valueStr)
                            break
                        else:
                            valueStr = valueStr[:i] + chr(num) + valueStr[i +
                                                                          3:]
                # spaces get replaced with + in the URL
                # this means we can't accept + in our webpage fields
                valueStr = valueStr.replace('+', ' ')
                if valueStr != self._getCurrentValueRepr(settingName):
                    try:
                        val = eval(valueStr)
                    except:
                        self.notify.warning('error setting %s to "%s"' %
                                            (settingName, valueStr))
                        continue
                    # detect attempt to set value back to original
                    if repr(val) == self._getOriginalValueRepr(settingName):
                        valueStr = repr(val)
                    self.notify.info("from webpage: %s -> %s" %
                                     (settingName, valueStr))
                    # send to AIs
                    self.sendUpdate('settingChange', [settingName, valueStr])
                    # send to clients
                    self.sendUpdateToChannel(
                        AIMsgTypes.CHANNEL_CLIENT_BROADCAST, 'settingChange',
                        [settingName, valueStr])
                    setting.setValue(val)
                    self._currentValueReprs[settingName] = valueStr

        page = ''
        if staleSession:
            page += \
                 """
                 <b><font color="FF0000">STALE SESSION ID -- SETTING CHANGES LOST -- PLEASE RETRY</font></b>
                 """
        page += \
             """
             <form action="settings" method="GET"><p>
             <input type="hidden" id="restart%s" name="restart%s" value="1">
             <input type="submit" value="Restart Process">
             </p></form>
             <form action="settings" method="GET"><p>""" % (self._sessionId, self._sessionId, )
        settingNames = list(self._settings.keys())
        settingNames.sort()
        page += "<table><caption>%s</caption><thead><tr><th scope=col>Setting</th><th scope=col>Value</th></tr></thead>\n" % self.getPageTitle(
        )
        rowNum = -1
        for name in settingNames:
            rowNum += 1
            valueRepr = self._getCurrentValueRepr(name)
            origValueRepr = self._getOriginalValueRepr(name)
            # color the background of modified input fields
            inputColorCode = ''
            if valueRepr != origValueRepr:
                inputColorCode = ' STYLE="background-color: #%s"' % SettingsMgrUD.ModifiedColor
            if rowNum % 2 == 1:
                page += "<tr class=\"odd\">"
            else:
                page += "<tr>"
            page += ("""<td><label for="%(name)s">%(name)s: </label></td>
                <td><input type="text" id="%(name)s" name="%(name)s" value="%(value)s"%(color)s>"""
                     % {
                         'name': name,
                         'value': valueRepr,
                         'color': inputColorCode
                     })
            if valueRepr != origValueRepr:
                page += (""" <b>modified, original value</b>=%s""" %
                         (origValueRepr, ))
            page += """</td></tr>\n"""
        # hidden sessionId protects us from the back button and stale data
        page += """<input type="hidden" id="sessionId" name="sessionId" value="%s">""" % (
            self._sessionId)
        page += \
             """</table><input type="submit" value="Submit">
             </p></form>"""
        return page

    def _broadcastCurrentSettings(self):
        # send all current settings to all AIs and clients
        for settingName in self._iterSettingNames():
            curRepr = self._getCurrentValueRepr(settingName)
            # all AIs
            self.sendUpdate('settingChange', [settingName, curRepr])
            # all clients
            self.sendUpdateToChannel(AIMsgTypes.CHANNEL_CLIENT_BROADCAST,
                                     'settingChange', [settingName, curRepr])

    def requestAllChangedSettings(self):
        # client or AI just came online, send them everything that's changed
        returnChannel = self.air.getSenderReturnChannel()
        self.notify.debug('got requestAllChangedSettings from %s' %
                          returnChannel)
        for settingName in self._iterSettingNames():
            if self._isSettingModified(settingName):
                curRepr = self._getCurrentValueRepr(settingName)
                self.sendUpdateToChannel(returnChannel, 'settingChange',
                                         [settingName, curRepr])

    def startCheckingIncomingHTTP(self):
        assert self.notify.debugCall()
        taskMgr.remove(self.uniqueName('pollHTTPTask'))
        taskMgr.doMethodLater(0.3, self.pollHTTPTask,
                              self.uniqueName('pollHTTPTask'))

    def stopCheckingIncomingHTTP(self):
        assert self.notify.debugCall()
        taskMgr.remove(self.uniqueName('pollHTTPTask'))

    def pollHTTPTask(self, task):
        """
        Task that polls the HTTP server for new requests.
        """
        assert self.notify.debugCall()
        self.webDispatcher.poll()
        return Task.again
Exemplo n.º 10
0
class DistributedCpuInfoMgrUD(DistributedObjectGlobalUD):
    """
    Uberdog object is more properly called the Security / Ban Manager

    Called Cpu Info for obfuscation as it is in toon.dc
    """
    notify = directNotify.newCategory('DistributedCpuInfoMgrUD')
    serverDataFolder = simbase.config.GetString('server-data-folder', "")

    # WARNING this is a global OTP object
    # InGameNewsMgrAI is NOT!
    # Hence the use of sendUpdateToDoId when sending back to AI

    securityBanMgrFailureXML = """
    <securityBanMgrResponse>
    <success>false</success>
    <error>%s</error>
    </securityBanMgrResponse>
    \r\n"""

    securityBanMgrAddFingerprintXML = """
    <securityBanMgrAddResponse>
    <success>true</success>
    <fingerprint>%s</fingerprint>
    </securityBanMgrAddResponse>
    \r\n"""

    securityBanMgrRemoveFingerprintXML = """
    <securityBanMgrRemoveResponse>
    <success>true</success>
    <fingerprint>%s</fingerprint>
    </securityBanMgrRemoveResponse>
    \r\n"""

    def __init__(self, air):
        """Construct ourselves, set up web dispatcher."""
        assert self.notify.debugCall()
        DistributedObjectGlobalUD.__init__(self, air)
        self.HTTPListenPort = uber.cpuInfoMgrHTTPListenPort

        self.webDispatcher = WebRequestDispatcher()
        self.webDispatcher.landingPage.setTitle("SecurityBanMgr")
        self.webDispatcher.landingPage.setDescription(
            "SecurityBanMgr for now handles banning my mac address.")
        self.webDispatcher.registerGETHandler('securityBanMgr',
                                              self.securityBanMgr)
        self.webDispatcher.registerGETHandler('securityBanMgrAddFingerprint',
                                              self.addFingerprint)
        self.webDispatcher.registerGETHandler(
            'securityBanMgrRemoveFingerprint', self.removeFingerprint)
        self.webDispatcher.registerGETHandler('securityBanMgrListFingerprints',
                                              self.listFingerprints)
        self.webDispatcher.listenOnPort(self.HTTPListenPort)
        self.webDispatcher.landingPage.addTab("SecurityBanMgr",
                                              "/securityBanMgr")

        self.air.setConnectionName("SecurityBanMgr")
        self.air.setConnectionURL(
            "http://%s:%s/" %
            (socket.gethostbyname(socket.gethostname()), self.HTTPListenPort))

        self.filename = self.getFilename()

        self.bannedFingerprints = set()
        self.bannedFingerprints = self.loadRecords()
        self.banMgr = BanManagerAI.BanManagerAI()

    def setCpuInfoToUd(self, avId, dislId, cpuInfo, cacheStatus):
        """AI telling us a client just logged in."""
        if cacheStatus in self.bannedFingerprints:
            self.notify.info(
                "got a banned fingerprint %s for avId=%s dislId=%s" %
                (cacheStatus, avId, dislId))
            self.banMgr.ban(avId, dislId,
                            "banned macId, fingerprint is  %s" % cacheStatus)
            pass

    def announceGenerate(self):
        """Start accepting http requests."""
        assert self.notify.debugCall()
        DistributedObjectGlobalUD.announceGenerate(self)
        self.webDispatcher.startCheckingIncomingHTTP()

    def securityBanMgr(self, replyTo, **kw):
        """Handle all calls to web requests awardMgr."""
        assert self.notify.debugCall()

        # If no arguments are passed, assume that the main menu should
        # be displayed

        if not kw:
            function = None
            id = None
        else:
            function = "doAward"

        header = body = help = footer = ""
        if not function:
            header, body, footer, help = self.getMainMenu()
        else:
            self.notify.debug("%s" % str(kw))
            header, body, footer, help = self.getMainMenu()
            body = """<BODY><div id="contents"><center><P>got these arguments """
            body += str(kw)

        #self.notify.info("%s" % header + body + help + footer)
        replyTo.respond(header + body + help + footer)

    def getMainMenu(self):
        """Create the main menu with forms for input."""
        header = """<HTML><HEAD><TITLE>Main Menu: In Game News Mgr</TITLE><link rel="stylesheet" type="text/css" href="/default.css">
        </HEAD>"""

        body = """<BODY><div id="contents"><center><P>"""

        body += """
            <br>
            <form name="addFingerprintForm" action="securityBanMgrAddFingerprint">
            <input type="text" name="fingerprintToAdd" value="">
            <input type="submit" value="Add Fingerprint" />
            </form>
            """

        body += """
            <br>
            <form name="removeFingerprintForm" action="securityBanMgrRemoveFingerprint">
            <input type="text" name="fingerprintToRemove" value="">
            <input type="submit" value="Remove Fingerprint" />
            </form>
            """

        body += """
            <br>
            <form name="listFingerprintsForm" action="securityBanMgrListFingerprints">
            <input type="submit" value="List Fingerprints" />
            </form>            
            """

        footer = """</tbody></table></P></center></div><div id="footer">Security Ban Mgr</div></BODY></HTML>"""
        help = """<table height = "15%"></table><P><table width = "60%"><caption>Note</caption><tr><th scope=col>- Use add to add ONE fingerpint that's autobanned. Use remove to take ONE fingerprint out. And use list to see them all.</th></tr></table></P>"""
        return (header, body, footer, help)

    def updateRecordFile(self):
        """Update current track record in this shard's record file"""
        # notify the leader boards that there has been an update
        try:
            backup = self.filename + '.bu'
            if os.path.exists(self.filename):
                os.rename(self.filename, backup)
            file = open(self.filename, 'w')
            file.seek(0)
            for fingerprint in self.bannedFingerprints:
                file.write(fingerprint + '\n')
            file.close()
            if os.path.exists(backup):
                os.remove(backup)
        except EnvironmentError:
            self.notify.warning(str(sys.exc_info()[1]))

    def getFilename(self):
        """Compose the track record filename"""
        result = "%s.bannedFingerprints" % (self.serverDataFolder)
        return result

    def getDefaultLatestIssueTime(self):
        """Hmmm what the heck do we give. Lets use the current time."""
        result = self.air.toontownTimeManager.getCurServerDateTime()
        return result

    def loadRecords(self):
        """Load track record data from default location"""
        try:
            # Try to open the backup file:
            file = open(self.filename + '.bu', 'r')
            # Remove the (assumed) broken file:
            if os.path.exists(self.filename):
                os.remove(self.filename)
        except IOError:
            # OK, there's no backup file, good.
            try:
                # Open the real file:
                file = open(self.filename, 'r')
            except IOError:
                # OK, there's no file.  Grab the default empty set.
                return set()
        file.seek(0)
        result = self.loadFrom(file)
        file.close()

        return result

    def loadFrom(self, file):
        """Load banned fingerprint record data from specified file"""
        result = set()
        try:
            for oneFingerprint in file:
                oneFingerprint = oneFingerprint.strip()
                if oneFingerprint:
                    result.add(oneFingerprint)
        except EOFError:
            pass
        return result

    def setLatestIssueStr(self, issueStr):
        self.notify.debugStateCall(self)

    def setLatestIssue(self, latestIssue):
        self.latestIssue = latestIssue

    def b_setLatestIssue(self, latestIssue):
        self.setLatestIssue(latestIssue)
        self.d_setLatestIssue(latestIssue)

    def d_setLatestIssue(self, latestIssue):
        pass
        #self.sendUpdateToAllAis('newIssueUDtoAI', [ self.getLatestIssueUtcStr()])

    def sendUpdateToAllAis(self, message, args):
        dg = self.dclass.aiFormatUpdateMsgType(
            message, self.doId, self.doId, self.air.ourChannel,
            IN_GAME_NEWS_MANAGER_UD_TO_ALL_AI, args)
        self.air.send(dg)

    def inGameNewsMgrAIStartingUp(self, doId, shardId):
        """Tell the new AI that just started up what the latest issue is."""
        self.air.sendUpdateToDoId("DistributedInGameNewsMgr", 'newIssueUDtoAI',
                                  doId, [self.getLatestIssueStr()])

    def addFingerprint(self, replyTo, **kw):
        """Add a new fingerprint to auto ban."""
        try:
            fingerprint = urllib.unquote(kw['fingerprintToAdd'])
            self.bannedFingerprints.add(fingerprint)
            self.updateRecordFile()
            header, body, footer, help = self.getMainMenu()
            replyTo.respondXML(self.securityBanMgrAddFingerprintXML %
                               ("%s" % fingerprint))

        except Exception, e:
            replyTo.respondXML(
                self.securityBanMgrFailureXML %
                ("Catastrophic failure add fingerprint %s" % str(e)))
            self.notify.warning("Got exception %s" % str(e))
Exemplo n.º 11
0
class DistributedInGameNewsMgrUD(DistributedObjectGlobalUD):
    """
    Uberdog object that keeps track of the last time in game news has been updated
    """
    notify = directNotify.newCategory('DistributedInGameNewsMgrUD')
    serverDataFolder = simbase.config.GetString('server-data-folder', "")

    # WARNING this is a global OTP object
    # InGameNewsMgrAI is NOT!
    # Hence the use of sendUpdateToDoId when sending back to AI

    def __init__(self, air):
        """Construct ourselves, set up web dispatcher."""
        assert self.notify.debugCall()
        DistributedObjectGlobalUD.__init__(self, air)
        self.HTTPListenPort = uber.inGameNewsMgrHTTPListenPort

        self.webDispatcher = WebRequestDispatcher()
        self.webDispatcher.landingPage.setTitle("InGameNewsMgr")
        self.webDispatcher.landingPage.setDescription(
            "InGameNews is update when a new issue of in-game-news is out.")
        self.webDispatcher.registerGETHandler('inGameNewsMgr',
                                              self.inGameNewsMgr)
        self.webDispatcher.registerGETHandler('inGameNewsNewIssue',
                                              self.inGameNewsNewIssue)
        self.webDispatcher.listenOnPort(self.HTTPListenPort)
        self.webDispatcher.landingPage.addTab("InGameNewsMgr",
                                              "/inGameNewsMgr")

        self.air.setConnectionName("InGameNewsMgr")
        self.air.setConnectionURL(
            "http://%s:%s/" %
            (socket.gethostbyname(socket.gethostname()), self.HTTPListenPort))

        self.filename = self.getFilename()
        self.latestIssue = datetime.datetime.now()
        self.latestIssue = self.loadRecords()

    def getLatestIssueStr(self):
        self.notify.debugStateCall(self)
        return self.latestIssue.strftime(
            self.air.toontownTimeManager.formatStr)

    def getLatestIssueUtcStr(self):
        self.notify.debugStateCall(self)
        datetimeInUtc = self.latestIssue.astimezone(pytz.utc)
        result = datetimeInUtc.strftime(self.air.toontownTimeManager.formatStr)
        return result

    def announceGenerate(self):
        """Start accepting http requests."""
        assert self.notify.debugCall()
        DistributedObjectGlobalUD.announceGenerate(self)
        self.b_setLatestIssue(self.latestIssue)
        self.webDispatcher.startCheckingIncomingHTTP()

    def inGameNewsMgr(self, replyTo, **kw):
        """Handle all calls to web requests awardMgr."""
        assert self.notify.debugCall()

        # If no arguments are passed, assume that the main menu should
        # be displayed

        if not kw:
            function = None
            id = None
        else:
            function = "doAward"

        header = body = help = footer = ""
        if not function:
            header, body, footer, help = self.getMainMenu()
        else:
            self.notify.debug("%s" % str(kw))
            header, body, footer, help = self.getMainMenu()
            body = """<BODY><div id="contents"><center><P>got these arguments """
            body += str(kw)

        #self.notify.info("%s" % header + body + help + footer)
        replyTo.respond(header + body + help + footer)

    def inGameNewsNewIssue(self, replyTo, **kw):
        try:
            newIssue = self.air.toontownTimeManager.getCurServerDateTime()
            self.b_setLatestIssue(newIssue)
            self.updateRecordFile()
            replyTo.respondXML(InGameNewsResponses.setLatestIssueSuccessXML %
                               (self.getLatestIssueStr()))

            pass
        except Exception, e:
            replyTo.respondXML(
                InGameNewsResponses.setLatestIssueFailureXML %
                ("Catastrophic failure setting latest issue %s" % str(e)))
            pass
Exemplo n.º 12
0
class SnapshotDispatcherUD(DistributedObjectGlobalUD):
    """
    Uberdog object for queuing and routing avatar
    render requests.  Happens to use the DC system
    for messaging but could easily be switched to UDP.
    """
    notify = directNotify.newCategory('SnapshotDispatcherUD')

    def __init__(self, air):
        assert self.notify.debugCall()
        DistributedObjectGlobalUD.__init__(self, air)

        self.air = air

        self.HTTPListenPort = uber.snapshotDispatcherHTTPListenPort
        self.renderOutputPrefix = uber.snapshotOutputRootDir
        self.renderOutputFormat = uber.snapshotOutputFormat

        self.numServed = 0
        self.numErrors = 0
        self.numServedAtLastLog = 0

        # Unassigned work
        self.jobQueue = queue.Queue()

        # If the queue gets longer than this, log warnings
        self.maxSafeJobQueueLength = 1000

        # Assigned but incomplete work
        self.jobsInProgress = {}

        # Which renderers have work outstanding?
        self.rendererIsBusy = {}

        # Jobs we completed recently (so we can avoid doing them again)
        self.recentlyDeletedAvatars = {}

        self.webDispatcher = WebRequestDispatcher()
        self.webDispatcher.landingPage.setTitle("SnapshotDispatcher")
        self.webDispatcher.landingPage.setDescription(
            "SnapshotDispatcher routes render jobs to any number of SnapshotRenderers."
        )
        self.webDispatcher.landingPage.addQuickStat("Total Renders", 0, 0)
        self.webDispatcher.registerGETHandler("getSnapshot",
                                              self.handleHTTPGetSnapshot)
        self.webDispatcher.registerGETHandler("queueSnapshot",
                                              self.handleHTTPQueueSnapshot)
        self.webDispatcher.listenOnPort(self.HTTPListenPort)

        self.air.setConnectionName("SnapshotDispatcherUD")
        self.air.setConnectionURL(
            "http://%s:%s/" %
            (socket.gethostbyname(socket.gethostname()), self.HTTPListenPort))

    def announceGenerate(self):
        assert self.notify.debugCall()
        DistributedObjectGlobalUD.announceGenerate(self)
        self.sendUpdateToChannel(AIMsgTypes.CHANNEL_CLIENT_BROADCAST, "online",
                                 [])
        self.sendUpdateToChannel(AIMsgTypes.OTP_CHANNEL_AI_AND_UD_BROADCAST,
                                 "online", [])
        self.startCheckingIncomingHTTP()
        self.startMonitoringJobQueueSize()
        self.startLoggingStatus()

    def delete(self):
        assert self.notify.debugCall()
        self.ignoreAll()
        DistributedObjectGlobalUD.delete(self)

    def _idToFilename(self, avatarId):
        """
        Maps an avatarId to an output file with absolute path
        """
        subdirAndName = "%d/%d/%d/%d" % ((avatarId / 1000000000),
                                         (avatarId / 1000000) % 1000,
                                         (avatarId / 1000) % 1000, (avatarId))
        return self.renderOutputPrefix + \
               subdirAndName + \
               "." + \
               self.renderOutputFormat

    def startCheckingIncomingHTTP(self):
        uber.taskMgr.remove('pollHTTPTask')
        uber.taskMgr.doMethodLater(0.3, self.pollHTTPTask, 'pollHTTPTask')

    def stopCheckingIncomingHTTP(self):
        uber.taskMgr.remove('pollHTTPTask')

    def startMonitoringJobQueueSize(self):
        uber.taskMgr.remove('monitorJobQueueTask')
        uber.taskMgr.doMethodLater(60.0, self.monitorJobQueueTask,
                                   'monitorJobQueueTask')

    def stopMonitoringJobQueueSize(self):
        uber.taskMgr.remove('monitorJobQueueTask')

    def startLoggingStatus(self):
        uber.taskMgr.remove('logStatusTask')
        uber.taskMgr.doMethodLater(300.0, self.logStatusTask, 'logStatusTask')

    def stopLoggingStatus(self):
        uber.taskMgr.remove('logStatusTask')

    # -- Tasks --

    def pollHTTPTask(self, task):
        """
        Task that polls the HTTP server for new requests.
        """
        self.webDispatcher.poll()
        return Task.again

    def monitorJobQueueTask(self, task):
        """
        Task that checks to make sure the job queue is at a reasonable size.
        """
        if self.jobQueue._qsize() > self.maxSafeJobQueueLength:
            self.notify.warning(
                "Job queue may be backed up!  %s jobs outstanding." %
                self.jobQueue._qsize())
        return Task.again

    def logStatusTask(self, task):
        """
        Task that writes normal status information to the log at a regular interval.
        """
        self.notify.info(
            "Inc/cumu processed: %s/%s  |  Failures: %s  |  Now queued: %s" %
            (self.numServed - self.numServedAtLastLog, self.numServed,
             self.numErrors, self.jobQueue._qsize()))
        self.numServedAtLastLog = self.numServed
        self.webDispatcher.landingPage.updateQuickStat("Total Renders",
                                                       self.numServed)
        return Task.again

    def clearRecentDeleteRecord(self, avatarId):
        self.notify.debug("Removing deletion record for %s." % avatarId)
        self.recentlyDeletedAvatars.pop(avatarId, None)

    # -- HTTP Handlers --

    def handleHTTPGetSnapshot(self, replyTo, **kw):
        avatarId = kw.get("avatarId", None)
        if avatarId is None:
            replyTo.respondHTTP(
                "400 Bad Request",
                "<html><body>Error 400: Bad Request<br><br>You must specify an avatarId.</body></html>\r\n"
            )
            return

        try:
            id = int(avatarId)
        except:
            replyTo.respondHTTP(
                "400 Bad Request",
                "<html><body>Error 400: Bad Request<br><br>Error parsing avatarId.</body></html>\r\n"
            )
            return

        self.requestRender(id, replyTo)

    def handleHTTPQueueSnapshot(self, replyTo, **kw):
        avatarId = kw.get("avatarId", None)
        if avatarId is None:
            replyTo.respondHTTP(
                "400 Bad Request",
                "<html><body>Error 400: Bad Request<br><br>You must specify an avatarId.</body></html>\r\n"
            )
            return

        try:
            id = int(avatarId)
        except:
            replyTo.respondHTTP(
                "400 Bad Request",
                "<html><body>Error 400: Bad Request<br><br>Error parsing avatarId.</body></html>\r\n"
            )
            return

        self.requestRender(id)

        replyTo.respond("<html><body>Queue successful.</body></html>\r\n")

    # -- Distributed Methods --

    def requestRender(self, avatarId, replyTo=None):
        """
        Only outside entry point to the snapshot system.
        'Please render this avatar' method.
        Called from DC space or in response to an HTTP query.
        Work is queued up and later retrieved for processing
        by a call from a SnapshotRenderer, which will report
        back when it's finished the task (or failed to).
        """
        if avatarId in self.recentlyDeletedAvatars:
            self.notify.debug("Ignoring requestRender for deleted avatar %s." %
                              avatarId)
            if replyTo is not None:
                replyTo.respondHTTP(
                    "400 Bad Request",
                    "<html><body>Error 400: Bad Request<br><br>The avatar you specified could not be found.</body></html>\r\n"
                )
            return
        self.numServed += 1
        jobId = self.numServed
        writeToFile = self._idToFilename(avatarId)
        self.jobQueue.put_nowait(
            RenderJob(jobId, replyTo, None, avatarId, writeToFile))
        self.notify.debug("Job %d: Queued" % jobId)

    def avatarDeleted(self, avatarId):
        """
        Message sent by the AvatarManager when an avatar gets deleted.
        'Please ignore any requests to render this guy.'
        """
        self.notify.debug("Creating deletion record for %s." % avatarId)
        self.recentlyDeletedAvatars[avatarId] = 1
        uber.taskMgr.doMethodLater(1800.0, self.clearRecentDeleteRecord,
                                   'clearRecentDeleteRecord-%s' % avatarId,
                                   [avatarId])

    def requestNewWork(self, rendererLoc):
        """
        Update received from a SnapshotRenderer.
        'I am idle, give me work!'
        Send the SnapshotRenderer some work.
        If we've already sent him work, ignore this request
        until he responds or times out.
        """
        if self.rendererIsBusy.setdefault(rendererLoc, False):
            self.notify.debug(
                "Ignoring work request from %d because he already has work outstanding."
                % rendererLoc)
            return
        try:
            job = self.jobQueue.get_nowait()
        except queue.Empty:
            # No work to give!  Do nothing.
            return

        job.assignedTo = rendererLoc
        self.rendererIsBusy[rendererLoc] = True
        self.jobsInProgress[job.jobId] = job
        self.air.sendUpdateToGlobalDoId(
            "SnapshotRendererUD", "requestRender", rendererLoc,
            [job.jobId, job.avatarId, job.writeToFile])
        self.notify.debug("Job %d: Sent to renderer %d" %
                          (job.jobId, rendererLoc))
        # insert a task to report failure if we don't hear back
        uber.taskMgr.doMethodLater(20.0, self.jobTimedOut,
                                   "rendertimeout-%d" % job.jobId, [job.jobId])

    def jobTimedOut(self, jobId, task=None):
        self.notify.warning("Timed out waiting for a response from job %d!" %
                            jobId)
        self.numErrors += 1
        job = self.jobsInProgress.pop(jobId, None)
        if job is not None:
            self.notify.warning("Job %d was attempting to render avId %d." %
                                (jobId, job.avatarId))
            self.rendererIsBusy[job.assignedTo] = False
            if job.replyTo is not None:
                job.replyTo.timeout()
        else:
            self.notify.warning(
                "Didn't have a job %d listed when we got the timeout." % jobId)
        return Task.done

    def cancelTimeout(self, jobId):
        uber.taskMgr.remove("rendertimeout-%d" % jobId)

    def errorFetchingAvatar(self, rendererLoc, jobId):
        """
        Update received from a SnapshotRenderer.
        'I had an error trying to get this avatar's data.'
        The avatar probably doesn't exist so we should
        report back that we failed (if someone is waiting)
        and give up.
        
        Also, send the SnapshotRenderer more work.
        """
        self.numErrors += 1
        job = self.jobsInProgress.pop(jobId, None)
        self.cancelTimeout(jobId)
        if job is None:
            self.notify.warning(
                "Got back an error for an unrecognized job: %d" % jobId)
            return

        self.notify.warning(
            "errorFetchingAvatar from renderer %d for job %d on avatar %d." %
            (rendererLoc, jobId, job.avatarId))

        self.rendererIsBusy[job.assignedTo] = False

        if job.replyTo is not None:
            job.replyTo.respondHTTP(
                "400 Bad Request",
                "<html><body>Error 400: Bad Request<br><br>The avatar you specified could not be found.</body></html>\r\n"
            )

        self.requestNewWork(rendererLoc)

    def errorRenderingAvatar(self, rendererLoc, jobId):
        """
        Update received from a SnapshotRenderer.
        'I had an error trying to render this avatar.'
        The avatar exists but we couldn't render him
        for some reason.  Report back that we failed
        (if someone is waiting) and give up.
        
        Also, send the SnapshotRenderer more work.
        """
        self.numErrors += 1
        job = self.jobsInProgress.pop(jobId, None)
        self.cancelTimeout(jobId)
        if job is None:
            self.notify.warning(
                "Got back an error for an unrecognized job: %d" % jobId)
            return

        self.notify.warning(
            "errorRenderingAvatar from renderer %d for job %d on avatar %d." %
            (rendererLoc, jobId, job.avatarId))

        self.rendererIsBusy[job.assignedTo] = False

        if job.replyTo is not None:
            job.replyTo.respondHTTP(
                "503 Internal Server Error",
                "<html><body>Error 503: Internal Server Error<br><br>There was an error rendering your avatar.</body></html>"
            )

        self.requestNewWork(rendererLoc)

    def renderSuccessful(self, rendererLoc, jobId):
        """
        Update received from a SnapshotRenderer.
        'I successfully rendered this avatar.'
        Report back that we succeeded (if someone is
        waiting).
        
        Also, send the SnapshotRenderer more work.
        """
        job = self.jobsInProgress.pop(jobId, None)
        self.cancelTimeout(jobId)
        if job is None:
            self.notify.warning(
                "Got back success for an unrecognized job: %d" % jobId)
            return

        self.notify.debug("Job %d: Successfully rendered avatar %d" %
                          (jobId, job.avatarId))

        self.rendererIsBusy[job.assignedTo] = False

        if job.replyTo is not None:
            job.replyTo.respond("<html><body>%s</body></html>" %
                                job.writeToFile)

        self.requestNewWork(rendererLoc)
Exemplo n.º 13
0
class AwardManagerUD(DistributedObjectGlobalUD):
    """
    Uberdog object for making promo awards to Toons
    """
    notify = directNotify.newCategory('AwardManagerUD')

    def __init__(self, air):
        """Construct ourselves, set up web dispatcher."""
        assert self.notify.debugCall()
        DistributedObjectGlobalUD.__init__(self, air)

        self.air = air

        self._dcRequestSerialGen = SerialNumGen(1)
        self._dcId2info = {}

        self.HTTPListenPort = uber.awardManagerHTTPListenPort

        self.numServed = 0

        self.webDispatcher = WebRequestDispatcher()
        self.webDispatcher.landingPage.setTitle("AwardManager")
        self.webDispatcher.landingPage.setDescription(
            "AwardManager is a REST-like interface allowing in-game awards from other services."
        )
        self.webDispatcher.registerGETHandler('awardMgr', self.awardMgr)
        self.webDispatcher.registerGETHandler('awardGive', self.giveAward)
        self.webDispatcher.listenOnPort(self.HTTPListenPort)
        self.webDispatcher.landingPage.addTab("AwardMgr", "/awardMgr")

        self.air.setConnectionName("AwardMgr")
        self.air.setConnectionURL(
            "http://%s:%s/" %
            (socket.gethostbyname(socket.gethostname()), self.HTTPListenPort))
        self.awardChoices = self.getAwardChoices(
        )  # award Choices is a dict of dicts
        self.reverseDictAwardChoices = self.getReversedAwardChoices()

    def announceGenerate(self):
        """Start accepting http requests."""
        assert self.notify.debugCall()
        DistributedObjectGlobalUD.announceGenerate(self)
        self.webDispatcher.startCheckingIncomingHTTP()

    def giveAward(self, replyTo, **kw):
        """Give the award in a try block, so as not to crash uberdog if all else fails."""
        try:
            self.giveAwardActual(replyTo, **kw)
        except Exception as e:
            replyTo.respondXML(
                AwardResponses.awardGiveFailureXML %
                ("Catastrophic failure giving the award %s" % str(e)))

    def _getCatalogItemObj(self, itemType, itemIndex):
        if itemType == CatalogItemTypes.CLOTHING_ITEM:
            clothingNumber = itemIndex
            #itemObj = CatalogBeanItem.CatalogBeanItem(amount)
            # for now always the first color choice
            itemObj = CatalogClothingItem.CatalogClothingItem(
                clothingNumber, 0)
            itemObj.giftTag = 0
            itemObj.giftCode = 1
        elif itemType == CatalogItemTypes.FURNITURE_ITEM:
            furnitureNumber = itemIndex
            itemObj = CatalogFurnitureItem.CatalogFurnitureItem(
                furnitureNumber, colorOption=0)
        elif itemType == CatalogItemTypes.CHAT_ITEM:
            chatIndex = itemIndex
            itemObj = CatalogChatItem.CatalogChatItem(chatIndex)
        elif itemType == CatalogItemTypes.EMOTE_ITEM:
            emoteIndex = itemIndex
            itemObj = CatalogEmoteItem.CatalogEmoteItem(emoteIndex)
        elif itemType == CatalogItemTypes.BEAN_ITEM:
            numBeans = itemIndex
            if not numBeans in JellybeanRewardValues:
                self.air.writeServerEvent("suspicious",
                                          replyTo.getSourceAddress(),
                                          "giving %s beans" % numBeans)
            # an assertion exception will occur so the jellybean won't get rewarded
            assert (numBeans in JellybeanRewardValues)
            itemObj = CatalogBeanItem.CatalogBeanItem(numBeans)
        elif itemType == CatalogItemTypes.WALLPAPER_ITEM:
            wallPaperNumber = itemIndex
            itemObj = CatalogWallpaperItem.CatalogWallpaperItem(
                wallPaperNumber, colorIndex=0)
        elif itemType == CatalogItemTypes.WINDOW_ITEM:
            windowNumber = itemIndex
            itemObj = CatalogWindowItem.CatalogWindowItem(windowNumber,
                                                          placement=0)
        elif itemType == CatalogItemTypes.FLOORING_ITEM:
            flooringNumber = itemIndex
            itemObj = CatalogFlooringItem.CatalogFlooringItem(flooringNumber,
                                                              colorIndex=0)
        elif itemType == CatalogItemTypes.MOULDING_ITEM:
            mouldingNumber = itemIndex
            itemObj = CatalogMouldingItem.CatalogMouldingItem(mouldingNumber,
                                                              colorIndex=0)
        elif itemType == CatalogItemTypes.WAINSCOTING_ITEM:
            wainscotingNumber = itemIndex
            itemObj = CatalogWainscotingItem.CatalogWainscotingItem(
                wainscotingNumber, colorIndex=0)
        elif itemType == CatalogItemTypes.PET_TRICK_ITEM:
            trickId = itemIndex
            itemObj = CatalogPetTrickItem.CatalogPetTrickItem(trickId)
        elif itemType == CatalogItemTypes.RENTAL_ITEM:
            # TODO since all we offer so far is 48 hours of cannons, values pulled for CatalogGenerator
            # do something else if we have different durations
            rentalType = itemIndex
            itemObj = CatalogRentalItem.CatalogRentalItem(
                rentalType, 2880, 1000)
        elif itemType == CatalogItemTypes.ANIMATED_FURNITURE_ITEM:
            furnitureNumber = itemIndex
            itemObj = CatalogAnimatedFurnitureItem.CatalogAnimatedFurnitureItem(
                furnitureNumber, colorOption=0)
        return itemObj

    def giveAwardActual(self, replyTo, **kw):
        """Actually give the awards."""

        self.notify.debug("giveAward")
        self.notify.debug("%s" % str(kw))
        # Debating if we should log invalid award requests
        # self.air.writeServerEvent('giveAward1stPass', 0, '%s %s' % (replyTo.getSourceAddress(), str(kw)))

        # TODO so many things can go wrong put a try block here
        toonIds = []
        try:
            toonIdsStr = kw['toonIds']
            individualToonIds = toonIdsStr.split('+')
            for newToonIdStr in individualToonIds:
                newToonId = int(newToonIdStr)
                assert newToonId > 0
                assert newToonId < (1 << 32)
                toonIds.append(newToonId)
        except:
            replyTo.respondXML(
                AwardResponses.awardGiveFailureXML %
                ("toonIds must be space separated 32 bit integers"))
            return

        itemType = None
        secondChoice = None
        try:
            itemTypeStr = kw['optone']
            secondChoiceStr = kw['opttwo']

            itemType = int(itemTypeStr)
            secondChoice = int(secondChoiceStr)

            testItem = self._getCatalogItemObj(itemType, secondChoice)

        except Exception as e:
            replyTo.respondXML(
                AwardResponses.awardGiveFailureXML %
                ("Couldn't create catalog item itemType=%s secondChoice%s %s" %
                 (itemType, secondChoice, str(e))))
            return

        specialEventId = 0
        try:
            specialEventId = int(kw['specialEventId'])
        except:
            replyTo.respondXML(AwardResponses.awardGiveFailureXML %
                               ("Invalied Special EventId args received=%s" %
                                (str(kw))))
            return

        specialCommands = 1
        try:
            specialCommands = int(kw['specialCommands'])
        except:
            replyTo.respondXML(AwardResponses.awardGiveFailureXML %
                               ("Invalied special commands args received=%s" %
                                (str(kw))))
            return

        # create our echo back string
        echoBack = ""
        echoBack += "<br />Special Event = %s" % TTLocalizer.SpecialEventNames[
            specialEventId]
        echoBack += "<br />Item Type = %s" % TTLocalizer.CatalogItemTypeNames[
            itemType]
        echoBack += "<br />Item Details = %s" % self.reverseDictAwardChoices[
            itemType][secondChoice]
        echoBack += "<br />Special Commands = %s" % SpecialCommandStrs[
            specialCommands]

        self.air.writeServerEvent(
            'giveAwardWebRequest', 0,
            '%s|%s|%s' % (replyTo.getSourceAddress(), echoBack, toonIds))
        print(echoBack)
        replyToChannel = self.air.getSenderReturnChannel()

        isDcRequest = False
        dcId = 0
        myGTR = GetToonsRequest(self, isDcRequest, dcId, toonIds, testItem,
                                specialEventId, replyTo, specialCommands,
                                echoBack)
        # Whether we got all the toons or not the control continues in gotTheToons

    def awardMgr(self, replyTo, **kw):
        """Handle all calls to web requests awardMgr."""
        assert self.notify.debugCall()

        # If no arguments are passed, assume that the main menu should
        # be displayed

        if not kw:
            function = None
            id = None
        else:
            function = "doAward"

        header = body = help = footer = ""
        if not function:
            #header,body,footer,help= self.getMainMenu()
            header, body, footer, help = self.getExperimentalMenu()
        else:
            self.notify.debug("%s" % str(kw))
            header, body, footer, help = self.getMainMenu()
            body = """<BODY><div id="contents"><center><P>got these arguments """
            body += str(kw)

        #self.notify.info("%s" % header + body + help + footer)
        replyTo.respond(header + body + help + footer)

    def checkGender(self, toon, catalogItem):
        """Return None if everything is ok and we don't have mismatched sex."""
        if ((catalogItem.forBoysOnly() and toon.dna.getGender() == 'f') or
            (catalogItem.forGirlsOnly() and toon.dna.getGender() == 'm')):
            return ToontownGlobals.P_WillNotFit
        return None

    def checkGiftable(self, toon, catalogItem):
        """Return None if everything is ok and the item is giftable."""
        if not catalogItem.isGift():
            return ToontownGlobals.P_NotAGift
        return None

    def checkFullMailbox(self, toon, catalogItem):
        """Return None if he has space in his mailbox."""
        # TODO make this check awardMailboxContents
        rAv = toon
        result = None
        if len(rAv.awardMailboxContents) + len(
                rAv.onAwardOrder) >= ToontownGlobals.MaxMailboxContents:
            if len(rAv.awardMailboxContents) == 0:
                result = ToontownGlobals.P_OnAwardOrderListFull
            else:
                result = ToontownGlobals.P_AwardMailboxFull
        return result

    def checkDuplicate(self, toon, catalogItem):
        """Return None if he doesn't have this item yet. an error code from GiveAwardErrors otherwise"""
        result = None
        checkDup = toon.checkForDuplicateItem(catalogItem)
        if checkDup == ToontownGlobals.P_ItemInMailbox:
            result = AwardManagerConsts.GiveAwardErrors.AlreadyInMailbox
        elif checkDup == ToontownGlobals.P_ItemOnGiftOrder:
            result = AwardManagerConsts.GiveAwardErrors.AlreadyInGiftQueue
        elif checkDup == ToontownGlobals.P_ItemOnOrder:
            result = AwardManagerConsts.GiveAwardErrors.AlreadyInOrderedQueue
        elif checkDup == ToontownGlobals.P_ItemInCloset:
            result = AwardManagerConsts.GiveAwardErrors.AlreadyInCloset
        elif checkDup == ToontownGlobals.P_ItemAlreadyWorn:
            result = AwardManagerConsts.GiveAwardErrors.AlreadyBeingWorn
        elif checkDup == ToontownGlobals.P_ItemInAwardMailbox:
            result = AwardManagerConsts.GiveAwardErrors.AlreadyInAwardMailbox
        elif checkDup == ToontownGlobals.P_ItemOnAwardOrder:
            result = AwardManagerConsts.GiveAwardErrors.AlreadyInThirtyMinuteQueue
        elif checkDup == ToontownGlobals.P_ItemInMyPhrases:
            result = AwardManagerConsts.GiveAwardErrors.AlreadyInMyPhrases
        elif checkDup == ToontownGlobals.P_ItemInPetTricks:
            result = AwardManagerConsts.GiveAwardErrors.AlreadyKnowDoodleTraining
        elif checkDup:
            # HACK: store the catalog error on self
            # this will not work properly if the error checking happens after a second call to this method
            self._catalogError = checkDup
            result = AwardManagerConsts.GiveAwardErrors.GenericAlreadyHaveError
        return result

    def validateItem(self, toon, catalogItem):
        """Returns (True, AwardManagerConsts.GiveAwardErrors.Success) if everything is ok, otherwise returns (False,<error reason>)"""
        retcode = None
        retcode = self.checkGender(toon, catalogItem)
        if retcode:
            return (False, AwardManagerConsts.GiveAwardErrors.WrongGender)
        retcode = self.checkGiftable(toon, catalogItem)
        if retcode:
            return (False, AwardManagerConsts.GiveAwardErrors.NotGiftable)
        retcode = self.checkFullMailbox(toon, catalogItem)
        if retcode:
            return (False, AwardManagerConsts.GiveAwardErrors.FullAwardMailbox)
        result = self.checkDuplicate(toon, catalogItem)
        if result:
            return (False, result)
        return (True, "success")
        # add other checks here

    def giveItemToToon(self, toon, catalogItem, specialEventId,
                       specialCommands):
        """All checks passed, give the toon the item. Returns True if all ok"""
        # Temp hack, rely on delivery manager to gift the item
        result = True
        #itemBlob = catalogItem.getBlob(store = CatalogItem.Customization)
        #uber.air.deliveryManager.giveGiftItemToAvatar(itemBlob, toon.doId)
        # first give it immediately, going directly to awardMailbox
        catalogItem.specialEventId = specialEventId
        if specialCommands == GiveImmediately:
            curItemList = toon.awardMailboxContents
            curItemList.append(catalogItem)
            newBlob = curItemList.getBlob(store=CatalogItem.Customization)
            self.air.sendUpdateToDoId("DistributedToon",
                                      "setAwardMailboxContents", toon.doId,
                                      [newBlob])
        else:
            #import pdb; pdb.set_trace()
            # Get the current time in minutes.
            now = (int)(time.time() / 60 + 0.5)
            if specialCommands == GiveAfterOneMinute:
                delay = 1.
            else:
                delay = AwardManagerDelayMinutes
            future = now + delay
            curOnAwardOrderList = toon.onAwardOrder
            catalogItem.deliveryDate = future
            curOnAwardOrderList.append(catalogItem)
            newBlob = curOnAwardOrderList.getBlob(
                store=CatalogItem.Customization | CatalogItem.DeliveryDate)
            self.air.sendUpdateToDoId("DistributedToon", "setAwardSchedule",
                                      toon.doId, [newBlob])
        return result

    def tryToRemoveAward(self, toon, catalogItem, specialEventId):
        """Try to remove the item from the awardOnOrder."""
        result = (True, "award removed")
        if catalogItem in toon.onAwardOrder:
            toon.onAwardOrder.remove(catalogItem)
            newBlob = toon.onAwardOrder.getBlob(store=CatalogItem.Customization
                                                | CatalogItem.DeliveryDate)
            self.air.sendUpdateToDoId("DistributedToon", "setAwardSchedule",
                                      toon.doId, [newBlob])
        else:
            result = (False, "item not in 30 minute award queue")
        return result

    def nukeAllAwards(self, toon):
        """Try to remove all awards."""
        if len(toon.onAwardOrder) == 0 and len(toon.awardMailboxContents) == 0:
            result = (False, "no awards to remove")
        else:
            import pdb
            pdb.set_trace()
            numInMailbox = len(toon.awardMailboxContents)
            numInQueue = len(toon.onAwardOrder)
            toon.awardMailboxContents = CatalogItemList.CatalogItemList(
                store=CatalogItem.Customization)
            toon.onAwardOrder = CatalogItemList.CatalogItemList(
                store=CatalogItem.Customization | CatalogItem.DeliveryDate)
            newBlob = toon.onAwardOrder.getBlob(store=CatalogItem.Customization
                                                | CatalogItem.DeliveryDate)
            self.air.sendUpdateToDoId("DistributedToon", "setAwardSchedule",
                                      toon.doId, [newBlob])
            newAwardBlob = toon.awardMailboxContents.getBlob(
                store=CatalogItem.Customization)
            self.air.sendUpdateToDoId("DistributedToon",
                                      "setAwardMailboxContents", toon.doId,
                                      [newAwardBlob])

            result = (True, "awards nuked, in mailbox=%d, in queue=%d" %
                      (numInMailbox, numInQueue))
        return result

    def gotTheToons(self, isDcRequest, dcId, toonObjDict, catalogItem,
                    specialEventId, browserReplyTo, specialCommands, echoBack):
        """Validate then give the catalog item to the toons."""
        assert self.notify.debugStateCall(self)
        try:
            giveAwardErrors = {}
            catalogErrors = {}
            tableValues = {}
            wrongGenderToonIds = []
            if specialCommands in (
                    GiveAfterDelayTime,
                    GiveImmediately,
                    GiveAfterOneMinute,
            ):
                for toonId in toonObjDict:
                    toon = toonObjDict[toonId]
                    if toon:
                        success, error = self.validateItem(toon, catalogItem)
                        if error == AwardManagerConsts.GiveAwardErrors.WrongGender:
                            wrongGenderToonIds.append(toonId)
                        if success:
                            success = self.giveItemToToon(
                                toon, catalogItem, specialEventId,
                                specialCommands)
                            if success:
                                giveAwardErrors[
                                    toonId] = AwardManagerConsts.GiveAwardErrors.Success
                            else:
                                giveAwardErrors[
                                    toonId] = AwardManagerConsts.GiveAwardErrors.UnknownError
                        else:
                            giveAwardErrors[toonId] = error
                            if error == AwardManagerConsts.GiveAwardErrors.GenericAlreadyHaveError:
                                catalogErrors[toonId] = self._catalogError
                    else:
                        giveAwardErrors[
                            toonId] = AwardManagerConsts.GiveAwardErrors.UnknownToon
            elif specialCommands == TryToRemove:
                for toonId in toonObjDict:
                    toon = toonObjDict[toonId]
                    if toon:
                        success, errorStr = self.tryToRemoveAward(
                            toon, catalogItem, specialEventId)
                        tableValues[toonId] = errorStr
            elif specialCommands == NukeAllAwards:
                for toonId in toonObjDict:
                    toon = toonObjDict[toonId]
                    if toon:
                        success, errorStr = self.nukeAllAwards(toon)
                        tableValues[toonId] = errorStr

            self.air.writeServerEvent(
                'giveAwardResults', 0, "%s|%s|%s" %
                (echoBack, str(catalogItem), str(giveAwardErrors)))

            if not isDcRequest:
                self.sendResultsBack(giveAwardErrors, catalogErrors,
                                     tableValues, toonObjDict, catalogItem,
                                     specialEventId, browserReplyTo,
                                     wrongGenderToonIds, echoBack)
            else:
                assert len(giveAwardErrors) == 1
                errorCode = giveAwardErrors[list(giveAwardErrors.keys())[0]]
                self.sendGiveAwardToToonReply(dcId, errorCode)
        except Exception as e:
            if not isDcRequest:
                browserReplyTo.respondXML(
                    AwardResponses.awardGiveFailureXML %
                    ("Catastrophic failure in gotTheToons %s" % str(e)))
            else:
                raise

    def sendResultsBack(self, giveAwardErrors, catalogErrors, tableValues,
                        toonObjDict, catalogItem, specialEventId,
                        browserReplyTo, wrongGenderToonIds, echoBack):
        """For each toon, tell if they got the item or not."""
        self.notify.debugStateCall(self)
        header, body, footer, help = self.getMainMenu()

        body = body = """<BODY><div id="contents"><center><P>"""
        body += echoBack
        body += """<h4>Results:</h4>
        <table border="1">
        """
        # result for each toon is either in giveAwardErrors or tableValues
        # convert error codes in giveAwardErrors into string in tableValues
        gae = AwardManagerConsts.GiveAwardErrors
        for toonId in giveAwardErrors:
            error = giveAwardErrors[toonId]
            errStr = ''
            if error == gae.GenericAlreadyHaveError:
                errStr = "unknown error description for checkDuplicate %s" % catalogErrors[
                    toonId]
            else:
                tableValues[toonId] = AwardManagerConsts.GiveAwardErrorStrings[
                    error]
        for toonId in tableValues:
            name = ""
            if toonObjDict[toonId]:
                name = toonObjDict[toonId].getName()
            body += "<tr><td>" + str(toonId) + "</td><td>" + tableValues[
                toonId] + "</td><td>" + name + "</td>" + "</tr>\n"
        body += """
        </table>
        """
        if wrongGenderToonIds:
            body += "<br />Wrong Gender Toon Ids:\n"
            for toonId in wrongGenderToonIds:
                body += "%d " % toonId
            body += "\n"
        body += """
        </body>
        """
        browserReplyTo.respond(header + body + help + footer)

    def giveAwardToToon(self, context, replyToDoId, replyToClass, avId,
                        awardType, awardItemId):
        self.air.writeServerEvent('giveAwardCodeRequest', avId,
                                  '%s|%s' % (str(awardType), str(awardItemId)))
        dcId = next(self._dcRequestSerialGen)
        self._dcId2info[dcId] = ScratchPad(replyToClass=replyToClass,
                                           replyToDoId=replyToDoId,
                                           context=context)
        isDcRequest = True
        catalogItem = self._getCatalogItemObj(awardType, awardItemId)
        specialEventId = 1
        browserReplyTo = None
        specialCommands = GiveAfterOneMinute
        echoBack = ''
        GetToonsRequest(self, isDcRequest, dcId, [avId], catalogItem,
                        specialEventId, browserReplyTo, specialCommands,
                        echoBack)

    def sendGiveAwardToToonReply(self, dcId, result):
        info = self._dcId2info.pop(dcId)
        self.air.dispatchUpdateToGlobalDoId(info.replyToClass,
                                            "giveAwardToToonResult",
                                            info.replyToDoId,
                                            [info.context, result])

    def getMainMenu(self):
        """Create the main menu with forms for input."""
        header = """<HTML><HEAD><TITLE>Main Menu: Toontown Award  Manager</TITLE><link rel="stylesheet" type="text/css" href="/default.css">
        </HEAD>"""

        body = """<BODY><div id="contents"><center><P>"""
        body += """
            Enter Award Info
            <form>ToonIds:
            <input type="text" name="toonIds" />
            <br />
            SpecialEventId:
            <select name="specialEventId" />
              <option value="1">Fishing1</option>
              <option value="2">Fishing2</option>
              <option value="3">racing1</option>
              <option value="4">generic</option>
            </select>
            <br />
            Prize:
            <input type="text" name="prize" />
            <br />
            <input type="submit" value="Submit" />
            </form>            
            """

        footer = """</tbody></table></P></center></div><div id="footer">Toontown AwardManager</div></BODY></HTML>"""
        help = """<table height = "15%"></table><P><table width = "60%"><caption>Note</caption><tr><th scope=col>- Report any prizing issues to  [email protected]<br>- Report any technical issues to [email protected]</th></tr></table></P>"""
        return (header, body, footer, help)

    @classmethod
    def getClothingChoices(cls):
        """Return a dictionary of clothing choices. Key is the description, clothingtype are values."""
        values = {}
        for key in list(CatalogClothingItem.ClothingTypes.keys()):
            clothingItem = CatalogClothingItem.ClothingTypes[key]
            typeOfClothes = clothingItem[0]
            styleString = clothingItem[1]
            if typeOfClothes in (CatalogClothingItem.AShirt,
                                 CatalogClothingItem.ABoysShirt,
                                 CatalogClothingItem.AGirlsShirt):
                textString = TTLocalizer.AwardMgrShirt
                # if its an exclusive boy or girl item, then say so
                if typeOfClothes == CatalogClothingItem.ABoysShirt:
                    textString += ' ' + TTLocalizer.AwardMgrBoy
                elif typeOfClothes == CatalogClothingItem.AGirlsShirt:
                    textString += ' ' + TTLocalizer.AwardMgrGirl
                else:
                    textString += ' ' + TTLocalizer.AwardMgrUnisex
                textString += ' ' + TTLocalizer.ShirtStylesDescriptions[
                    styleString]
                if textString in values:
                    cls.notify.error("Fix %s, descriptions must be unique" %
                                     textString)
                values[textString] = key

        # do a 2nd for loop to ensure bottoms always goes last
        for key in list(CatalogClothingItem.ClothingTypes.keys()):
            clothingItem = CatalogClothingItem.ClothingTypes[key]
            typeOfClothes = clothingItem[0]
            styleString = clothingItem[1]
            if typeOfClothes in (CatalogClothingItem.AShorts,
                                 CatalogClothingItem.ABoysShorts,
                                 CatalogClothingItem.AGirlsShorts,
                                 CatalogClothingItem.AGirlsSkirt):
                textString = ""
                if typeOfClothes == CatalogClothingItem.AGirlsSkirt:
                    textString = TTLocalizer.AwardMgrSkirt
                else:
                    textString = TTLocalizer.AwardMgrShorts
                # if its an exclusive boy or girl item, then say so
                if typeOfClothes == CatalogClothingItem.ABoysShorts:
                    textString += ' ' + TTLocalizer.AwardMgrBoy
                elif typeOfClothes in (CatalogClothingItem.AGirlsShorts,
                                       CatalogClothingItem.AGirlsSkirt):
                    textString += ' ' + TTLocalizer.AwardMgrGirl
                else:
                    textString += ' ' + TTLocalizer.AwardMgrUnisex
                textString += ' ' + TTLocalizer.BottomStylesDescriptions[
                    styleString]
                if textString in values:
                    cls.notify.error("Fix %s, descriptions must be unique" %
                                     textString)
                values[textString] = key
        return values

    @classmethod
    def getFurnitureChoices(cls):
        """Return a dictionary of furniture choices. Key is the description , values is the furniture type key"""
        values = {}
        for key in list(CatalogFurnitureItem.FurnitureTypes.keys()):
            furnitureItem = CatalogFurnitureItem.FurnitureTypes[key]
            typeOfFurniture = key
            # we must not give animted furniture choices, the item type is wrong for it
            if typeOfFurniture in CatalogAnimatedFurnitureItem.AnimatedFurnitureItemKeys:
                continue
            descString = TTLocalizer.AwardManagerFurnitureNames[
                typeOfFurniture]
            if descString in values:
                cls.notify.error("Fix %s, descriptions must be unique" %
                                 descString)
            values[descString] = key
        return values

    @classmethod
    def getSpeedChatChoices(cls):
        """Return a dictionary of speed chat choices. Key is the description , values is the chat id"""
        values = {}
        allChatItems = CatalogGenerator.getAllChatItemsSold()
        for chatItem in allChatItems:
            speedChatKey = chatItem.customIndex
            textString = OTPLocalizer.CustomSCStrings[speedChatKey]
            # I really can't mess with the strings, I'll add the speedChatKey at the end
            keyStr = "%5d" % speedChatKey
            textString = keyStr + " " + textString
            # javascript messes up with a " in the string
            textString = textString.replace('"', "'")
            if textString in values:
                cls.notify.error("fix duplicate %s" % textString)
            values[textString] = speedChatKey
        return values

    @classmethod
    def getEmoteChoices(cls):
        """Return a dictionary of emote choices. Key is the description , values is the emote id"""
        values = {}
        for key in list(OTPLocalizer.EmoteFuncDict.keys()):
            descString = key
            emoteIndex = OTPLocalizer.EmoteFuncDict[key]
            if descString in values:
                cls.notify.error("Fix %s, descriptions must be unique" %
                                 descString)
            values[descString] = emoteIndex
        return values

    @classmethod
    def getBeanChoices(cls):
        """Return a dictionary of bean choices. Key is the description , values is the amount of beans"""
        values = {}
        for key in JellybeanRewardValues:
            descString = "%3d" % key
            if descString in values:
                cls.notify.error("Fix %s, descriptions must be unique" %
                                 descString)
            values[descString] = key
        return values

    @classmethod
    def getWallpaperChoices(cls):
        """Return a dictionary of wallpaper choices. Key is the description , values is the wallpaper id"""
        values = {}
        for key in list(CatalogWallpaperItem.WallpaperTypes.keys()):
            # the comments on CatalogWallpaperItem say 2920 to 2980 are problematic, so don't include them
            if key in (2920, 2930, 2940, 2950, 2960, 2970, 2980):
                continue
            # we have duplicate names, just add the key to be unique
            descString = "%5d " % key
            # ok it looks like some items are never offered, so if there's no name for it
            # lets not include it
            if key in TTLocalizer.WallpaperNames:
                descString += TTLocalizer.WallpaperNames[key]
                if descString in values:
                    cls.notify.error("Fix %s, descriptions must be unique" %
                                     descString)
                values[descString] = key
        return values

    @classmethod
    def getWindowViewChoices(cls):
        """Return a dictionary of window choices. Key is the description , values is the wallpaper id"""
        values = {}
        for key in list(CatalogWindowItem.WindowViewTypes.keys()):
            descString = ""
            descString += TTLocalizer.WindowViewNames[key]
            if descString in values:
                cls.notify.error("Fix %s, descriptions must be unique" %
                                 descString)
            values[descString] = key
        return values

    @classmethod
    def getFlooringChoices(cls):
        """Return a dictionary of flooring choices. Key is the description , values is the wallpaper id"""
        values = {}
        for key in list(CatalogFlooringItem.FlooringTypes.keys()):
            descString = "%5d " % key  # add key to make it unique
            descString += TTLocalizer.FlooringNames[key]
            if descString in values:
                cls.notify.error("Fix %s, descriptions must be unique" %
                                 descString)
            values[descString] = key
        return values

    @classmethod
    def getMouldingChoices(cls):
        """Return a dictionary of moulding choices. Key is the description , values is the wallpaper id"""
        values = {}
        for key in list(CatalogMouldingItem.MouldingTypes.keys()):
            descString = "%5d " % key  # add key to make it unique
            descString += TTLocalizer.MouldingNames[key]
            if descString in values:
                cls.notify.error("Fix %s, descriptions must be unique" %
                                 descString)
            values[descString] = key
        return values

    @classmethod
    def getWainscotingChoices(cls):
        """Return a dictionary of wainscotting choices. Key is the description , values is the wallpaper id"""
        values = {}
        for key in list(CatalogWainscotingItem.WainscotingTypes.keys()):
            descString = ""  #%5d " % key # add key to make it unique
            descString += TTLocalizer.WainscotingNames[key]
            if descString in values:
                cls.notify.error("Fix %s, descriptions must be unique" %
                                 descString)
            values[descString] = key
        return values

    @classmethod
    def getPetTrickChoices(cls):
        """Return a dictionary of pet trick choices. Key is the description , values is the wallpaper id"""
        values = {}
        allTricks = CatalogPetTrickItem.getAllPetTricks()
        for oneTrick in allTricks:
            descString = ""  #%5d " % key # add key to make it unique
            descString += oneTrick.getName()
            key = oneTrick.trickId
            if descString in values:
                cls.notify.error("Fix %s, descriptions must be unique" %
                                 descString)
            values[descString] = key
        return values

    @classmethod
    def getRentalChoices(cls):
        """Return a dictionary of pet rental choices. Key is the description , values is the wallpaper id"""
        values = {}
        allRentals = CatalogRentalItem.getAllRentalItems()
        for oneRental in allRentals:
            descString = ""  #%5d " % key # add key to make it unique
            descString += oneRental.getName()
            key = oneRental.typeIndex
            if descString in values:
                cls.notify.error("Fix %s, descriptions must be unique" %
                                 descString)
            values[descString] = key
        return values

    @classmethod
    def getAnimatedFurnitureChoices(cls):
        """Return a dictionary of furniture choices. Key is the description , values is the furniture type key"""
        values = {}
        for key in CatalogAnimatedFurnitureItem.AnimatedFurnitureItemKeys:
            furnitureItem = CatalogFurnitureItem.FurnitureTypes[key]
            typeOfFurniture = key
            descString = TTLocalizer.AwardManagerFurnitureNames[
                typeOfFurniture]
            if descString in values:
                cls.notify.error("Fix %s, descriptions must be unique" %
                                 descString)
            values[descString] = key
        return values

    @classmethod
    def getReversedAwardChoices(cls):
        """The key in the returned dictionaries should be catalog item numbers, the value should be desc strings."""
        if hasattr(cls, '_revAwardChoices'):
            return cls._revAwardChoices
        result = {}
        awardChoices = cls.getAwardChoices()
        for itemType in awardChoices:
            reversedDict = {}
            curDict = awardChoices[itemType]
            for descString in curDict:
                itemId = curDict[descString]
                if itemId in reversedDict:
                    cls.notify.error("item %s already in %s" %
                                     (itemId, reversedDict))
                reversedDict[itemId] = descString
            result[itemType] = reversedDict
        cls._revAwardChoices = result
        return result

    @classmethod
    def getAwardChoices(cls):
        """Return a tree of the choices for our drop down list."""
        # static data, cache it
        if hasattr(cls, '_awardChoices'):
            return cls._awardChoices
        result = {}
        for itemType in list(CatalogItemTypes.CatalogItemTypes.values()):
            if itemType in (CatalogItemTypes.INVALID_ITEM,
                            CatalogItemTypes.GARDENSTARTER_ITEM,
                            CatalogItemTypes.POLE_ITEM,
                            CatalogItemTypes.GARDEN_ITEM,
                            CatalogItemTypes.NAMETAG_ITEM,
                            CatalogItemTypes.TOON_STATUE_ITEM):
                # we really can't give this out as awards, so don't add them to the choices
                continue
            if itemType == CatalogItemTypes.CLOTHING_ITEM:
                values = cls.getClothingChoices()
                result[itemType] = values
            elif itemType == CatalogItemTypes.FURNITURE_ITEM:
                values = cls.getFurnitureChoices()
                result[itemType] = values
            elif itemType == CatalogItemTypes.CHAT_ITEM:
                values = cls.getSpeedChatChoices()
                result[itemType] = values
            elif itemType == CatalogItemTypes.EMOTE_ITEM:
                values = cls.getEmoteChoices()
                result[itemType] = values
            elif itemType == CatalogItemTypes.BEAN_ITEM:
                values = cls.getBeanChoices()
                result[itemType] = values
            elif itemType == CatalogItemTypes.WALLPAPER_ITEM:
                values = cls.getWallpaperChoices()
                result[itemType] = values
            elif itemType == CatalogItemTypes.WINDOW_ITEM:
                values = cls.getWindowViewChoices()
                result[itemType] = values
            elif itemType == CatalogItemTypes.FLOORING_ITEM:
                values = cls.getFlooringChoices()
                result[itemType] = values
            elif itemType == CatalogItemTypes.MOULDING_ITEM:
                values = cls.getMouldingChoices()
                result[itemType] = values
            elif itemType == CatalogItemTypes.WAINSCOTING_ITEM:
                values = cls.getWainscotingChoices()
                result[itemType] = values
            elif itemType == CatalogItemTypes.PET_TRICK_ITEM:
                values = cls.getPetTrickChoices()
                result[itemType] = values
            elif itemType == CatalogItemTypes.RENTAL_ITEM:
                values = cls.getRentalChoices()
                result[itemType] = values
            elif itemType == CatalogItemTypes.ANIMATED_FURNITURE_ITEM:
                values = cls.getAnimatedFurnitureChoices()
                result[itemType] = values

            else:
                values = {
                    "choice1": "Unimplemented One",
                    "choice2": "Unimplemented Two"
                }
                result[itemType] = values
        cls._awardChoices = result
        return result

    @staticmethod
    def getAwardTypeName(awardType):
        return TTLocalizer.CatalogItemTypeNames[awardType]

    @classmethod
    def getAwardText(cls, awardType, awardId):
        rAwardChoices = cls.getReversedAwardChoices()
        return rAwardChoices[awardType][awardId]

    def getExperimentalMenu(self):
        """Stuff to fool around with."""
        header = """
        <head>
        <TITLE>Main Menu: Toontown Award  Manager</TITLE><link rel="stylesheet" type="text/css" href="/default.css">
        <script type="text/javascript">
        function message()
        {
        alert("This alert box was called with the onload event");
        }

        function setOptions(chosen) {
        var selbox = document.myform.opttwo;
        selbox.options.length = 0;
        
        if (chosen == " ") {
          selbox.options[selbox.options.length] = new Option('Please select one of the options above first',' ');

        }\n"""

        for itemType in self.awardChoices:
            header += '\tif (chosen == "%s") {\n' % itemType
            secondChoices = self.awardChoices[itemType]
            sortedKeys = list(secondChoices.keys())
            sortedKeys.sort()
            for key in sortedKeys:
                header += "\t\tselbox.options[selbox.options.length] = new "
                header += 'Option("%s","%s");\n' % (key, secondChoices[key])
            header += '\t}\n'

        header += """
        
        }        
        </script>
        </head>
        """

        help = ""
        footer = ""
        body = """
        <html>
        <body ><center>
        <div id="contents" align="center"> 
        <form name="myform" action="awardGive">
        ToonIds:<input type="text" name="toonIds" size="50" />
            <br />
            SpecialEventId:
            <select name="specialEventId" />
            """
        for eventId in TTLocalizer.SpecialEventNames:
            body += '<option value="%d">%s</option>' % (
                eventId, TTLocalizer.SpecialEventNames[eventId])
        body += """    </select>
            <br />
        Item Type<select name="optone" size="1"
        onchange="setOptions(document.myform.optone.options[document.myform.optone.selectedIndex].value);">
        <option value=" " selected="selected"> </option>
        """

        for itemType in self.awardChoices:
            body += '<option value="%s">%s</option>\n' % (
                str(itemType), TTLocalizer.CatalogItemTypeNames[itemType])

        body += """
        </select><br>
        Item Details<select name="opttwo" size="1">
        <option value=" " selected="selected">Please select one of the options above first</option>
        </select>
        <br>
        Special Commands:
           <select name="specialCommands" />
        """

        commandsList = list(SpecialCommandStrs.keys())
        commandsList.sort()
        if uber.config.GetBool('awards-immediate'):
            commandsList.remove(GiveImmediately)
            commandsList.insert(0, GiveImmediately)
        for command in commandsList:
            body += "<option value=%d>%s</option>" % (
                command, SpecialCommandStrs[command])
        body += """   </select>
        <br />
        <input type="submit" value="Submit" />
        <!--
        <input type="button" name="go" value="Value Selected"
        onclick="alert(document.myform.opttwo.options[document.myform.opttwo.selectedIndex].value);">
        -->
       
        </form>        
        </script>
        </div>
        
        """
        help = """<table height = "15%"></table><P><table width = "60%"><caption>Note</caption><tr><th scope=col>- Use give award immediately only to test the award on your own toon. Try to remove the award may fail if 30 minutes have gone by since the award was given.<br><br>- Use Nuke All Awards only if the regular players can't enter toontown.<br><br>- Report any prizing issues to  [email protected]<br>- Report any issues to [email protected]</th></tr></table></P>"""
        footer = """</tbody></table></P></center><div id="footer">Toontown Award Manager</div></BODY></HTML>"""
        return (header, body, footer, help)