from sqlalchemy.orm.exc import NoResultFound
from xonstat.elo import EloProcessor
from xonstat.models import DBSession, Server, Map, Game, PlayerGameStat, PlayerWeaponStat
-from xonstat.models import PlayerRank, PlayerCaptime
+from xonstat.models import PlayerRank, PlayerCaptime, PlayerGameFragMatrix
from xonstat.models import TeamGameStat, PlayerGameAnticheat, Player, Hashkey, PlayerNick
from xonstat.util import strip_colors, qfont_decode, verify_request, weapon_map
# bots who played in the match
self.bots = []
+ # player indexes for those who played
+ self.player_indexes = set()
+
# distinct weapons that we have seen fired
self.weapons = set()
if sub_key.endswith("cnt-fired"):
player_fired_weapon = True
self.add_weapon_fired(sub_key)
- elif sub_key == 'scoreboard-score' and int(sub_value) != 0:
+ elif sub_key == 'scoreboard-score' and int(round(float(sub_value))) != 0:
player_nonzero_score = True
elif sub_key == 'scoreboard-fastest':
player_fastest = True
played = self.played_in_game(player)
human = self.is_human_player(player)
+ if played:
+ self.player_indexes.add(int(player["i"]))
+
if played and human:
self.humans.append(player)
except:
minimum_required_players = 2
+ # Make an exception for CTS since it can be done by individuals and there is no Elo anyway
+ if submission.game_type_cd == "cts":
+ minimum_required_players = 1
+
return len(submission.humans) >= minimum_required_players
return game_type_cd not in {'cts'}
+def should_do_frag_matrix(game_type_cd):
+ """True if the game type should record frag matrix values. False otherwise."""
+ return game_type_cd in {
+ 'as', 'ca', 'ctf', 'dm', 'dom', 'ft', 'freezetag', 'ka', 'kh', 'rune', 'tdm',
+ }
+
+
def gametype_elo_eligible(game_type_cd):
"""True of the game type should process Elos. False otherwise."""
return game_type_cd in {'duel', 'dm', 'ca', 'ctf', 'tdm', 'ka', 'ft'}
game.mod = mod[:64]
# There is some drift between start_dt (provided by app) and create_dt
- # (default in the database), so we'll make them the same until this is
+ # (default in the database), so we'll make them the same until this is
# resolved.
game.create_dt = start_dt
if game_type_cd in 'ca' 'dm' 'duel' 'rune' 'tdm':
pgstat.kills = pgstat.deaths = pgstat.suicides = 0
- if game_type_cd == 'cq':
- pgstat.kills = pgstat.deaths = pgstat.suicides = pgstat.captures = 0
- pgstat.drops = 0
-
if game_type_cd == 'ctf':
pgstat.kills = pgstat.captures = pgstat.pickups = pgstat.drops = 0
pgstat.returns = pgstat.carrier_frags = 0
pgstat.captures = pgstat.drops = pgstat.pushes = pgstat.destroys = 0
pgstat.carrier_frags = 0
- if game_type_cd == 'lms':
- pgstat.kills = pgstat.deaths = pgstat.suicides = pgstat.lives = 0
-
if game_type_cd == 'nb':
pgstat.kills = pgstat.deaths = pgstat.suicides = pgstat.captures = 0
pgstat.drops = 0
- if game_type_cd == 'rc':
- pgstat.kills = pgstat.deaths = pgstat.suicides = pgstat.laps = 0
-
return pgstat
# all team game modes have a score, so we'll zero that out always
teamstat.score = 0
- if game_type_cd in 'ca' 'ft' 'lms' 'ka':
+ if game_type_cd in 'ca' 'ft' 'ka':
teamstat.rounds = 0
if game_type_cd == 'ctf':
return players_by_hashkey
+def create_frag_matrix(session, player_indexes, pgstat, events):
+ """
+ Construct a PlayerFragMatrix object from the events of a given player.
+
+ :param session: The DBSession we're adding objects to.
+ :param player_indexes: The set of player indexes of those that actually played in the game.
+ :param pgstat: The PlayerGameStat object of the player whose frag matrix we want to create.
+ :param events: The raw player events of the above player.
+ :return: PlayerFragMatrix
+ """
+ player_index = int(events.get("i", None))
+
+ # "kills-4" -> 4
+ victim_index = lambda x: int(x.split("-")[1])
+
+ matrix = {victim_index(k): int(v) for (k, v) in events.items()
+ if k.startswith("kills-") and victim_index(k) in player_indexes}
+
+ if len(matrix) > 0:
+ pfm = PlayerGameFragMatrix(pgstat.game_id, pgstat.player_game_stat_id, pgstat.player_id,
+ player_index, matrix)
+
+ session.add(pfm)
+ return pfm
+ else:
+ return None
+
+
def submit_stats(request):
"""
Entry handler for POST stats submissions.
hashkeys_by_player_id = {}
for hashkey, player in players_by_hashkey.items():
events = events_by_hashkey[hashkey]
+
pgstat = create_game_stat(session, game, gmap, player, events)
pgstats.append(pgstat)
+ if should_do_frag_matrix(submission.game_type_cd):
+ create_frag_matrix(session, submission.player_indexes, pgstat, events)
+
# player ranking opt-out
- if 'r' in events and events['r'] != '0':
+ if 'r' in events and events['r'] == '0':
+ log.debug("Excluding player {} from ranking calculations (opt-out)"
+ .format(pgstat.player_id))
+ else:
elo_pgstats.append(pgstat)
if player.player_id > 1: