Exemplo n.º 1
0
    def start_vote_session(self, client=None):
        if self.last_vote_start_time:
            now = time.time()
            how_long_ago = now - self.last_vote_start_time
            next_vote_allowed_seconds = self.last_vote_start_time + (self.vote_interval * 60) - now
            if how_long_ago < self.vote_interval * 60:
                self.info(
                    "cannot start vote because a previous vote started less than %s" % minutesStr(self.vote_interval))
                if client:
                    client.message(self.getMessage('votemap_feedback_interval_error',
                            {'time': minutesStr("%ss" % next_vote_allowed_seconds)}))
                return

        # get the maps to choose from
        available_maps = self._getAvailableMaps()
        self.debug("available maps : %s" % available_maps)
        current_mapinfo = self._get_current_mapinfo()

        excluded_maps = []
        if self.exclude_current_map:
            excluded_maps.append(current_mapinfo)
        if self.exclude_next_map:
            excluded_maps.append(self._get_next_mapinfo())

        options = self.map_options_pickup_strategy(available_maps, self.number_of_vote_options, current_mapinfo, excluded_maps)

        if len(options) >= 2:
            self.current_vote_session = VoteSession(self, self._adminPlugin, self._messages)
            for mapinfo in options:
                mapinfo['label'] = self._make_map_label(mapinfo)
                self.current_vote_session.addOption(mapinfo)
            self.current_vote_session.start()

            self.console.say(self.getMessage("vote_announcement_started"))
            time.sleep(.5)
            self.announce_vote_options()
            self.current_vote_session_timer = Timer(interval=self.vote_duration * 60,
                function=self.stop_current_vote_session)
            self.current_vote_session_timer.start()

            if client:
                client.message(self.getMessage('votemap_feedback_success'))
        else:
            self.warning("cannot start a vote with less than 2 options")
            if client:
                client.message(self.getMessage('votemap_feedback_not_enough_maps'))
