Ejemplo n.º 1
0
    def __init__(self,
                 layoutDatabase,
                 halfMapWidth=None,
                 mapHeight=None,
                 blockRatio=0.5,
                 duration=0,
                 maxPerTeam=100,
                 maxTotal=500,
                 authTagManager=None,
                 onceOnly=False,
                 saveReplay=False,
                 gamePrefix=None,
                 replayPath=None,
                 level=None,
                 gameType=None):

        self._serverCommandStack = []
        self._waitingForEmptyCommandQueue = []
        self.maxPerTeam = maxPerTeam
        self.maxTotalPlayers = min(2 * maxPerTeam, maxTotal)
        self.authTagManager = authTagManager

        self.agents = set()
        self.agentInfos = {}
        self.layoutDatabase = layoutDatabase

        self.pendingZombieNotices = []

        if level is None:
            level = StandardRandomLevel(halfMapWidth,
                                        mapHeight,
                                        blockRatio=blockRatio,
                                        duration=duration)
        world = ServerUniverse(self,
                               layoutDatabase,
                               authTagManager=authTagManager,
                               onceOnly=onceOnly,
                               level=level,
                               gameType=gameType)

        self.idManager = world.idManager

        self.gameRecorder = GameRecorder(self,
                                         world,
                                         saveReplay=saveReplay,
                                         gamePrefix=gamePrefix,
                                         replayPath=replayPath)

        super(LocalGame, self).__init__(world)

        world.onServerTickComplete.addListener(self.worldTickDone)
        self.gameRecorder.start()

        self.updateDelayLoop = WeakLoopingCall(self, 'updateDelays')
        self.updateDelayLoop.start(10, False)

        self.achievementManager = AchievementManager(self)
        self.achievementManager.start()
Ejemplo n.º 2
0
    def __init__(self, filename, *args, **kwargs):
        super(ReplayPlayer, self).__init__(*args, **kwargs)
        self.tickPeriod = TICK_PERIOD
        self.finished = False
        self.loop = WeakLoopingCall(self, 'tick')
        self.agentIds = []
        self.nextAgentId = 0

        self.settings, self.msgIterator = readReplay(open(filename, 'rb'))
Ejemplo n.º 3
0
    def __init__(self, level, playerCount=6):
        from trosnoth.bots.base import listAIs

        super(AddBotsForLobbyTrigger, self).__init__(level)
        self.playerCount = playerCount
        self.loop = WeakLoopingCall(self, 'recheck')
        self.agents = []
        self.newAgents = set()
        self.aiNames = listAIs(playableOnly=True)
Ejemplo n.º 4
0
 def __init__(self, game, aiClass, fromLevel, nick=None, *args, **kwargs):
     super(AIAgent, self).__init__(game=game, *args, **kwargs)
     self.aiClass = aiClass
     self.fromLevel = fromLevel
     self._initialisationNick = nick
     self.ai = None
     self.team = None
     self.requestedNick = None
     self._onBotSet = Event([])
     self._loop = WeakLoopingCall(self, '_tick')
Ejemplo n.º 5
0
    def __init__(self, authFactory, serverPort):
        ServerState.__init__(self, pages)
        self.authFactory = authFactory
        self.serverPort = serverPort
        self.nextEventListeners = []
        self._loop = WeakLoopingCall(self, 'keepEventPipeAlive')
        self._loop.start(5, False)

        self.gameServer = None
        self.authFactory.onPrimaryGameChanged.addListener(
            self.gameServerChanged)
Ejemplo n.º 6
0
    def __init__(self, filename, *args, **kwargs):
        super(ReplayPlayer, self).__init__(*args, **kwargs)
        self.tickPeriod = TICK_PERIOD
        self.file = open(filename, 'rb')
        self.finished = False
        self.loop = WeakLoopingCall(self, 'tick')
        self.agentIds = []
        self.nextAgentId = 0

        try:
            length = struct.unpack('!I', self.file.read(4))[0]
        except struct.error:
            raise ReplayFileError('invalid replay format')
        data = self.file.read(length)
        self.settings = unrepr(data)
    def __init__(self, game):
        self.game = game
        self.world = world = game.world
        self.lastSave = timeNow()

        # All players recorded this game, indexed by user id.
        self.allPlayers = {}

        defs = self.achievementDefs
        self.oncePerGameAchievements = [c(world) for c in defs.oncePerGame]
        self.oncePerTeamPerGameAchievements = [
            c(world, team) for c in defs.oncePerTeamPerGame
            for team in world.teams
        ]

        self.loop = WeakLoopingCall(self, 'save')
        self.started = False
        self.stopped = False
Ejemplo n.º 8
0
class StartGameWhenReadyTrigger(Trigger):
    def __init__(self, level):
        super(StartGameWhenReadyTrigger, self).__init__(level)
        self.loop = WeakLoopingCall(self, 'check')

    def doActivate(self):
        if not self.loop.running:
            self.loop.start(3, False)

    def doDeactivate(self):
        if self.loop.running:
            self.loop.stop()

    def check(self):
        try:
            self.world.voteArbiter.startNewGameIfReady()
        except Exception:
            log.exception('Error trying to start new game')
Ejemplo n.º 9
0
    def __init__(self, app, player, achievementId):
        super(AchievementBox, self).__init__(app)
        self.app = app
        self.player = player

        self.achievements = [achievementId]

        self.width = 453
        self.height = 75

        self._setColours()

        self.area = Area(
            FullScreenAttachedPoint(ScaledSize(0, -100), 'midbottom'),
            ScaledSize(self.width, self.height), 'midbottom')

        self.smlBox = Area(
            FullScreenAttachedPoint(ScaledSize(-self.width / 2 + 6,
                                               -104), 'midbottom'),
            ScaledSize(66, 66), 'bottomleft')

        self.titleText = TextElement(
            self.app, "ACHIEVEMENT UNLOCKED!", self.fonts.achievementTitleFont,
            Location(
                FullScreenAttachedPoint(
                    ScaledSize(73 / 2, -100 - self.height + 10), 'midbottom'),
                'midtop'), self.borderColour)

        self.nameText = TextElement(
            self.app,
            self.achievementDefs.getAchievementDetails(achievementId)[0],
            self.fonts.achievementNameFont,
            Location(
                FullScreenAttachedPoint(ScaledSize(73 / 2, -100 - 13),
                                        'midbottom'),
                'midbottom'), self.colours.black)

        self.elements = [self.titleText, self.nameText]
        self._updateImage()

        self.cycler = WeakLoopingCall(self, 'cycleAchievements')
        self.cycler.start(5.0, now=False)
Ejemplo n.º 10
0
class ReplayPlayer(Hub):
    '''
    Emulates a normal server by outputting the same messages that a server once
    did.
    '''
    def __init__(self, filename, *args, **kwargs):
        super(ReplayPlayer, self).__init__(*args, **kwargs)
        self.tickPeriod = TICK_PERIOD
        self.finished = False
        self.loop = WeakLoopingCall(self, 'tick')
        self.agentIds = []
        self.nextAgentId = 0

        self.settings, self.msgIterator = readReplay(open(filename, 'rb'))

    def popSettings(self):
        result, self.settings = self.settings, None
        return result

    def start(self):
        self.loop.start(self.tickPeriod)

    def stop(self):
        if self.loop.running:
            self.loop.stop()

    def tick(self):
        while True:
            try:
                msg = next(self.msgIterator)
            except StopIteration:
                self.finished = True
                if self.node:
                    while self.agentIds:
                        agentId = self.agentIds.pop(0)
                        # Give 2 seconds before ending the replay
                        reactor.callLater(2, self.node.agentDisconnected,
                                          agentId)
                self.loop.stop()
                break

            self.node.gotServerCommand(msg)
            if isinstance(msg, TickMsg):
                break

    def connectNewAgent(self, authTag=0):
        agentId = self.nextAgentId
        self.nextAgentId += 1
        self.agentIds.append(agentId)
        return agentId

    def disconnectAgent(self, agentId):
        self.agentIds.remove(agentId)

    def sendRequestToGame(self, agentId, msg):
        pass
