This commit is contained in:
ilyastar9999
2025-12-03 13:52:28 +03:00
parent 07b338e823
commit 6f73f50f31
3 changed files with 57 additions and 10 deletions

View File

@@ -32,6 +32,17 @@ CREATE TABLE IF NOT EXISTS scoreboard_state (
UNIQUE(team_id, service_name) UNIQUE(team_id, service_name)
); );
-- Team scores table - track total and flag points over time
CREATE TABLE IF NOT EXISTS team_scores (
id SERIAL PRIMARY KEY,
team_id INTEGER NOT NULL,
team_name VARCHAR(255) NOT NULL,
total_score FLOAT DEFAULT 0,
flag_points FLOAT DEFAULT 0,
round INTEGER NOT NULL,
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Attacks tracking table for scoreboard injector -- Attacks tracking table for scoreboard injector
CREATE TABLE IF NOT EXISTS attacks ( CREATE TABLE IF NOT EXISTS attacks (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
@@ -84,6 +95,8 @@ ON CONFLICT (key) DO NOTHING;
-- Create indexes for performance -- Create indexes for performance
CREATE INDEX IF NOT EXISTS idx_scoreboard_state_team_service ON scoreboard_state(team_id, service_name); CREATE INDEX IF NOT EXISTS idx_scoreboard_state_team_service ON scoreboard_state(team_id, service_name);
CREATE INDEX IF NOT EXISTS idx_team_scores_round ON team_scores(round);
CREATE INDEX IF NOT EXISTS idx_team_scores_team_id ON team_scores(team_id);
CREATE INDEX IF NOT EXISTS idx_attacks_timestamp ON attacks(timestamp); CREATE INDEX IF NOT EXISTS idx_attacks_timestamp ON attacks(timestamp);
CREATE INDEX IF NOT EXISTS idx_attacks_our_attack ON attacks(is_our_attack); CREATE INDEX IF NOT EXISTS idx_attacks_our_attack ON attacks(is_our_attack);
CREATE INDEX IF NOT EXISTS idx_attacks_attack_to_us ON attacks(is_attack_to_us); CREATE INDEX IF NOT EXISTS idx_attacks_attack_to_us ON attacks(is_attack_to_us);

View File

@@ -189,8 +189,9 @@ async def socketio_listener():
"""Listen to ForcAD scoreboard using Socket.IO""" """Listen to ForcAD scoreboard using Socket.IO"""
sio = socketio.AsyncClient(logger=False, engineio_logger=False) sio = socketio.AsyncClient(logger=False, engineio_logger=False)
# Cache for task names # Cache for task and team names
task_names = {} task_names = {}
team_names = {}
@sio.event(namespace='/game_events') @sio.event(namespace='/game_events')
async def update_scoreboard(data): async def update_scoreboard(data):
@@ -200,11 +201,20 @@ async def socketio_listener():
round_num = event_data.get('round', 0) round_num = event_data.get('round', 0)
round_start = event_data.get('round_start', 0) round_start = event_data.get('round_start', 0)
team_tasks = event_data.get('team_tasks', []) team_tasks = event_data.get('team_tasks', [])
teams_data = event_data.get('teams', [])
print(f"📊 Round {round_num} - Processing {len(team_tasks)} team updates") print(f"📊 Round {round_num} - Processing {len(team_tasks)} team updates")
conn = await db_pool.acquire() conn = await db_pool.acquire()
try: try:
# Store team scores
for team in teams_data:
team_id = team.get('id')
await conn.execute("""
INSERT INTO team_scores (team_id, team_name, total_score, flag_points, round, timestamp)
VALUES ($1, $2, $3, $4, $5, NOW())
""", team_id, team_names.get(team_id, f'Team {team_id}'),
team.get('score', 0), team.get('flag_points', 0), round_num)
for team_task in team_tasks: for team_task in team_tasks:
team_id = team_task.get('team_id') team_id = team_task.get('team_id')
task_id = team_task.get('task_id') task_id = team_task.get('task_id')
@@ -294,6 +304,10 @@ async def socketio_listener():
for task in tasks: for task in tasks:
task_names[task.get('id')] = task.get('name') task_names[task.get('id')] = task.get('name')
# Cache team names
for team in teams:
team_names[team.get('id')] = team.get('name')
team_names_str = ', '.join([f"{t.get('name')} (ID:{t.get('id')})" for t in teams]) team_names_str = ', '.join([f"{t.get('name')} (ID:{t.get('id')})" for t in teams])
task_names_str = ', '.join([t.get('name') for t in tasks]) task_names_str = ', '.join([t.get('name') for t in tasks])
print(f" Teams: {team_names_str}") print(f" Teams: {team_names_str}")
@@ -390,25 +404,42 @@ async def get_stats():
@app.get("/attacks", dependencies=[Depends(verify_token)]) @app.get("/attacks", dependencies=[Depends(verify_token)])
async def get_attacks(limit: int = 100, our_attacks: Optional[bool] = None, attacks_to_us: Optional[bool] = None): async def get_attacks(limit: int = 100, our_attacks: Optional[bool] = None, attacks_to_us: Optional[bool] = None):
"""Get recent attacks""" """Get recent attacks with team names"""
conn = await get_db() conn = await get_db()
try: try:
query = "SELECT * FROM attacks WHERE 1=1" query = """
SELECT
a.*,
ts_attacker.team_name as attacker_team_name,
ts_victim.team_name as victim_team_name
FROM attacks a
LEFT JOIN (
SELECT DISTINCT ON (team_id) team_id, team_name
FROM team_scores
ORDER BY team_id, timestamp DESC
) ts_attacker ON a.attacker_team_id = ts_attacker.team_id
LEFT JOIN (
SELECT DISTINCT ON (team_id) team_id, team_name
FROM team_scores
ORDER BY team_id, timestamp DESC
) ts_victim ON a.victim_team_id = ts_victim.team_id
WHERE 1=1
"""
params = [] params = []
param_count = 0 param_count = 0
if our_attacks is not None: if our_attacks is not None:
param_count += 1 param_count += 1
query += f" AND is_our_attack = ${param_count}" query += f" AND a.is_our_attack = ${param_count}"
params.append(our_attacks) params.append(our_attacks)
if attacks_to_us is not None: if attacks_to_us is not None:
param_count += 1 param_count += 1
query += f" AND is_attack_to_us = ${param_count}" query += f" AND a.is_attack_to_us = ${param_count}"
params.append(attacks_to_us) params.append(attacks_to_us)
param_count += 1 param_count += 1
query += f" ORDER BY timestamp DESC LIMIT ${param_count}" query += f" ORDER BY a.timestamp DESC LIMIT ${param_count}"
params.append(limit) params.append(limit)
rows = await conn.fetch(query, *params) rows = await conn.fetch(query, *params)

View File

@@ -65,7 +65,7 @@
<th>Attacker</th> <th>Attacker</th>
<th>Victim</th> <th>Victim</th>
<th>Service</th> <th>Service</th>
<th>Points</th> <th>Flags</th>
<th>Type</th> <th>Type</th>
</tr> </tr>
</thead> </thead>
@@ -126,13 +126,16 @@
typeLabel = '<span class="badge bg-danger">Against Us</span>'; typeLabel = '<span class="badge bg-danger">Against Us</span>';
} }
const attackerName = attack.attacker_team_id ? (attack.attacker_team_name || `Team ${attack.attacker_team_id}`) : 'Unknown';
const victimName = attack.victim_team_id ? (attack.victim_team_name || `Team ${attack.victim_team_id}`) : 'Unknown';
html += ` html += `
<tr> <tr>
<td>${new Date(attack.timestamp).toLocaleString()}</td> <td>${new Date(attack.timestamp).toLocaleString()}</td>
<td>Team ${attack.attacker_team_id}</td> <td>${attackerName}</td>
<td>Team ${attack.victim_team_id}</td> <td>${victimName}</td>
<td>${attack.service_name}</td> <td>${attack.service_name}</td>
<td>${attack.points ? attack.points.toFixed(2) : '-'}</td> <td>${attack.points ? Math.round(attack.points) : '-'}</td>
<td>${typeLabel}</td> <td>${typeLabel}</td>
</tr> </tr>
`; `;