Exemplo n.º 2
0
class VotemapPlugin(Plugin):
    def __init__(self, console, config=None):
        self._adminPlugin = None

        self.vote_interval = None
        self.nextmap_display_interval = None
        self.vote_duration = None
        self.vote_threshold = None
        self.number_of_vote_options = None
        self.exclude_current_map = None
        self.exclude_next_map = None
        self.map_options_pickup_strategy = None
        self.map_options_source_file = None

        self.current_vote_session = None
        self.current_vote_session_timer = None
        self.last_vote_start_time = None

        self.nextmap_timer = None
        self.game_modes_shortnames = {}

        Plugin.__init__(self, console, config)


    ################################################################################################################
    #
    #    Plugin interface implementation
    #
    ################################################################################################################

    def onLoadConfig(self):
        """\
        This is called after loadConfig(). Any plugin private variables loaded
        from the config need to be reset here.
        """
        self._load_default_messages()
        self._load_messages()
        self._load_preferences()
        self._load_preference_pickup_strategy()
        self._load_preference_map_options_source()
        self._init_nextmap_display_timer()


    def onStartup(self):
        """\
        Initialize plugin settings
        """
        try:
            self.game_modes_shortnames = GAME_MODES_SHORTNAMES_BY_GAME[self.console.gameName]
        except KeyError:
            self.critical("unsupported game: %s" % self.console.gameName)
            raise

        # get the admin plugin so we can register commands
        self._adminPlugin = self.console.getPlugin('admin')
        if not self._adminPlugin:
            # something is wrong, can't start without admin plugin
            self.error('Could not find admin plugin')
            return False
        self._registerCommands()

        # Register our events
        self.registerEvent(b3.events.EVT_GAME_MAP_CHANGE)
        self.registerEvent(b3.events.EVT_CLIENT_DISCONNECT)


    def onEvent(self, event):
        """\
        Handle intercepted events
        """
        if event.type == b3.events.EVT_GAME_MAP_CHANGE:
            self.cancel_current_vote_session()

        elif event.type == b3.events.EVT_CLIENT_DISCONNECT:
            if self.current_vote_session:
                self.current_vote_session.removeVoter(event.client)


    def enable(self):
        Plugin.enable(self)
        self._init_nextmap_display_timer()

    def disable(self):
        self.cancel_current_vote_session()
        self._cancel_nextmap_display_timer()
        Plugin.disable(self)


    ################################################################################################################
    #
    #   Commands implementations
    #
    ################################################################################################################

    def cmd_votemap(self, data, client, cmd=None):
        """\
        Start a vote for the next map
        """
        if self.current_vote_session:
            client.message(self.getMessage('votemap_feedback_in_progress_error'))
        else:
            self.start_vote_session(client)

    def cmd_cancelvote(self, data, client, cmd=None):
        """\
        Cancel the current vote
        """
        if not self.current_vote_session:
            client.message(self.getMessage('cancelvote_feedback_no_vote_in_progress'))
        else:
            self.cancel_current_vote_session()
            client.message(self.getMessage('cancelvote_feedback_success'))

    def _cmd_vote(self, data, client, cmd=None):
        """\
        vote for a map
        """
        if self.current_vote_session:
            self.current_vote_session.vote(client, cmd.command)

    def cmd_v(self, data, client, cmd=None):
        """\
        Display current vote options
        """
        if not self.current_vote_session:
            client.message(self.getMessage("v_feedback_no_vote_in_progress"))
        else:
            self.announce_vote_options(client)


    ################################################################################################################
    #
    #   Other methods
    #
    ################################################################################################################

    def announce_vote_options(self, client=None):
        """display the vote options to all players or the given player"""

        def write(txt, client=None):
            if txt[0] == '/': # if line starts with '/', then it won't be displayed by the BF3 server
                txt = ' ' + txt
            if client:
                self.console.write(("admin.say", txt, 'player', client.cid ))
            else:
                self.console.write(("admin.say", txt, 'all'))

        for two_options in two_by_two(self.current_vote_session.getOptions()):
            options = [ljust("/%s %s" % x, pct=48) for x in two_options]
            write(" | ".join(options), client)

    def start_vote_session(self, client=None):
        if self.last_vote_start_time:
            now = time.time()
            how_long_ago = now - self.last_vote_start_time
            next_vote_allowed_seconds = self.last_vote_start_time + (self.vote_interval * 60) - now
            if how_long_ago < self.vote_interval * 60:
                self.info(
                    "cannot start vote because a previous vote started less than %s" % minutesStr(self.vote_interval))
                if client:
                    client.message(self.getMessage('votemap_feedback_interval_error',
                            {'time': minutesStr("%ss" % next_vote_allowed_seconds)}))
                return

        # get the maps to choose from
        available_maps = self._getAvailableMaps()
        self.debug("available maps : %s" % available_maps)
        current_mapinfo = self._get_current_mapinfo()

        excluded_maps = []
        if self.exclude_current_map:
            excluded_maps.append(current_mapinfo)
        if self.exclude_next_map:
            excluded_maps.append(self._get_next_mapinfo())

        options = self.map_options_pickup_strategy(available_maps, self.number_of_vote_options, current_mapinfo, excluded_maps)

        if len(options) >= 2:
            self.current_vote_session = VoteSession(self, self._adminPlugin, self._messages)
            for mapinfo in options:
                mapinfo['label'] = self._make_map_label(mapinfo)
                self.current_vote_session.addOption(mapinfo)
            self.current_vote_session.start()

            self.console.say(self.getMessage("vote_announcement_started"))
            time.sleep(.5)
            self.announce_vote_options()
            self.current_vote_session_timer = Timer(interval=self.vote_duration * 60,
                function=self.stop_current_vote_session)
            self.current_vote_session_timer.start()

            if client:
                client.message(self.getMessage('votemap_feedback_success'))
        else:
            self.warning("cannot start a vote with less than 2 options")
            if client:
                client.message(self.getMessage('votemap_feedback_not_enough_maps'))


    def stop_current_vote_session(self):
        """End the current vote session"""
        if self.current_vote_session:
            self.current_vote_session.stop()

            winning_option = self.current_vote_session.getWinningOption(min_votes=self.vote_threshold)
            if winning_option:
                self.last_vote_start_time = self.current_vote_session.getStartTime()
                self._set_next_map(winning_option['name'],  winning_option['gamemode'], winning_option['num_of_rounds'])
                self.console.say(self.current_vote_session.getCurrentVotesAsTextLines())
                self.console.say(self.getMessage("voteresult_map_chosen", {'map': winning_option['label']}))
                self.console.saybig(self.getMessage("voteresult_map_chosen", {'map': winning_option['label']}))
                self.current_vote_session = None
            else:
                self.console.say(self.getMessage("voteresult_no_map_chosen"))

            self.__reset_current_vote_session()


    def cancel_current_vote_session(self):
        if self.current_vote_session:
            self.__reset_current_vote_session()
            self.console.say(self.getMessage('vote_announcement_cancel'))


    def __reset_current_vote_session(self):
        self.current_vote_session = None
        if self.current_vote_session_timer:
            self.current_vote_session_timer.cancel()
            self.current_vote_session_timer = None


    def _registerCommands(self):
        if 'commands' in self.config.sections():
            for cmd in self.config.options('commands'):
                level = self.config.get('commands', cmd)
                sp = cmd.split('-')
                alias = None
                if len(sp) == 2:
                    cmd, alias = sp
                func = getattr(self, "cmd_" + cmd, None)
                if func:
                    self._adminPlugin.registerCommand(self, cmd, level, func, alias)
                else:
                    self.warning("config defines unknown command '%s'" % cmd)


    def _load_messages(self):
        """ loads messages from config """
        if self.config.has_section('messages'):
            self._messages.update(dict(self.config.items('messages', raw=True)))


    def _load_default_messages(self):
        self._messages = {
            "vote_announcement_started": "Type: /1, /2, ... in chat to vote for the next map",
            "vote_announcement_cancel": "Vote canceled",
            "votemap_feedback_interval_error": "Next map vote allowed in %(time)s",
            "votemap_feedback_in_progress_error": "A vote is already in progress",
            "votemap_feedback_not_enough_maps": "Not enough maps to vote for",
            "votemap_feedback_success": "New vote session started",
            "cancelvote_feedback_success": "Vote canceled",
            "cancelvote_feedback_no_vote_in_progress": "There is no vote to cancel",
            "votecast_feedback_success": "You voted for map %(map)s",
            "voteresult_no_map_chosen": "No single map won the vote",
            "voteresult_map_chosen": "Voted map : %(map)s",
            "v_feedback_no_vote_in_progress": "no vote in progress, type !votemap to request a vote",
            }


    def _load_preferences(self):
        self.vote_interval = self._load_preference(self.config.getint, 'vote_interval', 5)
        self.nextmap_display_interval = self._load_preference(self.config.getint, 'nextmap_display_interval', 0)
        self.vote_duration = self._load_preference(self.config.getint, 'vote_duration', 4)
        self.vote_threshold = self._load_preference(self.config.getint, 'vote_threshold', 0)
        self.number_of_vote_options = self._load_preference(self.config.getint, 'number_of_vote_options', 4)
        self.exclude_current_map = self._load_preference(self.config.getboolean, 'exclude_current_map', True)
        self.exclude_next_map = self._load_preference(self.config.getboolean, 'exclude_next_map', True)


    def _load_preference_pickup_strategy(self):
        default_value = 'sequential'
        try:
            value = self.config.get('preferences', "map_options_pickup_strategy")
            if value not in ('sequential', 'random'):
                raise ValueError('expecting one of sequential/random')
        except ConfigParser.NoOptionError, err:
            self.warning("Cannot find config option preferences.map_options_pickup_strategy. Using default value instead")
            value = default_value
        except ValueError, err:
            self.warning("Cannot read value for config option preferences.map_options_pickup_strategy. Expecting one of sequential/random. Using default value instead")
            value = default_value