Ejemplo n.º 11
0
class AchievementManager(object):
    SAVE_PERIOD = 20

    achievementDefs = availableAchievements

    def __init__(self, game):
        self.game = game
        self.world = world = game.world
        self.lastSave = timeNow()

        # All players recorded this game, indexed by user id.
        self.allPlayers = {}

        defs = self.achievementDefs
        self.oncePerGameAchievements = [c(world) for c in defs.oncePerGame]
        self.oncePerTeamPerGameAchievements = [
            c(world, team) for c in defs.oncePerTeamPerGame
            for team in world.teams
        ]

        self.loop = WeakLoopingCall(self, 'save')
        self.started = False
        self.stopped = False

    def save(self, force=False):
        for playerAchievements in list(self.allPlayers.values()):
            playerAchievements.saveProgress(force=force)

    def start(self):
        assert not self.started
        self.started = True
        for player in self.world.players:
            self.playerAdded(player)

        self.world.onPlayerAdded.addListener(self.playerAdded)
        self.loop.start(self.SAVE_PERIOD)

        for a in self.oncePerGameAchievements:
            a.onUnlocked.addListener(self.achievementUnlocked)
            a.start()
        for a in self.oncePerTeamPerGameAchievements:
            a.start()
            a.onUnlocked.addListener(self.achievementUnlocked)

    def stop(self):
        assert not self.stopped
        self.stopped = True
        self.loop.stop()
        self.world.onPlayerAdded.removeListener(self.playerAdded)
        self.save(force=True)
        for a in self.oncePerGameAchievements:
            a.stop()
            a.onUnlocked.removeListener(self.achievementUnlocked)
        for a in self.oncePerTeamPerGameAchievements:
            a.stop()
            a.onUnlocked.removeListener(self.achievementUnlocked)
        for p in list(self.allPlayers.values()):
            p.stop()

    @defer.inlineCallbacks
    def playerAdded(self, player):
        if self.world.isServer and not player.joinComplete:
            # We want to identify by user rather than nickname if the user
            # is authenticated.
            yield player.onJoinComplete.wait()

        name = player.identifyingName
        if name not in self.allPlayers:
            a = PlayerAchievements(self, player)
            self.allPlayers[name] = a
            a.start()
        else:
            self.allPlayers[name].rejoined(player)

    def triggerAchievement(self, player, achievementId):
        '''
        Called from stats manager to trigger a stats-related achievement.
        Never raises an exception.
        '''
        try:
            a = self.allPlayers[
                player.identifyingName].achievements[achievementId]
            a.achievementTriggered()
        except Exception:
            log.exception('Error triggering achievement')

    def achievementUnlocked(self, achievement, player):
        if player.user is not None:
            player.user.achievementUnlocked(achievement.idstring)
        self.game.sendServerCommand(
            AchievementUnlockedMsg(player.id, achievement.idstring))
Ejemplo n.º 12
0
class LocalGame(Game):
    def __init__(self,
                 layoutDatabase,
                 halfMapWidth=None,
                 mapHeight=None,
                 blockRatio=0.5,
                 duration=0,
                 maxPerTeam=100,
                 maxTotal=500,
                 serverInterface=None,
                 onceOnly=False,
                 saveReplay=False,
                 gamePrefix=None,
                 replayPath=None,
                 level=None,
                 gameType=None,
                 lobbySettings=None,
                 *args,
                 **kwargs):

        self._serverCommandStack = []
        self._waitingForEmptyCommandQueue = []
        self.maxPerTeam = maxPerTeam
        self.maxTotalPlayers = min(2 * maxPerTeam, maxTotal)
        self.serverInterface = serverInterface
        self.lobbySettings = lobbySettings

        self.agents = set()
        self.agentInfos = {}
        self.layoutDatabase = layoutDatabase

        if level is None:
            level = StandardRandomLevel(halfMapWidth,
                                        mapHeight,
                                        blockRatio=blockRatio,
                                        duration=duration)
        world = ServerUniverse(self,
                               layoutDatabase,
                               onceOnly=onceOnly,
                               level=level,
                               gameType=gameType)

        self.idManager = world.idManager

        self.gameRecorder = GameRecorder(self,
                                         world,
                                         saveReplay=saveReplay,
                                         gamePrefix=gamePrefix,
                                         replayPath=replayPath)

        super(LocalGame, self).__init__(world, *args, **kwargs)

        world.onServerTickComplete.addListener(self.worldTickDone)
        self.gameRecorder.start()

        self.updateDelayLoop = WeakLoopingCall(self, 'updateDelays')
        self.updateDelayLoop.start(10, False)

        self.achievementManager = AchievementManager(self)
        self.achievementManager.start()

        if VERIFY_PLAYER_CONSISTENCY:
            self.playerConsistencyVerifier = PlayerConsistencyVerifier(self)

    def updateDelays(self):
        for info in self.agentInfos.values():
            info.updateDelays()

    def addAgent(self, agent, user=None, authTag=0):
        agent.user = user
        info = AgentInfo(self, agent)
        self.agentInfos[agent] = info
        self.agents.add(agent)

    def detachAgent(self, agent):
        if agent not in self.agents:
            return
        self.agents.remove(agent)
        if agent.player:
            self.kickPlayer(agent.player.id)
            self.agentInfos[agent].takePlayer()
        del self.agentInfos[agent]
        agent.detached()

    def kickPlayer(self, playerId):
        '''
        Removes the player with the specified ID from the game.
        '''
        self.sendServerCommand(RemovePlayerMsg(playerId))

    def agentRequest(self, agent, msg):
        '''
        Some messages need to be delayed until the correct time comes.
        '''
        msg.tracePoint(self, 'agentRequest')
        if agent not in self.agents:
            # Probably just because it's a delayed call
            log.debug('LocalGame got message %s from unconnected agent', msg)
            return
        info = self.agentInfos[agent]
        info.requestFromAgent(msg)

    def dispatchDelayedRequest(self, agent, msg):
        '''
        Called by an AgentInfo when the correct time comes for a request to be
        dispatched.
        '''
        msg.tracePoint(self, 'dispatchDelayedRequest')
        msg.serverApply(self, agent)

    def setPlayerLimits(self, maxPerTeam, maxTotal=40):
        '''
        Changes the player limits in the current game. Note that this does not
        affect players who are already in the game.

        @param maxPerTeam: Maximum number of players per team at once
        @param maxTotal: Maximum number of players in the game at once
        '''
        self.maxPerTeam = maxPerTeam
        self.maxTotalPlayers = min(2 * maxPerTeam, maxTotal)

    def stop(self):
        super(LocalGame, self).stop()
        self.gameRecorder.stop()
        self.achievementManager.stop()
        self.updateDelayLoop.stop()
        self.idManager.stop()

    def worldTickDone(self):
        '''
        Called when the universe has ticked.
        '''
        self.checkCollisionsWithCollectables()
        for info in self.agentInfos.values():
            info.tick()

    def checkCollisionsWithCollectables(self):
        '''
        When a player runs into a collectable unit (e.g. a coin or the
        trosball), we pay attention not to where we think the collectable units
        are, but where the player's client thinks they are. To do this, we need
        to project the collectable units back in time based on the player's
        current delay.
        '''
        greatestDelay = 0
        for info in self.agentInfos.values():
            if not info.player or info.player.dead:
                continue
            greatestDelay = max(greatestDelay, info.currentDelay)

            for unit in self.world.getCollectableUnits():
                if unit.checkCollision(info.player, info.currentDelay):
                    unit.collidedWithPlayer(info.player)

        for unit in self.world.getCollectableUnits():
            unit.clearOldHistory(greatestDelay)

    def joinSuccessful(self, agent, playerId):
        super(LocalGame, self).joinSuccessful(agent, playerId)
        if agent in self.agentInfos:
            self.agentInfos[agent].givePlayer(self.world.getPlayer(playerId))

    def playerRemoved(self, player, playerId):
        super(LocalGame, self).playerRemoved(player, playerId)
        info = self.agentInfos.get(player.agent)
        if info:
            info.takePlayer()

    def sendServerCommand(self, msg):
        '''
        Sends a command to the universe and all attached agents. Typically
        called by message classes in serverApply().
        '''
        self._serverCommandStack.append(msg)
        if len(self._serverCommandStack) > 1:
            # Sometimes one of the calls below (e.g. self.world.consumeMsg())
            # triggers another sendServerCommand() call, so to make sure
            # that all the messages arrive at the clients in the same order
            # as the server, we queue them and release them as soon as the
            # first message is completely sent.
            msg.tracePoint(self, '(sendServerCommand: deferred)')
            return

        while self._serverCommandStack:
            cmd = self._serverCommandStack[0]
            cmd.tracePoint(self, 'sendServerCommand')
            if VERIFY_PLAYER_CONSISTENCY:
                self.playerConsistencyVerifier.preMessage(cmd)
            self.world.consumeMsg(cmd)
            self.gameRecorder.consumeMsg(cmd)
            if VERIFY_PLAYER_CONSISTENCY:
                self.playerConsistencyVerifier.postMessage(cmd)
            self.onServerCommand(cmd)
            cmd.tracePoint(self, 'done sendServerCommand')
            self._serverCommandStack.pop(0)

        # Release things that are waiting for the command stack to empty
        while not self._serverCommandStack and self._waitingForEmptyCommandQueue:
            d = self._waitingForEmptyCommandQueue.pop(0)
            try:
                d.callback(None)
            except Exception:
                log.exception('Error in queued callback')

    def waitForEmptyCommandQueue(self):
        '''
        :return: a Deferred which will callback when the queue of server
            commands to send is empty. This is useful to be sure that the
            world is in a consistent state (all messages have been
            processed).
        '''
        if not self._serverCommandStack:
            return defer.succeed(None)

        d = defer.Deferred()
        self._waitingForEmptyCommandQueue.append(d)
        return d

    def sendResync(self,
                   playerId,
                   reason='Your computer was out of sync with the server!'):
        '''
        Resyncs the position of the player with the given id. Typically called
        by message classes in serverApply().
        '''
        player = self.world.getPlayer(playerId)
        player.sendResync(reason)
