Sync rosters at match start, harden dedup and TIME handling, add scoreboard and damage summary
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <[email protected]>
This commit is contained in:
+70
-29
@@ -32,6 +32,15 @@ class EventParser:
|
||||
self.json_logger = json_logger
|
||||
self.unknown_logger = unknown_logger
|
||||
|
||||
def _sync_match_time(self, data):
|
||||
"""Sync the live match timer; a malformed TIME value must not discard
|
||||
the whole event (the sync runs inside the handler try blocks)"""
|
||||
try:
|
||||
self.game_state.server_info.match_time = int(data['TIME'])
|
||||
self.game_state.server_info.match_time_last_sync = time.time()
|
||||
except (TypeError, ValueError):
|
||||
logger.debug(f'Malformed TIME value: {data.get("TIME")!r}')
|
||||
|
||||
def parse_event(self, message):
|
||||
"""
|
||||
Parse JSON event and return formatted message string
|
||||
@@ -118,9 +127,7 @@ class EventParser:
|
||||
"""Handle PLAYER_SWITCHTEAM event"""
|
||||
|
||||
# Get Match Time
|
||||
if 'TIME' in data:
|
||||
self.game_state.server_info.match_time = int(data['TIME'])
|
||||
self.game_state.server_info.match_time_last_sync = time.time()
|
||||
self._sync_match_time(data)
|
||||
|
||||
if 'KILLER' not in data:
|
||||
return None
|
||||
@@ -163,9 +170,7 @@ class EventParser:
|
||||
"""Handle PLAYER_DEATH and PLAYER_KILL events"""
|
||||
|
||||
# Get Match Time
|
||||
if 'TIME' in data:
|
||||
self.game_state.server_info.match_time = int(data['TIME'])
|
||||
self.game_state.server_info.match_time_last_sync = time.time()
|
||||
self._sync_match_time(data)
|
||||
|
||||
if 'VICTIM' not in data:
|
||||
return None
|
||||
@@ -283,9 +288,7 @@ class EventParser:
|
||||
"""Handle ROUND_OVER events for CA"""
|
||||
|
||||
# Get Match Time
|
||||
if 'TIME' in data:
|
||||
self.game_state.server_info.match_time = int(data['TIME'])
|
||||
self.game_state.server_info.match_time_last_sync = time.time()
|
||||
self._sync_match_time(data)
|
||||
|
||||
team_won = data.get('TEAM_WON')
|
||||
round_num = data.get('ROUND', 0)
|
||||
@@ -305,9 +308,7 @@ class EventParser:
|
||||
"""Handle PLAYER_MEDAL event"""
|
||||
|
||||
# Get Match Time
|
||||
if 'TIME' in data:
|
||||
self.game_state.server_info.match_time = int(data['TIME'])
|
||||
self.game_state.server_info.match_time_last_sync = time.time()
|
||||
self._sync_match_time(data)
|
||||
|
||||
name = data.get('NAME', 'Unknown')
|
||||
medal = data.get('MEDAL', 'UNKNOWN')
|
||||
@@ -340,18 +341,35 @@ class EventParser:
|
||||
"""Handle MATCH_STARTED event"""
|
||||
|
||||
# Get Match Time
|
||||
if 'TIME' in data:
|
||||
self.game_state.server_info.match_time = int(data['TIME'])
|
||||
self.game_state.server_info.match_time_last_sync = time.time()
|
||||
self._sync_match_time(data)
|
||||
|
||||
# The PLAYERS array is the authoritative roster at match start: sync
|
||||
# teams (numeric codes via TEAM_MAP) and reset scores, clear dead
|
||||
# markers, and drop stale dedup signatures (TIME restarts at 0)
|
||||
players = []
|
||||
for player in data.get('PLAYERS', []):
|
||||
name = player.get('NAME')
|
||||
if not name:
|
||||
continue
|
||||
players.append(name)
|
||||
team = player.get('TEAM')
|
||||
if team is None:
|
||||
team = 'SPECTATOR'
|
||||
self.game_state.player_tracker.update_team(name, team)
|
||||
self.game_state.player_tracker.add_player(name, score='0', ping='0')
|
||||
self.game_state.player_tracker.set_score(name, '0')
|
||||
# The array is the complete match roster: drop tracked players absent
|
||||
# from it (they left before the match started)
|
||||
roster_names = {strip_color_codes(name) for name in players}
|
||||
for tracked in list(self.game_state.server_info.players.keys()):
|
||||
if strip_color_codes(tracked) not in roster_names:
|
||||
self.game_state.player_tracker.remove_player(tracked)
|
||||
self.game_state.server_info.dead_players.clear()
|
||||
self.game_state.event_deduplicator.reset()
|
||||
|
||||
if self.game_state.server_info.is_team_mode():
|
||||
return f"^8^2[GAME ON]^0 ^7Match has started - ^1^8RED ^0^7vs. ^4^8BLUE\n"
|
||||
|
||||
players = []
|
||||
for player in data.get('PLAYERS', []):
|
||||
name = player.get('NAME', 'Unknown')
|
||||
players.append(name)
|
||||
|
||||
if players:
|
||||
formatted = "^0 vs. ^8".join(players)
|
||||
return f"^8^2[GAME ON]^0 ^7Match has started - ^8^7{formatted}\n"
|
||||
@@ -362,12 +380,7 @@ class EventParser:
|
||||
"""Handle MATCH_REPORT event"""
|
||||
|
||||
# Get Match Time
|
||||
if 'TIME' in data:
|
||||
self.game_state.server_info.match_time = int(data['TIME'])
|
||||
self.game_state.server_info.match_time_last_sync = time.time()
|
||||
|
||||
if not self.game_state.server_info.is_team_mode():
|
||||
return None
|
||||
self._sync_match_time(data)
|
||||
|
||||
# Restarted matches (server shutdown mid-game) have no winner; aborts
|
||||
# and forfeits still have final scores that name one
|
||||
@@ -377,16 +390,44 @@ class EventParser:
|
||||
except (TypeError, ValueError):
|
||||
return None
|
||||
|
||||
# Final standings from the per-player STATS array
|
||||
stats_rows = []
|
||||
for entry in data.get('STATS') or []:
|
||||
if not isinstance(entry, dict):
|
||||
continue
|
||||
entry_name = entry.get('NAME')
|
||||
if not entry_name:
|
||||
continue
|
||||
try:
|
||||
entry_score = int(entry.get('SCORE', 0))
|
||||
except (TypeError, ValueError):
|
||||
entry_score = 0
|
||||
stats_rows.append((strip_color_codes(entry_name), entry_score,
|
||||
entry.get('KILLS', '?'), entry.get('DEATHS', '?')))
|
||||
stats_rows.sort(key=lambda x: x[1], reverse=True)
|
||||
scoreboard = ''
|
||||
if stats_rows:
|
||||
formatted = ' '.join(f"{name} {score} ({kills}/{deaths})"
|
||||
for name, score, kills, deaths in stats_rows[:5])
|
||||
scoreboard = f"^8^1[SCORES]^7 {formatted}\n"
|
||||
|
||||
if not self.game_state.server_info.is_team_mode():
|
||||
return scoreboard or None
|
||||
|
||||
red_score = int(data.get('TSCORE0', '0'))
|
||||
blue_score = int(data.get('TSCORE1', '0'))
|
||||
report_prefix = "^8^1[GAME OVER]"
|
||||
|
||||
if red_score > blue_score:
|
||||
return f"{report_prefix} ^7The ^1RED TEAM ^7WINS^0 by a score of ^8^1{red_score} ^0^7to ^8^4{blue_score}\n"
|
||||
winner = f"{report_prefix} ^7The ^1RED TEAM ^7WINS^0 by a score of ^8^1{red_score} ^0^7to ^8^4{blue_score}\n"
|
||||
elif blue_score > red_score:
|
||||
return f"{report_prefix} ^7The ^4BLUE TEAM ^7WINS^0 by a score of ^8^4{blue_score} ^0^7to ^8^1{red_score}\n"
|
||||
winner = f"{report_prefix} ^7The ^4BLUE TEAM ^7WINS^0 by a score of ^8^4{blue_score} ^0^7to ^8^1{red_score}\n"
|
||||
else:
|
||||
return f"{report_prefix} ^7The match is a TIE^0 with a score of ^8^1{red_score} ^0^7to ^8^4{blue_score}\n"
|
||||
winner = f"{report_prefix} ^7The match is a TIE^0 with a score of ^8^1{red_score} ^0^7to ^8^4{blue_score}\n"
|
||||
|
||||
if scoreboard:
|
||||
winner = f"{winner.rstrip(chr(10))}\n{scoreboard}"
|
||||
return winner
|
||||
|
||||
def _handle_player_stats(self, data):
|
||||
"""Handle PLAYER_STATS event"""
|
||||
|
||||
Reference in New Issue
Block a user