Populate team rosters from a LIVESTATS team token and tag the damage list
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <[email protected]>
This commit is contained in:
+12
-3
@@ -149,16 +149,25 @@ class PlayerTracker:
|
|||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def update_livestats(self, clean_name, score, dealt, taken, ping):
|
def update_livestats(self, clean_name, score, dealt, taken, ping, team=None):
|
||||||
"""Update live score/damage/ping from a LIVESTATS console print (clean name)"""
|
"""Update live score/damage/ping from a LIVESTATS console print (clean name).
|
||||||
|
With a team token (extended livedamage format) the team is authoritative —
|
||||||
|
it sorts tracked players and adds unknown ones to the roster, which is
|
||||||
|
what populates teams correctly during warmup."""
|
||||||
for player_name, player_data in self.server_info.players.items():
|
for player_name, player_data in self.server_info.players.items():
|
||||||
if strip_color_codes(player_name) == clean_name:
|
if strip_color_codes(player_name) == clean_name:
|
||||||
player_data['score'] = str(score)
|
player_data['score'] = str(score)
|
||||||
player_data['ping'] = str(ping)
|
player_data['ping'] = str(ping)
|
||||||
player_data['dealt'] = str(dealt)
|
player_data['dealt'] = str(dealt)
|
||||||
player_data['taken'] = str(taken)
|
player_data['taken'] = str(taken)
|
||||||
|
if team is not None:
|
||||||
|
self.update_team(player_name, team)
|
||||||
return True
|
return True
|
||||||
return False
|
if team is None:
|
||||||
|
return False
|
||||||
|
self.add_player(clean_name, score=str(score), ping=str(ping))
|
||||||
|
self.update_team(clean_name, team)
|
||||||
|
return True
|
||||||
|
|
||||||
def remove_player(self, name):
|
def remove_player(self, name):
|
||||||
"""Remove player from tracking"""
|
"""Remove player from tracking"""
|
||||||
|
|||||||
@@ -18,9 +18,10 @@ import time
|
|||||||
from collections import deque
|
from collections import deque
|
||||||
|
|
||||||
from .constants import (INFO_WINDOW_HEIGHT, INFO_WINDOW_Y, OUTPUT_WINDOW_Y, INPUT_WINDOW_HEIGHT,
|
from .constants import (INFO_WINDOW_HEIGHT, INFO_WINDOW_Y, OUTPUT_WINDOW_Y, INPUT_WINDOW_HEIGHT,
|
||||||
MIN_ROWS, MIN_COLS, OUTPUT_SCROLLBACK_LINES, TEAM_MODES, MAX_COMMAND_HISTORY)
|
MIN_ROWS, MIN_COLS, OUTPUT_SCROLLBACK_LINES, TEAM_MODES, MAX_COMMAND_HISTORY,
|
||||||
|
TEAM_COLORS)
|
||||||
from .cvars import autocomplete, COMMAND_SIGNATURES, get_signature_with_highlight, get_argument_suggestions, COMMAND_ARGUMENTS
|
from .cvars import autocomplete, COMMAND_SIGNATURES, get_signature_with_highlight, get_argument_suggestions, COMMAND_ARGUMENTS
|
||||||
from .formatter import strip_color_codes
|
from .formatter import get_team_prefix, strip_color_codes
|
||||||
|
|
||||||
logger = logging.getLogger('ui')
|
logger = logging.getLogger('ui')
|
||||||
|
|
||||||
@@ -431,9 +432,11 @@ def _draw_server_menu(screen, servers, names, selected):
|
|||||||
screen.refresh()
|
screen.refresh()
|
||||||
|
|
||||||
|
|
||||||
def format_damage_summary(server_info):
|
def format_damage_summary(server_info, player_tracker=None):
|
||||||
"""One-line damage summary (top 3 by damage dealt) from LIVESTATS counters;
|
"""One-line damage summary (top 3 by damage dealt) from LIVESTATS counters,
|
||||||
None when no damage data exists yet. Dealt/taken are stored as strings."""
|
tagged with the tracked team of each player (data-driven, so tags work
|
||||||
|
during warmup too); None when no damage data exists yet. Dealt/taken are
|
||||||
|
stored as strings."""
|
||||||
entries = []
|
entries = []
|
||||||
for player_name, player_data in server_info.players.items():
|
for player_name, player_data in server_info.players.items():
|
||||||
try:
|
try:
|
||||||
@@ -447,8 +450,13 @@ def format_damage_summary(server_info):
|
|||||||
if not entries:
|
if not entries:
|
||||||
return None
|
return None
|
||||||
entries.sort(key=lambda x: x[1], reverse=True)
|
entries.sort(key=lambda x: x[1], reverse=True)
|
||||||
summary = ' '.join(f"{name} {dealt}/{taken}" for name, dealt, taken in entries[:3])
|
parts = []
|
||||||
return f"^8^3DMG^7 {summary}\n"
|
for name, dealt, taken in entries[:3]:
|
||||||
|
prefix = ''
|
||||||
|
if player_tracker is not None:
|
||||||
|
prefix = TEAM_COLORS.get(player_tracker.get_team(name), '')
|
||||||
|
parts.append(f"{prefix}{name} {dealt}/{taken}")
|
||||||
|
return f"^8^3DMG^7 {' '.join(parts)}\n"
|
||||||
|
|
||||||
|
|
||||||
class UIManager:
|
class UIManager:
|
||||||
@@ -893,7 +901,7 @@ class UIManager:
|
|||||||
print_colored(self.info_window, line, 0)
|
print_colored(self.info_window, line, 0)
|
||||||
|
|
||||||
# Damage summary from LIVESTATS counters (top 3 by damage dealt)
|
# Damage summary from LIVESTATS counters (top 3 by damage dealt)
|
||||||
damage_line = format_damage_summary(server_info)
|
damage_line = format_damage_summary(server_info, game_state.player_tracker)
|
||||||
if damage_line:
|
if damage_line:
|
||||||
print_colored(self.info_window, damage_line, 0)
|
print_colored(self.info_window, damage_line, 0)
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,8 @@ RENAME_PATTERN = re.compile(r'^(.+?)\s+renamed to\s+(.+?)$')
|
|||||||
STATUS_HEAD_PATTERN = re.compile(r'^\s*\d+\s+-?\d+\s+(bot|\d+)\s+\S', re.MULTILINE)
|
STATUS_HEAD_PATTERN = re.compile(r'^\s*\d+\s+-?\d+\s+(bot|\d+)\s+\S', re.MULTILINE)
|
||||||
STATUS_MAP_PATTERN = re.compile(r'^map:\s+\S+\s*$', re.MULTILINE)
|
STATUS_MAP_PATTERN = re.compile(r'^map:\s+\S+\s*$', re.MULTILINE)
|
||||||
STEAMID_PATTERN = re.compile(r'\b\d{16,}\b')
|
STEAMID_PATTERN = re.compile(r'\b\d{16,}\b')
|
||||||
LIVESTATS_PATTERN = re.compile(r'^LIVESTATS\s+(.+)\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)\s*$')
|
LIVESTATS_PATTERN = re.compile(
|
||||||
|
r'^LIVESTATS\s+(.+)\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)(?:\s+([A-Za-z]+|\d))?\s*$')
|
||||||
STATUS_ROW_PATTERN = re.compile(
|
STATUS_ROW_PATTERN = re.compile(
|
||||||
r'^\s*(\d+)\s+(-?\d+)\s+(\d+)\s+(.+?)\s+(\d+)\s+(\S+)\s+(\d+)\s+(\d+)(?:\s+(\d{15,}))?\s*$')
|
r'^\s*(\d+)\s+(-?\d+)\s+(\d+)\s+(.+?)\s+(\d+)\s+(\S+)\s+(\d+)\s+(\d+)(?:\s+(\d{15,}))?\s*$')
|
||||||
ROSTER_CHAT_GUARD_PATTERN = re.compile(r'^.{1,40}:\s')
|
ROSTER_CHAT_GUARD_PATTERN = re.compile(r'^.{1,40}:\s')
|
||||||
@@ -179,7 +180,9 @@ def handle_livestats(message, game_state, ui, verbose=0):
|
|||||||
Consume every LIVESTATS heartbeat line (engine damage counters emitted
|
Consume every LIVESTATS heartbeat line (engine damage counters emitted
|
||||||
every 10s by the livedamage plugin, plus its summary variants). Lines
|
every 10s by the livedamage plugin, plus its summary variants). Lines
|
||||||
matching the full player shape update live score/damage/ping; names may
|
matching the full player shape update live score/damage/ping; names may
|
||||||
contain spaces, so the counters anchor from the end. The raw lines
|
contain spaces, so the counters anchor from the end. An optional trailing
|
||||||
|
team token (extended livedamage format) is authoritative and populates
|
||||||
|
the roster with correct teams — including during warmup. The raw lines
|
||||||
display only at -v or higher verbosity; they are never treated as chat.
|
display only at -v or higher verbosity; they are never treated as chat.
|
||||||
"""
|
"""
|
||||||
clean = strip_color_codes(message).strip()
|
clean = strip_color_codes(message).strip()
|
||||||
@@ -187,8 +190,11 @@ def handle_livestats(message, game_state, ui, verbose=0):
|
|||||||
return False
|
return False
|
||||||
match = LIVESTATS_PATTERN.match(clean)
|
match = LIVESTATS_PATTERN.match(clean)
|
||||||
if match:
|
if match:
|
||||||
name, score, dealt, taken, ping = match.groups()
|
name, score, dealt, taken, ping, team = match.groups()
|
||||||
game_state.player_tracker.update_livestats(name, int(score), int(dealt), int(taken), int(ping))
|
if team is not None and team.isdigit():
|
||||||
|
team = int(team)
|
||||||
|
game_state.player_tracker.update_livestats(
|
||||||
|
name, int(score), int(dealt), int(taken), int(ping), team)
|
||||||
ui.update_server_info(game_state)
|
ui.update_server_info(game_state)
|
||||||
if verbose > 0:
|
if verbose > 0:
|
||||||
timestamp = time.strftime('%H:%M:%S')
|
timestamp = time.strftime('%H:%M:%S')
|
||||||
|
|||||||
Reference in New Issue
Block a user