Ejemplo n.º 13
0
class AddBotsForLobbyTrigger(Trigger):
    '''
    Adds a number of bots so even a single player can mess around in the
    lobby until others arrive.

    Keeps the total number of bots at least up to a particular minimum,
    adding and removing bots as necessary.
    '''
    def __init__(self, level, playerCount=6):
        from trosnoth.bots.base import listAIs

        super(AddBotsForLobbyTrigger, self).__init__(level)
        self.playerCount = playerCount
        self.loop = WeakLoopingCall(self, 'recheck')
        self.agents = []
        self.newAgents = set()
        self.aiNames = listAIs(playableOnly=True)

    def doActivate(self):
        if not self.loop.running:
            self.loop.start(3, False)

    def doDeactivate(self):
        if self.loop.running:
            self.loop.stop()
        self._stopAllAgents()

    @defer.inlineCallbacks
    def recheck(self):
        self._graduateNewAgents()
        yield self._adjustAgentsToTarget()

    def _graduateNewAgents(self):
        for agent in list(self.newAgents):
            if agent.player is not None:
                self.agents.append(agent)
                self.newAgents.remove(agent)

    def _stopAllAgents(self):
        if len(self.agents) != 0:
            log.info('AIInjector: Stopping all agents')
        for agent in self.agents:
            agent.stop()
            self.world.game.detachAgent(agent)
        self.agents = []

    @defer.inlineCallbacks
    def _adjustAgentsToTarget(self):
        worldPlayers = len(self.world.game.world.players)
        newAgents = len(self.newAgents)
        if self.playerCount > worldPlayers + newAgents:
            yield self._addAgents(self.playerCount - worldPlayers - newAgents)
        else:
            self._removeAgents(worldPlayers + newAgents - self.playerCount)

    @defer.inlineCallbacks
    def _addAgents(self, count):
        log.info('AIInjector: Adding %d agents', count)
        for i in range(count):
            agent = yield self.world.game.addBot(random.choice(self.aiNames))
            self.newAgents.add(agent)

    def _removeAgents(self, count):
        if count != 0:
            log.info('AIInjector: Removing %d agents', count)
        for i in range(count):
            if len(self.agents) == 0:
                break
            agent = self.agents.pop(0)
            agent.stop()
            self.world.game.detachAgent(agent)
Ejemplo n.º 14
0
 def __init__(self, level):
     super(StartGameWhenReadyTrigger, self).__init__(level)
     self.loop = WeakLoopingCall(self, 'check')
