Source code for filters.players
# ../filters/players.py
"""Provides player filtering functionality."""
# =============================================================================
# >> IMPORTS
# =============================================================================
# Python
# Ast
import ast
# Site-Package Imports
# ConfigObj
from configobj import ConfigObj
# Source.Python Imports
# Core
from core import GAME_NAME
# Entities
from entities.helpers import index_from_edict
# Filters
from filters.iterator import _IterObject
# Paths
from paths import SP_DATA_PATH
# Players
from players import PlayerGenerator
from players.entity import Player
from players.helpers import index_from_userid
# =============================================================================
# >> ALL DECLARATION
# =============================================================================
__all__ = ('get_default_filters',
'parse_filter',
'PlayerIter',
)
# =============================================================================
# >> GLOBAL VARIABLES
# =============================================================================
# Get the team's file for the current game
_game_teams = ConfigObj(SP_DATA_PATH / 'teams' / GAME_NAME + '.ini')
# =============================================================================
# >> PLAYER ITERATION CLASSES
# =============================================================================
[docs]class PlayerIter(_IterObject):
"""Player iterate class."""
@staticmethod
[docs] def iterator():
"""Iterate over all :class:`players.entity.Player` objects."""
# Loop through all players on the server
for edict in PlayerGenerator():
# Yield the Player instance for the current edict
yield Player(index_from_edict(edict))
# =============================================================================
# PLAYER TEAM CLASSES
# =============================================================================
class _PlayerTeams(dict):
"""Class used to store team names for the current game."""
def __setitem__(self, item, value):
"""Add a _Team instance for the given team name."""
# Get the _Team instance
instance = _Team(value)
# Add the _Team instance for the given team name
super().__setitem__(item, instance)
# Get the _PlayerTeams instance
_player_teams = _PlayerTeams()
class _Team(object):
"""Class used to store a team number and compare to a given player."""
def __init__(self, team):
"""Store the team number for future use."""
self.team = team
def _player_is_on_team(self, player):
"""Return whether the player is on the team."""
return player.team == self.team
# =============================================================================
# >> FILTER REGISTRATION
# =============================================================================
# Register the filter functions
PlayerIter.register_filter('all', lambda player: True)
PlayerIter.register_filter('bot', lambda player: player.is_bot())
PlayerIter.register_filter('human', lambda player: not player.is_bot())
PlayerIter.register_filter('alive', lambda player: not player.dead)
PlayerIter.register_filter('dead', lambda player: player.dead)
# Loop through all teams in the game's team file
for _team in _game_teams.get('names', {}):
# Add the team to the _player_teams dictionary
_player_teams[_team] = int(_game_teams['names'][_team])
# Register the filter
PlayerIter.register_filter(
_team, _player_teams[_team]._player_is_on_team)
# Loop through all base team names
for _number, _team in enumerate(('un', 'spec', 't', 'ct')):
# Has the team already been added to the _player_teams dictionary
if _team in _player_teams:
# If it has been added, do not re-add it
continue
# Add the team to the _player_teams dictionary
_player_teams[_team] = _number
# Register the filter
PlayerIter.register_filter(
_team, _player_teams[_team]._player_is_on_team)
# =============================================================================
# >> FUNCTIONS
# =============================================================================
[docs]def parse_filter(expr, filters=None):
"""Parse an expression and return a set containing :class:`Player` objects.
:param str expr: The expression to parse.
:param dict filters: Filters that should be used instead of the default
filters. All filter names must be lowercase.
:rtype: set
:raise SyntaxError: Raised if the expression is invalid.
:raise KeyError: Raised if an invalid filter was used.
:raise ValueError: Raised if the conversion from userid to index failed.
"""
if filters is None:
filters = get_default_filters()
return _parse_node(ast.parse(expr, mode='eval').body, filters)
[docs]def get_default_filters():
"""Return the default filters (all available filters)."""
return dict((name, set(PlayerIter(name))) for name in PlayerIter.filters)
def _parse_node(node, filters):
"""Parse an ast node."""
# Userid?
if isinstance(node, ast.Num):
return set([Player(index_from_userid(node.n))])
# Filter?
if isinstance(node, ast.Name):
return filters[node.id.casefold()]
# + or -?
if isinstance(node.op, (ast.Add, ast.Sub)):
left = _parse_node(node.left, filters)
right = _parse_node(node.right, filters)
if isinstance(node.op, ast.Add):
return left | right
return left - right
# TODO:
# Figure out how to get the offset of the wrong node (for raising a better
# SyntaxError).
raise SyntaxError('Unsupported node type: {}'.format(type(node)))