diff --git a/init-db.sql b/init-db.sql index 7412af9..7b2327a 100644 --- a/init-db.sql +++ b/init-db.sql @@ -21,6 +21,17 @@ CREATE TABLE IF NOT EXISTS service_logs ( created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); +-- Scoreboard state table - current flags count for each team+service +CREATE TABLE IF NOT EXISTS scoreboard_state ( + id SERIAL PRIMARY KEY, + team_id INTEGER NOT NULL, + service_name VARCHAR(255) NOT NULL, + stolen_flags INTEGER DEFAULT 0, + lost_flags INTEGER DEFAULT 0, + last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + UNIQUE(team_id, service_name) +); + -- Attacks tracking table for scoreboard injector CREATE TABLE IF NOT EXISTS attacks ( id SERIAL PRIMARY KEY, @@ -72,6 +83,7 @@ INSERT INTO settings (key, value) VALUES ON CONFLICT (key) DO NOTHING; -- 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_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_attack_to_us ON attacks(is_attack_to_us); diff --git a/scoreboard_injector/main.py b/scoreboard_injector/main.py index e9432a1..42edea4 100644 --- a/scoreboard_injector/main.py +++ b/scoreboard_injector/main.py @@ -194,7 +194,7 @@ async def socketio_listener(): @sio.event(namespace='/game_events') async def update_scoreboard(data): - """Handle scoreboard update events""" + """Handle scoreboard update - compare with previous state to detect NEW attacks""" try: event_data = data.get('data', {}) round_num = event_data.get('round', 0) @@ -203,58 +203,76 @@ async def socketio_listener(): print(f"📊 Round {round_num} - Processing {len(team_tasks)} team updates") - # Get task names from previous init_scoreboard or fetch them conn = await db_pool.acquire() try: for team_task in team_tasks: team_id = team_task.get('team_id') task_id = team_task.get('task_id') - stolen = team_task.get('stolen', 0) - lost = team_task.get('lost', 0) - score = team_task.get('score', 0) - status = team_task.get('status', 0) + current_stolen = team_task.get('stolen', 0) + current_lost = team_task.get('lost', 0) - # Only process if there are stolen or lost flags - if stolen > 0 or lost > 0: - # Determine service name - service_name = task_names.get(task_id, f"task_{task_id}") - - # Create attack records based on stolen/lost changes + service_name = task_names.get(task_id, f"task_{task_id}") + + # Get previous state from database + prev_state = await conn.fetchrow( + "SELECT stolen_flags, lost_flags FROM scoreboard_state WHERE team_id = $1 AND service_name = $2", + team_id, service_name + ) + + prev_stolen = prev_state['stolen_flags'] if prev_state else 0 + prev_lost = prev_state['lost_flags'] if prev_state else 0 + + # Calculate NEW flags (difference from previous state) + new_stolen = current_stolen - prev_stolen + new_lost = current_lost - prev_lost + + # Update current state in database + await conn.execute(""" + INSERT INTO scoreboard_state (team_id, service_name, stolen_flags, lost_flags, last_updated) + VALUES ($1, $2, $3, $4, NOW()) + ON CONFLICT (team_id, service_name) + DO UPDATE SET stolen_flags = $3, lost_flags = $4, last_updated = NOW() + """, team_id, service_name, current_stolen, current_lost) + + # Create attack record only for NEW stolen flags + if new_stolen > 0: timestamp = datetime.utcnow() + attack_id = f"r{round_num}_stolen_team{team_id}_{service_name}_{int(timestamp.timestamp())}" - # If flags were stolen BY this team - if stolen > 0: - is_our_attack = team_id == OUR_TEAM_ID - # Generate attack record for each stolen flag (simplified) - attack_id = f"r{round_num}_stolen_team{team_id}_task{task_id}_{int(timestamp.timestamp())}" - - # For stolen flags, we are the attacker - if is_our_attack: - # We stole from someone (we don't know exact victim from this event) - await conn.execute(""" - INSERT INTO attacks (attack_id, attacker_team_id, victim_team_id, service_name, flag, timestamp, points, is_our_attack, is_attack_to_us) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) - ON CONFLICT (attack_id) DO NOTHING - """, attack_id, team_id, 0, service_name, f"ROUND_{round_num}", timestamp, stolen, True, False) - print(f" ✅ Our team stole {stolen} flags from {service_name}") + is_our_attack = team_id == OUR_TEAM_ID - # If flags were lost BY this team - if lost > 0: - is_attack_to_us = team_id == OUR_TEAM_ID - attack_id = f"r{round_num}_lost_team{team_id}_task{task_id}_{int(timestamp.timestamp())}" + await conn.execute(""" + INSERT INTO attacks (attack_id, attacker_team_id, service_name, timestamp, points, is_our_attack, is_attack_to_us) + VALUES ($1, $2, $3, $4, $5, $6, $7) + ON CONFLICT (attack_id) DO NOTHING + """, attack_id, team_id, service_name, timestamp, float(new_stolen), is_our_attack, False) + + if is_our_attack: + print(f" ✅ We stole {new_stolen} NEW flags from {service_name} (total: {current_stolen})") + else: + print(f" 📌 Team {team_id} stole {new_stolen} NEW flags from {service_name} (total: {current_stolen})") + + # Create attack record only for NEW lost flags + if new_lost > 0: + timestamp = datetime.utcnow() + attack_id = f"r{round_num}_lost_team{team_id}_{service_name}_{int(timestamp.timestamp())}" + + is_attack_to_us = team_id == OUR_TEAM_ID + + await conn.execute(""" + INSERT INTO attacks (attack_id, victim_team_id, service_name, timestamp, points, is_our_attack, is_attack_to_us) + VALUES ($1, $2, $3, $4, $5, $6, $7) + ON CONFLICT (attack_id) DO NOTHING + """, attack_id, team_id, service_name, timestamp, float(new_lost), False, is_attack_to_us) + + if is_attack_to_us: + print(f" ⚠️ We LOST {new_lost} NEW flags on {service_name} (total lost: {current_lost})") - # For lost flags, we are the victim - if is_attack_to_us: - # Someone stole from us - await conn.execute(""" - INSERT INTO attacks (attack_id, attacker_team_id, victim_team_id, service_name, flag, timestamp, points, is_our_attack, is_attack_to_us) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) - ON CONFLICT (attack_id) DO NOTHING - """, attack_id, 0, team_id, service_name, f"ROUND_{round_num}", timestamp, lost, False, True) - print(f" ⚠️ We lost {lost} flags on {service_name}") - - # Check for alerts + # Check for alerts on high-value attacks + if new_lost >= ALERT_THRESHOLD_POINTS: await check_and_create_alerts(conn, 0, service_name) + else: + print(f" 📌 Team {team_id} lost {new_lost} NEW flags on {service_name} (total: {current_lost})") finally: await db_pool.release(conn)