class GameInterface(framework.CompoundElement, ConcreteAgent):
    '''Interface for when we are connected to a game.'''

    achievementDefs = availableAchievements

    def __init__(self,
                 app,
                 game,
                 onDisconnectRequest=None,
                 onConnectionLost=None,
                 replay=False,
                 authTag=0):
        super(GameInterface, self).__init__(app, game=game)
        self.localState.onShoxwave.addListener(self.localShoxwaveFired)
        self.localState.onGameInfoChanged.addListener(self.gameInfoChanged)
        self.world.onOpenChatReceived.addListener(self.openChat)
        self.world.onTeamChatReceived.addListener(self.teamChat)
        self.world.onReset.addListener(self.worldReset)
        self.world.onGrenadeExplosion.addListener(self.grenadeExploded)
        self.world.onTrosballExplosion.addListener(self.trosballExploded)
        self.world.onBomberExplosion.addListener(self.trosballExploded)
        self.world.uiOptions.onChange.addListener(self.uiOptionsChanged)
        self.timingsLoop = WeakLoopingCall(self, '_sendPing')
        self.timingsLoop.start(1, now=False)

        self.subscribedPlayers = set()

        self.onDisconnectRequest = Event()
        if onDisconnectRequest is not None:
            self.onDisconnectRequest.addListener(onDisconnectRequest)

        self.onConnectionLost = Event()
        if onConnectionLost is not None:
            self.onConnectionLost.addListener(onConnectionLost)
        self.game = game

        self.joinDialogReason = None

        self.keyMapping = keyboard.KeyboardMapping(keymap.default_game_keys)
        self.runningPlayerInterface = None
        self.updateKeyMapping()
        self.gameViewer = viewManager.GameViewer(self.app, self, game, replay)
        if replay:
            self.joinController = None
        else:
            self.joinController = JoinGameController(self.app, self, self.game)
        self.detailsInterface = DetailsInterface(self.app, self)
        self.winnerMsg = WinnerMsg(app)
        self.timingInfo = TimingInfo(
            app,
            self,
            Location(Canvas(307, 768), 'midbottom'),
            app.screenManager.fonts.timingsFont,
        )
        self.gameInfoDisplay = GameInfoDisplay(
            app, self, Region(topleft=Screen(0.01, 0.05),
                              size=Canvas(330, 200)))
        self.hotkeys = hotkey.Hotkeys(self.app, self.keyMapping,
                                      self.detailsInterface.doAction)
        self.terminal = None

        self.joinInProgress = False

        self.vcInterface = None
        if replay:
            self.vcInterface = ViewControlInterface(self.app, self)

        self.ready = False
        defer.maybeDeferred(game.addAgent, self,
                            authTag=authTag).addCallback(self.addedAgent)

        self.setElements()

    def _sendPing(self):
        for i in range(3):
            data = bytes([random.randrange(256)])
            if data not in self.localState.pings:
                self.sendRequest(PingMsg(data))
                return

    def gameInfoChanged(self):
        self.gameInfoDisplay.refreshInfo()

    def addedAgent(self, result):
        self.ready = True
        if self.joinController:
            self.joinDialogReason = 'automatic'
            self.joinController.start()

    def spectatorWantsToJoin(self):
        if self.runningPlayerInterface or not self.joinController:
            return
        self.joinDialogReason = 'from menu'
        self.joinController.maybeShowJoinDialog(autoJoin=True)

    def sendRequest(self, msg):
        if not self.ready:
            # Not yet completely connected to game
            return
        super(GameInterface, self).sendRequest(msg)

    def worldReset(self, *args, **kwarsg):
        self.winnerMsg.hide()
        if self.ready and self.joinController:
            self.joinController.gotWorldReset()
        self.gameViewer.reset()

    def updateKeyMapping(self):
        # Set up the keyboard mapping.
        try:
            # Try to load keyboard mappings from the user's personal settings.
            with open(getPath(user, 'keymap'), 'r') as f:
                config = f.read()
            self.keyMapping.load(config)
            if self.runningPlayerInterface:
                self.runningPlayerInterface.keyMappingUpdated()
        except IOError:
            pass

    @ConnectionLostMsg.handler
    def connectionLost(self, msg):
        self.cleanUp()
        if self.joinController:
            self.joinController.hide()
        self.onConnectionLost.execute()

    def joined(self, player):
        '''Called when joining of game is successful.'''
        pygame.key.set_repeat()
        self.gameViewer.worldgui.overridePlayer(self.localState.player)
        self.runningPlayerInterface = pi = PlayerInterface(self.app, self)
        self.detailsInterface.setPlayer(pi.player)
        self.setElements()

        self.joinController.hide()
        self.gameViewer.leaderboard.update()

    def spectate(self):
        '''
        Called by join controller if user selects to only spectate.
        '''
        self.vcInterface = ViewControlInterface(self.app, self)
        self.setElements()
        self.joinController.hide()

    def joinDialogCancelled(self):
        if self.joinDialogReason == 'automatic':
            self.disconnect()
        else:
            self.spectate()

    def stop(self):
        super(GameInterface, self).stop()
        self.localState.onShoxwave.removeListener(self.localShoxwaveFired)
        self.localState.onGameInfoChanged.removeListener(self.gameInfoChanged)
        self.world.onOpenChatReceived.removeListener(self.openChat)
        self.world.onTeamChatReceived.removeListener(self.teamChat)
        self.world.onReset.removeListener(self.worldReset)
        self.world.onGrenadeExplosion.removeListener(self.grenadeExploded)
        self.world.onTrosballExplosion.removeListener(self.trosballExploded)
        self.world.onBomberExplosion.removeListener(self.trosballExploded)
        self.world.uiOptions.onChange.removeListener(self.uiOptionsChanged)
        self.timingsLoop.stop()
        self.gameViewer.stop()
        if self.runningPlayerInterface is not None:
            self.runningPlayerInterface.stop()

    def setElements(self):
        spectate = replay = False
        if self.runningPlayerInterface:
            self.elements = [
                self.gameViewer, self.runningPlayerInterface,
                self.gameInfoDisplay, self.hotkeys, self.detailsInterface,
                self.winnerMsg, self.timingInfo
            ]
        else:
            self.elements = [
                self.gameViewer, self.gameInfoDisplay, self.hotkeys,
                self.detailsInterface, self.winnerMsg, self.timingInfo
            ]
            if self.vcInterface is not None:
                self.elements.insert(2, self.vcInterface)

            if self.joinController:
                spectate = True
            else:
                replay = True
        self.detailsInterface.menuManager.setMode(spectate=spectate,
                                                  replay=replay)

    def toggleTerminal(self):
        if self.terminal is None:
            locs = {'app': self.app}
            if hasattr(self.app, 'getConsoleLocals'):
                locs.update(self.app.getConsoleLocals())
            self.terminal = console.TrosnothInteractiveConsole(
                self.app,
                self.app.screenManager.fonts.consoleFont,
                Region(size=Screen(1, 0.4), bottomright=Screen(1, 1)),
                locals=locs)
            self.terminal.interact().addCallback(self._terminalQuit)

        from trosnoth.utils.utils import timeNow
        if self.terminal in self.elements:
            if timeNow() > self._termWaitTime:
                self.elements.remove(self.terminal)
        else:
            self._termWaitTime = timeNow() + 0.1
            self.elements.append(self.terminal)
            self.setFocus(self.terminal)

    def _terminalQuit(self, result):
        if self.terminal in self.elements:
            self.elements.remove(self.terminal)
        self.terminal = None

    def disconnect(self):
        self.cleanUp()
        self.onDisconnectRequest.execute()

    def joinGame(self, nick, head, team, timeout=10):
        if self.joinInProgress:
            return

        if team is None:
            teamId = NEUTRAL_TEAM_ID
        else:
            teamId = team.id

        self.joinInProgress = True
        self.sendJoinRequest(teamId, nick, head)
        WeakCallLater(timeout, self, '_joinTimedOut')

    def setPlayer(self, player):
        if not player:
            self.gameViewer.worldgui.removeOverride()
            self.lostPlayer()

        super(GameInterface, self).setPlayer(player)

        if player:
            if __debug__ and globaldebug.enabled:
                globaldebug.localPlayerId = player.id

            self.joinInProgress = False
            self.joined(player)

    @CannotJoinMsg.handler
    def joinFailed(self, msg):
        self.joinInProgress = False
        self.joinController.joinFailed(msg.reasonId)

    def _joinTimedOut(self):
        if self.player or not self.joinInProgress:
            return
        self.joinInProgress = False
        self.joinController.joinFailed('timeout')

    def cleanUp(self):
        if self.gameViewer.timerBar is not None:
            self.gameViewer.timerBar = None
        pygame.key.set_repeat(300, 30)

    def uiOptionsChanged(self):
        if self.world.uiOptions.showGameOver:
            winner = self.world.uiOptions.winningTeam
            if winner:
                self.winnerMsg.show(
                    '{} win'.format(winner),
                    self.app.theme.colours.chatColour(winner),
                )
            else:
                self.winnerMsg.show('Game drawn', (128, 128, 128))
        else:
            self.winnerMsg.hide()

    @PlayerCoinsSpentMsg.handler
    def discard(self, msg):
        pass

    @AwardPlayerCoinMsg.handler
    def playerAwardedCoin(self, msg):
        if not self.localState.player:
            return
        if msg.sound and msg.playerId == self.localState.player.id:
            self.playSound('gotCoin')

    @PlayerHasElephantMsg.handler
    def gotElephant(self, msg, _lastElephantPlayer=[None]):
        player = self.world.getPlayer(msg.playerId)
        if player and player != _lastElephantPlayer[0]:
            message = '%s now has %s!' % (player.nick,
                                          player.world.uiOptions.elephantName)
            self.detailsInterface.newMessage(message)
            _lastElephantPlayer[0] = player

    @PlayerHasTrosballMsg.handler
    def gotTrosball(self, msg, _lastTrosballPlayer=[None]):
        player = self.world.playerWithId.get(msg.playerId)

        if player != _lastTrosballPlayer[0]:
            _lastTrosballPlayer[0] = player
            if player is None:
                message = 'The ball has been dropped!'
            else:
                message = '%s has the ball!' % (player.nick, )
            self.detailsInterface.newMessage(message)

    @AddPlayerMsg.handler
    def addPlayer(self, msg):
        player = self.world.getPlayer(msg.playerId)
        if player and player not in self.subscribedPlayers:
            self.subscribedPlayers.add(player)
            team = player.team if player.team else self.world.rogueTeamName
            message = '%s has joined %s' % (player.nick, team)
            self.detailsInterface.newMessage(message)
            player.onDied.addListener(partial(self.playerDied, player))

    @SetPlayerTeamMsg.handler
    def changeTeam(self, msg):
        self.defaultHandler(msg)  # Make sure the local player changes team
        player = self.world.getPlayer(msg.playerId)
        if player:
            message = '%s has joined %s' % (player.nick,
                                            self.world.getTeamName(msg.teamId))
            self.detailsInterface.newMessage(message)

    @RemovePlayerMsg.handler
    def handle_RemovePlayerMsg(self, msg):
        player = self.world.getPlayer(msg.playerId)
        if player:
            message = '%s has left the game' % (player.nick, )
            self.detailsInterface.newMessage(message)
            self.subscribedPlayers.discard(player)

    def lostPlayer(self):
        self.runningPlayerInterface.stop()
        self.runningPlayerInterface = None
        self.setElements()

    @CannotBuyUpgradeMsg.handler
    def notEnoughCoins(self, msg):
        if msg.reasonId == NOT_ENOUGH_COINS_REASON:
            text = 'Your team does not have enough coins.'
        elif msg.reasonId == CANNOT_REACTIVATE_REASON:
            text = 'You already have that item.'
        elif msg.reasonId == PLAYER_DEAD_REASON:
            text = 'You cannot buy an upgrade while dead.'
        elif msg.reasonId == GAME_NOT_STARTED_REASON:
            text = 'Upgrades can' 't be bought at this time.'
        elif msg.reasonId == TOO_CLOSE_TO_EDGE_REASON:
            text = 'You are too close to the zone edge.'
        elif msg.reasonId == TOO_CLOSE_TO_ORB_REASON:
            text = 'You are too close to the orb.'
        elif msg.reasonId == NOT_IN_DARK_ZONE_REASON:
            text = 'You are not in a dark friendly zone.'
        elif msg.reasonId == INVALID_UPGRADE_REASON:
            text = 'Upgrade not recognised by server.'
        elif msg.reasonId == DISABLED_UPGRADE_REASON:
            text = 'That upgrade is currently disabled.'
        else:
            text = 'You cannot buy that item at this time.'
        self.detailsInterface.newMessage(text)
        self.defaultHandler(msg)

    @PlayerHasUpgradeMsg.handler
    def gotUpgrade(self, msg):
        player = self.world.getPlayer(msg.playerId)
        if player:
            self.detailsInterface.upgradeUsed(player, msg.upgradeType)
            upgradeClass = self.world.getUpgradeType(msg.upgradeType)
            existing = player.items.get(upgradeClass)
            if not existing:
                if (self.detailsInterface.player is None
                        or self.detailsInterface.player.isFriendsWith(player)):
                    self.playSound('buyUpgrade')

        self.defaultHandler(msg)

    @ChatFromServerMsg.handler
    def gotChatFromServer(self, msg):
        self.detailsInterface.newMessage(msg.text.decode('utf-8'),
                                         error=msg.error)

    @TaggingZoneMsg.handler
    def zoneTagged(self, msg):
        try:
            zone = self.world.zoneWithId[msg.zoneId]
            zoneLabel = zone.defn.label
        except KeyError:
            zoneLabel = '<?>'

        if msg.playerId != NO_PLAYER:
            try:
                player = self.world.playerWithId[msg.playerId]
            except KeyError:
                nick = '<?>'
            else:
                nick = player.nick
            message = '%s tagged zone %s' % (nick, zoneLabel)

            self.detailsInterface.newMessage(message)

    def playerDied(self, target, killer, deathType):
        if deathType == OFF_MAP_DEATH_HIT:
            messages = [
                'fell into the void', 'looked into the abyss',
                'dug too greedily and too deep'
            ]
            message = '%s %s' % (target.nick, random.choice(messages))
        elif deathType == TROSBALL_DEATH_HIT:
            message = '%s was killed by the Trosball' % (target.nick, )
        elif deathType == BOMBER_DEATH_HIT:
            message = '%s head asplode' % (target.nick, )
            thisPlayer = self.detailsInterface.player
            if thisPlayer and target.id == thisPlayer.id:
                self.detailsInterface.doAction(ACTION_CLEAR_UPGRADE)
        else:
            if killer is None:
                message = '%s was killed' % (target.nick, )
                self.detailsInterface.newMessage(message)
            else:
                message = '%s killed %s' % (killer.nick, target.nick)

        self.detailsInterface.newMessage(message)

    @RespawnMsg.handler
    def playerRespawn(self, msg):
        player = self.world.getPlayer(msg.playerId)
        if player:
            message = '%s is back in the game' % (player.nick, )
            self.detailsInterface.newMessage(message)

    @CannotRespawnMsg.handler
    def respawnFailed(self, msg):
        if msg.reasonId == GAME_NOT_STARTED_REASON:
            message = 'The game has not started yet.'
        elif msg.reasonId == ALREADY_ALIVE_REASON:
            message = 'You are already alive.'
        elif msg.reasonId == BE_PATIENT_REASON:
            message = 'You cannot respawn yet.'
        elif msg.reasonId == ENEMY_ZONE_REASON:
            message = 'Cannot respawn outside friendly zone.'
        elif msg.reasonId == FROZEN_ZONE_REASON:
            message = 'That zone has been frozen!'
        else:
            message = 'You cannot respawn here.'
        self.detailsInterface.newMessage(
            message, self.app.theme.colours.errorMessageColour)

    def sendPrivateChat(self, player, targetId, text):
        self.sendRequest(ChatMsg(PRIVATE_CHAT, targetId, text=text.encode()))

    def sendTeamChat(self, player, text):
        self.sendRequest(ChatMsg(TEAM_CHAT, player.teamId, text=text.encode()))

    def sendPublicChat(self, player, text):
        self.sendRequest(ChatMsg(OPEN_CHAT, text=text.encode()))

    def openChat(self, text, sender):
        text = ': ' + text
        self.detailsInterface.newChat(text, sender)

    def teamChat(self, team, text, sender):
        player = self.detailsInterface.player
        if player and player.isFriendsWithTeam(team):
            text = ' (team): ' + text
            self.detailsInterface.newChat(text, sender)

    @AchievementUnlockedMsg.handler
    def achievementUnlocked(self, msg):
        player = self.world.getPlayer(msg.playerId)
        if not player:
            return

        achievementName = self.achievementDefs.getAchievementDetails(
            msg.achievementId)[0]
        self.detailsInterface.newMessage(
            '%s has unlocked "%s"!' % (player.nick, achievementName),
            self.app.theme.colours.achievementMessageColour)

        focusPlayer = self.detailsInterface.player
        if (focusPlayer is not None and focusPlayer.id == msg.playerId):
            self.detailsInterface.localAchievement(msg.achievementId)

    @ShotFiredMsg.handler
    def shotFired(self, msg):
        self.defaultHandler(msg)
        try:
            shot = self.world.getShot(msg.shotId)
        except KeyError:
            return

        pos = shot.pos
        dist = self.distance(pos)
        self.playSound('shoot', self.getSoundVolume(dist))

    def grenadeExploded(self, pos, radius):
        self.gameViewer.worldgui.addExplosion(pos)
        dist = self.distance(pos)
        self.playSound('explodeGrenade', self.getSoundVolume(dist))

    def trosballExploded(self, player):
        self.gameViewer.worldgui.addTrosballExplosion(player.pos)
        dist = self.distance(player.pos)
        self.playSound('explodeGrenade', self.getSoundVolume(dist))

    @FireShoxwaveMsg.handler
    def shoxwaveExplosion(self, msg):
        localPlayer = self.localState.player
        if localPlayer and msg.playerId == localPlayer.id:
            return
        self.gameViewer.worldgui.addShoxwaveExplosion((msg.xpos, msg.ypos))

    def localShoxwaveFired(self):
        localPlayer = self.localState.player
        self.gameViewer.worldgui.addShoxwaveExplosion(localPlayer.pos)

    @UpgradeChangedMsg.handler
    def upgradeChanged(self, msg):
        self.detailsInterface.upgradeDisplay.refresh()

    def distance(self, pos):
        return distance(self.gameViewer.viewManager.getTargetPoint(), pos)

    def getSoundVolume(self, distance):
        'The volume for something that far away from the player'
        # Up to 500px away is within the "full sound zone" - full sound
        distFromScreen = max(0, distance - 500)
        # 1000px away from "full sound zone" is 0 volume:
        return 1 - min(1, (distFromScreen / 1000.))

    def playSound(self, action, volume=1):
        self.app.soundPlayer.play(action, volume)

    @PlaySoundMsg.handler
    def playSoundFromServerCommand(self, msg):
        self.app.soundPlayer.playFromServerCommand(
            msg.filename.decode('utf-8'))

    @TickMsg.handler
    def handle_TickMsg(self, msg):
        super(GameInterface, self).handle_TickMsg(msg)
        self.timingInfo.ticksSeen += 1
