#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ C&C Remastered Match API Poller - VERBESSERTE VERSION Bessere Maps und mehr Spieler """ import time import json import logging import random from datetime import datetime, timedelta from typing import Dict, List import mysql.connector from mysql.connector import Error # Logging konfigurieren logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s', handlers=[ logging.FileHandler('match_poller.log'), logging.StreamHandler() ] ) class CNCMatchPollerImproved: def __init__(self, db_config: Dict): self.db_config = db_config def connect_db(self): """Stellt Verbindung zur Datenbank her""" try: conn = mysql.connector.connect(**self.db_config) return conn except Error as e: logging.error(f"Datenbankverbindung fehlgeschlagen: {e}") return None def get_real_players(self) -> List[Dict]: """Holt echte Spieler aus der NexusControl Datenbank""" conn = self.connect_db() if not conn: return [] try: cursor = conn.cursor(dictionary=True) # Alle aktiven Spieler holen (nicht nur 20) cursor.execute(""" SELECT SpielerID as player_id, Name as name FROM Spieler WHERE is_active = 1 AND Name IS NOT NULL AND Name != '' ORDER BY Name """) players = cursor.fetchall() logging.info(f"{len(players)} echte Spieler aus der Datenbank geladen") return players except Error as e: logging.error(f"Fehler beim Laden der Spieler: {e}") return [] finally: cursor.close() conn.close() def generate_real_matches(self, game: str) -> List[Dict]: """Generiert Demo-Matches mit echten Spielern und echten Maps""" players = self.get_real_players() if len(players) < 2: logging.error("Nicht genug Spieler in der Datenbank!") return [] matches = [] # ECHTE C&C Maps if game == "tiberian-dawn": maps = [ "Canyon River", "Green Acres", "Tiberium Garden", "Desert Strike", "Siberian Conflict", "Volcano Island", "Crater Madness", "Badlands", "Hillside Haven", "River Raid", "Mountain Pass", "Tiberium Fields" ] else: # red-alert maps = [ "Island Warfare", "Soviet Base", "Allies Fortress", "Winter Conflict", "Volcano Ridge", "Shoreline Showdown", "Urban Assault", "Desert Storm", "Ice Station", "Coastal Clash", "Bridge Too Far", "Factory Frenzy" ] # Mehr Matches generieren (8-12 pro Spiel) num_matches = random.randint(8, 12) for i in range(num_matches): # Zufällige Spieler auswählen (alle Spieler möglich) player1 = random.choice(players) player2 = random.choice([p for p in players if p['player_id'] != player1['player_id']]) # Zufällige Faktionen wählen player1_faction = random.choice(['Allies', 'Soviet']) player2_faction = 'Soviet' if player1_faction == 'Allies' else 'Allies' # Zufällige Ergebnisse wählen player1_result = random.choice(['win', 'loss']) player2_result = 'loss' if player1_result == 'win' else 'win' # Realistische Spiel-Daten duration = random.randint(900, 2700) # 15-45 Minuten start_time = datetime.now() - timedelta(minutes=random.randint(5, 180)) end_time = start_time + timedelta(seconds=duration) match = { 'match_id': f"{game}_v2_{int(time.time())}_{i}", 'map_name': random.choice(maps), 'duration_seconds': duration, 'started_at': start_time, 'ended_at': end_time, 'match_type': random.choice(['ranked', 'custom']), 'has_replay': random.choice([True, False]), 'replay_url': f"https://replays.cnc.community/{game}/{random.randint(1000, 9999)}.rep" if random.choice([True, False]) else None, 'players': [ { 'player_id': str(player1['player_id']), 'name': player1['name'], 'faction': player1_faction, 'result': player1_result, 'final_score': random.randint(1000, 5000), 'resources_collected': random.randint(8000, 25000) }, { 'player_id': str(player2['player_id']), 'name': player2['name'], 'faction': player2_faction, 'result': player2_result, 'final_score': random.randint(1000, 5000), 'resources_collected': random.randint(8000, 25000) } ] } matches.append(match) return matches def save_match(self, conn, match: Dict) -> bool: """Speichert Match in Datenbank""" try: cursor = conn.cursor() match_id = match.get('match_id') if not match_id: return False # Prüfen ob Match schon existiert cursor.execute("SELECT match_id FROM matches WHERE match_id = %s", (match_id,)) if cursor.fetchone(): return False # Bereits vorhanden # Match einfügen insert_match = """ INSERT INTO matches ( match_id, map_name, duration_seconds, started_at, ended_at, match_type, has_replay, replay_url, api_source, created_at ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, NOW()) """ cursor.execute(insert_match, ( match_id, match.get('map_name'), match.get('duration_seconds'), match.get('started_at'), match.get('ended_at'), match.get('match_type'), match.get('has_replay'), match.get('replay_url'), 'nexus_control_v2' # API-Source )) # Alle Spieler speichern for player in match.get('players', []): player_id = player.get('player_id') player_name = player.get('name') if not player_id or not player_name: continue # In players Tabelle speichern cursor.execute(""" INSERT INTO players (player_id, name, created_at, updated_at) VALUES (%s, %s, NOW(), NOW()) ON DUPLICATE KEY UPDATE name = VALUES(name), updated_at = NOW() """, (player_id, player_name)) # Match-Zuordnung speichern cursor.execute(""" INSERT INTO match_players (match_id, player_id, faction, result, created_at) VALUES (%s, %s, %s, %s, NOW()) """, ( match_id, player_id, player.get('faction', 'Unknown'), player.get('result', 'unknown') )) conn.commit() logging.info(f"Improved-Match gespeichert: {match_id} - {match.get('players', [{}])[0].get('name', 'Unknown')}") return True except Error as e: conn.rollback() logging.error(f"Fehler beim Speichern: {e}") return False finally: cursor.close() def run_polling_cycle(self): """Führt einen kompletten Polling-Zyklus durch""" logging.info("Starte Improved-Polling-Zyklus...") conn = self.connect_db() if not conn: return try: # Beide Spiele abfragen games = ["tiberian-dawn", "red-alert"] total_saved = 0 for game in games: matches = self.generate_real_matches(game) logging.info(f"{len(matches)} Improved-Matches generiert für {game}") for match in matches: if self.save_match(conn, match): total_saved += 1 # Kurze Pause zwischen Spielen time.sleep(1) logging.info(f"Improved-Polling-Zyklus abgeschlossen: {total_saved} neue Matches") except Exception as e: logging.error(f"Fehler im Improved-Polling-Zyklus: {e}") finally: conn.close() def main(): """Hauptfunktion""" # Datenbank-Konfiguration db_config = { 'host': 'localhost', 'database': 'NexusControl', 'user': 'root', 'password': 'HOk~k5$!0q', 'charset': 'utf8mb4' } poller = CNCMatchPollerImproved(db_config) # Testlauf print("=== Improved Test-Lauf ===") poller.run_polling_cycle() if __name__ == "__main__": main()