Ejemplo n.º 16
0
class AchievementBox(framework.CompoundElement):
    achievementDefs = availableAchievements

    def __init__(self, app, player, achievementId):
        super(AchievementBox, self).__init__(app)
        self.app = app
        self.player = player

        self.achievements = [achievementId]

        self.width = 453
        self.height = 75

        self._setColours()

        self.area = Area(
            FullScreenAttachedPoint(ScaledSize(0, -100), 'midbottom'),
            ScaledSize(self.width, self.height), 'midbottom')

        self.smlBox = Area(
            FullScreenAttachedPoint(ScaledSize(-self.width / 2 + 6,
                                               -104), 'midbottom'),
            ScaledSize(66, 66), 'bottomleft')

        self.titleText = TextElement(
            self.app, "ACHIEVEMENT UNLOCKED!", self.fonts.achievementTitleFont,
            Location(
                FullScreenAttachedPoint(
                    ScaledSize(73 / 2, -100 - self.height + 10), 'midbottom'),
                'midtop'), self.borderColour)

        self.nameText = TextElement(
            self.app,
            self.achievementDefs.getAchievementDetails(achievementId)[0],
            self.fonts.achievementNameFont,
            Location(
                FullScreenAttachedPoint(ScaledSize(73 / 2, -100 - 13),
                                        'midbottom'),
                'midbottom'), self.colours.black)

        self.elements = [self.titleText, self.nameText]
        self._updateImage()

        self.cycler = WeakLoopingCall(self, 'cycleAchievements')
        self.cycler.start(5.0, now=False)

    def addAchievement(self, achievementId):
        self.achievements.append(achievementId)
        self.titleText.setText("%d ACHIEVEMENTS UNLOCKED!" %
                               len(self.achievements))

    def _updateImage(self):
        try:
            filepath = self.app.theme.getPath('achievements',
                                              '%s.png' % self.achievements[0])
        except IOError:
            filepath = self.app.theme.getPath('achievements', 'default.png')

        image = pygame.image.load(filepath).convert()
        image = pygame.transform.smoothscale(
            image,
            ScaledSize(64, 64).getSize(self.app))

        if type(self.elements[-1]) == PictureElement:
            self.elements.pop()

        self.image = PictureElement(
            self.app, image,
            Location(
                FullScreenAttachedPoint(ScaledSize(-self.width / 2 + 7, -105),
                                        'midbottom'), 'bottomleft'))

        self.elements.append(self.image)

    def cycleAchievements(self):
        del self.achievements[0]

        if len(self.achievements) == 0:
            self.cycler.stop()
            return
        elif len(self.achievements) == 1:
            self.titleText.setText("ACHIEVEMENT UNLOCKED!")
        else:
            self.titleText.setText("%d ACHIEVEMENTS UNLOCKED!" %
                                   len(self.achievements))

        self.nameText.setText(
            self.achievementDefs.getAchievementDetails(
                self.achievements[0])[0])
        self._updateImage()

    def _setColours(self):
        self.colours = self.app.theme.colours
        self.fonts = self.app.screenManager.fonts

        if self.player.teamId == 'A':
            self.borderColour = self.colours.achvBlueBorder
            self.bgColour = self.colours.achvBlueBackground
        else:
            self.borderColour = self.colours.achvRedBorder
            self.bgColour = self.colours.achvRedBackground

    def _getRect(self, area):
        return area.getRect(self.app)

    def draw(self, surface):
        mainRect = self._getRect(self.area)
        boxRect = self._getRect(self.smlBox)

        surface.fill(self.bgColour, mainRect)
        pygame.draw.rect(surface, self.borderColour, mainRect, 2)

        surface.fill(self.colours.white, boxRect)
        pygame.draw.rect(surface, self.borderColour, boxRect, 1)

        super(AchievementBox, self).draw(surface)
Ejemplo n.º 17
0
    def __init__(self,
                 layoutDatabase,
                 halfMapWidth=None,
                 mapHeight=None,
                 blockRatio=0.5,
                 duration=0,
                 maxPerTeam=100,
                 maxTotal=500,
                 serverInterface=None,
                 onceOnly=False,
                 saveReplay=False,
                 gamePrefix=None,
                 replayPath=None,
                 level=None,
                 gameType=None,
                 lobbySettings=None,
                 *args,
                 **kwargs):

        self._serverCommandStack = []
        self._waitingForEmptyCommandQueue = []
        self.maxPerTeam = maxPerTeam
        self.maxTotalPlayers = min(2 * maxPerTeam, maxTotal)
        self.serverInterface = serverInterface
        self.lobbySettings = lobbySettings

        self.agents = set()
        self.agentInfos = {}
        self.layoutDatabase = layoutDatabase

        if level is None:
            level = StandardRandomLevel(halfMapWidth,
                                        mapHeight,
                                        blockRatio=blockRatio,
                                        duration=duration)
        world = ServerUniverse(self,
                               layoutDatabase,
                               onceOnly=onceOnly,
                               level=level,
                               gameType=gameType)

        self.idManager = world.idManager

        self.gameRecorder = GameRecorder(self,
                                         world,
                                         saveReplay=saveReplay,
                                         gamePrefix=gamePrefix,
                                         replayPath=replayPath)

        super(LocalGame, self).__init__(world, *args, **kwargs)

        world.onServerTickComplete.addListener(self.worldTickDone)
        self.gameRecorder.start()

        self.updateDelayLoop = WeakLoopingCall(self, 'updateDelays')
        self.updateDelayLoop.start(10, False)

        self.achievementManager = AchievementManager(self)
        self.achievementManager.start()

        if VERIFY_PLAYER_CONSISTENCY:
            self.playerConsistencyVerifier = PlayerConsistencyVerifier(self)
    def __init__(self,
                 app,
                 game,
                 onDisconnectRequest=None,
                 onConnectionLost=None,
                 replay=False,
                 authTag=0):
        super(GameInterface, self).__init__(app, game=game)
        self.localState.onShoxwave.addListener(self.localShoxwaveFired)
        self.localState.onGameInfoChanged.addListener(self.gameInfoChanged)
        self.world.onOpenChatReceived.addListener(self.openChat)
        self.world.onTeamChatReceived.addListener(self.teamChat)
        self.world.onReset.addListener(self.worldReset)
        self.world.onGrenadeExplosion.addListener(self.grenadeExploded)
        self.world.onTrosballExplosion.addListener(self.trosballExploded)
        self.world.onBomberExplosion.addListener(self.trosballExploded)
        self.world.uiOptions.onChange.addListener(self.uiOptionsChanged)
        self.timingsLoop = WeakLoopingCall(self, '_sendPing')
        self.timingsLoop.start(1, now=False)

        self.subscribedPlayers = set()

        self.onDisconnectRequest = Event()
        if onDisconnectRequest is not None:
            self.onDisconnectRequest.addListener(onDisconnectRequest)

        self.onConnectionLost = Event()
        if onConnectionLost is not None:
            self.onConnectionLost.addListener(onConnectionLost)
        self.game = game

        self.joinDialogReason = None

        self.keyMapping = keyboard.KeyboardMapping(keymap.default_game_keys)
        self.runningPlayerInterface = None
        self.updateKeyMapping()
        self.gameViewer = viewManager.GameViewer(self.app, self, game, replay)
        if replay:
            self.joinController = None
        else:
            self.joinController = JoinGameController(self.app, self, self.game)
        self.detailsInterface = DetailsInterface(self.app, self)
        self.winnerMsg = WinnerMsg(app)
        self.timingInfo = TimingInfo(
            app,
            self,
            Location(Canvas(307, 768), 'midbottom'),
            app.screenManager.fonts.timingsFont,
        )
        self.gameInfoDisplay = GameInfoDisplay(
            app, self, Region(topleft=Screen(0.01, 0.05),
                              size=Canvas(330, 200)))
        self.hotkeys = hotkey.Hotkeys(self.app, self.keyMapping,
                                      self.detailsInterface.doAction)
        self.terminal = None

        self.joinInProgress = False

        self.vcInterface = None
        if replay:
            self.vcInterface = ViewControlInterface(self.app, self)

        self.ready = False
        defer.maybeDeferred(game.addAgent, self,
                            authTag=authTag).addCallback(self.addedAgent)

        self.setElements()
Ejemplo n.º 19
0
class ReplayPlayer(Hub):
    '''
    Emulates a normal server by outputting the same messages that a server once
    did.
    '''

    def __init__(self, filename, *args, **kwargs):
        super(ReplayPlayer, self).__init__(*args, **kwargs)
        self.tickPeriod = TICK_PERIOD
        self.file = open(filename, 'rb')
        self.finished = False
        self.loop = WeakLoopingCall(self, 'tick')
        self.agentIds = []
        self.nextAgentId = 0

        try:
            length = struct.unpack('!I', self.file.read(4))[0]
        except struct.error:
            raise ReplayFileError('invalid replay format')
        data = self.file.read(length)
        self.settings = unrepr(data)

    def popSettings(self):
        result, self.settings = self.settings, None
        return result

    def start(self):
        self.loop.start(self.tickPeriod)

    def stop(self):
        if self.loop.running:
            self.loop.stop()

    def tick(self):
        while True:
            data = self.file.read(4)
            if not data:
                self.finished = True
                if self.node:
                    while self.agentIds:
                        agentId = self.agentIds.pop(0)
                        # Give 2 seconds before ending the replay
                        reactor.callLater(
                            2, self.node.agentDisconnected, agentId)
                self.loop.stop()
                break
            length = struct.unpack('!I', data)[0]

            data = self.file.read(length)
            try:
                msg = clientMsgs.buildMessage(data)
            except MessageTypeError:
                log.warning('WARNING: UNKNOWN MESSAGE: %r' % (data,))
                continue

            self.node.gotServerCommand(msg)
            if isinstance(msg, TickMsg):
                break

    def connectNewAgent(self):
        agentId = self.nextAgentId
        self.nextAgentId += 1
        self.agentIds.append(agentId)
        return agentId

    def disconnectAgent(self, agentId):
        self.agentIds.remove(agentId)

    def sendRequestToGame(self, agentId, msg):
        pass
Ejemplo n.º 20
0
class AIAgent(ConcreteAgent):
    '''
    Base class for an AI agent.
    '''
    def __init__(self, game, aiClass, fromLevel, nick=None, *args, **kwargs):
        super(AIAgent, self).__init__(game=game, *args, **kwargs)
        self.aiClass = aiClass
        self.fromLevel = fromLevel
        self._initialisationNick = nick
        self.ai = None
        self.team = None
        self.requestedNick = None
        self._onBotSet = Event([])
        self._loop = WeakLoopingCall(self, '_tick')

    def __str__(self):
        if self.ai:
            bot = self.ai
        else:
            bot = 'None (was {})'.format(self.aiClass.__name__)
        return '{}<{}>'.format(self.__class__.__name__, bot)

    def start(self, team=None):
        self.team = team
        self._loop.start(2)

    def stop(self):
        super(AIAgent, self).stop()
        self._loop.stop()
        if self.ai:
            self.ai.disable()
            self.ai = None

    def detached(self):
        super().detached()
        self.stop()

    def _tick(self):
        if self.ai is not None:
            return

        if self.fromLevel and self._initialisationNick:
            nick = self._initialisationNick
        else:
            nick = self.aiClass.nick

        if self.team is None:
            teamId = NEUTRAL_TEAM_ID
        else:
            teamId = self.team.id

        self._joinGame(nick, teamId)

    def _joinGame(self, nick, teamId):
        self.requestedNick = nick
        self.sendJoinRequest(teamId, nick, bot=True, fromLevel=self.fromLevel)

    @CannotJoinMsg.handler
    def _joinFailed(self, msg):
        r = msg.reasonId
        nick = self.requestedNick

        if r == GAME_FULL_REASON:
            message = 'full'
        elif r == UNAUTHORISED_REASON:
            message = 'not authenticated'
        elif r == NICK_USED_REASON:
            message = 'nick in use'
        elif r == USER_IN_GAME_REASON:
            message = 'user already in game'  # Should never happen
        elif r == ALREADY_JOINED_REASON:
            message = 'tried to join twice'  # Should never happen
        else:
            message = repr(r)

        log.error('Join failed for AI %r (%s)', nick, message)
        self.stop()

    def setPlayer(self, player):
        if player is None and self.ai:
            self.ai.disable()
            self.ai = None

        super(AIAgent, self).setPlayer(player)

        if player:
            self.requestedNick = None
            self.ai = self.aiClass(self.world, self.localState.player, self)
            self._onBotSet()

    @defer.inlineCallbacks
    def getBot(self):
        '''
        @return: a Deferred which fires with this agent's Bot object,
            as soon as it has one.
        '''
        if self.ai is not None:
            defer.returnValue(self.ai)

        yield self._onBotSet.wait()
        defer.returnValue(self.ai)

    @TickMsg.handler
    def handle_TickMsg(self, msg):
        super(AIAgent, self).handle_TickMsg(msg)
        if self.ai:
            self.ai.consumeMsg(msg)

    @ResyncPlayerMsg.handler
    def handle_ResyncPlayerMsg(self, msg):
        super(AIAgent, self).handle_ResyncPlayerMsg(msg)
        if self.ai:
            self.ai.playerResynced()

    @WorldResetMsg.handler
    def handle_WorldResetMsg(self, msg):
        super().handle_WorldResetMsg(msg)
        if self.ai:
            self.ai.worldReset()

    def defaultHandler(self, msg):
        super(AIAgent, self).defaultHandler(msg)
        if self.ai:
            self.ai.consumeMsg(msg)
Ejemplo n.º 21
0
class WebServer(ServerState):
    def __init__(self, authFactory, serverPort):
        ServerState.__init__(self, pages)
        self.authFactory = authFactory
        self.serverPort = serverPort
        self.nextEventListeners = []
        self._loop = WeakLoopingCall(self, 'keepEventPipeAlive')
        self._loop.start(5, False)

        self.gameServer = None
        self.authFactory.onPrimaryGameChanged.addListener(
            self.gameServerChanged)

    def gameServerChanged(self, gameServer):
        if self.gameServer:
            world = self.gameServer.game.world
            world.onPlayerAdded.removeListener(self.playerCountChanged)
            world.onPlayerRemoved.removeListener(self.playerCountChanged)
            world.onStartMatch.removeListener(self.levelChanged)
            world.onTeamScoreChanged.removeListener(self.teamScoreChanged)
        self.gameServer = gameServer
        if gameServer:
            world = gameServer.game.world
            world.onPlayerAdded.addListener(self.playerCountChanged)
            world.onPlayerRemoved.addListener(self.playerCountChanged)
            world.onStartMatch.addListener(self.levelChanged)
            world.onTeamScoreChanged.addListener(self.teamScoreChanged)

        self.transmitEvent(self.getInitialEvents())

    def inLobby(self):
        game = self.gameServer.game
        return game.world.uiOptions.showReadyStates

    def getPlayerCount(self):
        game = self.gameServer.game
        return len([p for p in game.world.players if not p.bot])

    def playerCountChanged(self, *args, **kwargs):
        if self.inLobby():
            playerCount = self.getPlayerCount()
            playersString = '1 player' if playerCount == 1 else '%d players' % (
                playerCount, )
            self.transmitEvent('message("%s in lobby.");' % (playersString, ))

    def levelChanged(self, *args, **kwargs):
        self.transmitEvent(self.getInitialEvents())

    def getInitialEvents(self):
        if self.gameServer is None:
            return 'hideScoreboard();message("No running games on server.");\n'

        if self.inLobby():
            playerCount = self.getPlayerCount()
            return 'hideScoreboard();message("%d players in lobby.");\n' % (
                playerCount, )

        return 'hideMessage();%s\n' % (self.getScoreMessage(), )

    def teamScoreChanged(self, *args, **kwargs):
        self.transmitEvent(self.getScoreMessage())

    def getScoreMessage(self):
        world = self.gameServer.game.world
        blueTeam, redTeam = world.teams[:2]
        blueZones = blueTeam.numZonesOwned
        redZones = redTeam.numZonesOwned
        totalZones = len(world.zones)
        neutralZones = totalZones - blueZones - redZones

        return 'score(%d,%d,%d,%d,%d);' % (
            blueTeam.orbScore,
            redTeam.orbScore,
            blueZones,
            neutralZones,
            redZones,
        )

    def waitForEvent(self):
        d = defer.Deferred()
        self.nextEventListeners.append(d)
        return d

    def transmitEvent(self, jsCommand):
        listeners = self.nextEventListeners
        self.nextEventListeners = []
        for d in listeners:
            d.callback(jsCommand + '\n')

    def keepEventPipeAlive(self):
        '''
        To make sure that a reverse proxy doesn't close connections due to
        inactivity.
        '''
        self.transmitEvent('')
Ejemplo n.º 22
0
 def __init__(self):
     self.lastNote = None
     self.ignoreUntil = None
     self.reset()
     self.monitor = WeakLoopingCall(self, 'notePeriod')
     self.monitor.start(self.NOTE_TIME)
Ejemplo n.º 23
0
class LagAnalyser(object):
    TIME_SPAN = 5
    NOTE_TIME = 0.1

    def __init__(self):
        self.lastNote = None
        self.ignoreUntil = None
        self.reset()
        self.monitor = WeakLoopingCall(self, 'notePeriod')
        self.monitor.start(self.NOTE_TIME)

    def reset(self):
        self.startTime = timeNow()
        self.ticks = [self.startTime]

    def notePeriod(self):
        '''
        Called periodically to allow the analyser to notice if the reactor is
        not running smoothly.
        '''
        now = timeNow()
        if self.lastNote is None:
            self.lastNote = now
            return
        period = now - self.lastNote
        self.lastNote = now

        if period - self.NOTE_TIME >= self.NOTE_TIME:
            # Reactor congestion: reset tally
            self.ignoreUntil = now + self.NOTE_TIME

    def gotTick(self):
        now = timeNow()

        if self.ignoreUntil is not None:
            if now > self.ignoreUntil:
                self.reset()
            else:
                return

        self.ticks.append(now)
        limit = now - self.TIME_SPAN
        while self.ticks[0] < limit:
            self.ticks.pop(0)

    def getIdealUILag(self):
        '''
        Based on the ticks received, calculates the time which the UI should
        lag behind receiving messages, in order for things to be displayed
        smoothly.
        '''
        final = self.ticks[-1]
        initial = max(self.startTime, final - self.TIME_SPAN)

        oldT = initial
        oldValue = upper = lower = 0
        for t in self.ticks:
            value = oldValue + (t - oldT)
            upper = max(upper, value)
            value -= TICK_PERIOD
            lower = min(lower, value)
            oldT, oldValue = t, value

        return upper - lower