Releases: Zyphro3D/pannel-ac-evo-server
Release list
v2.0.1
Full Changelog: v2.0.0...v2.0.1
v2.0.0
v1.9.6
Sécurité — le mot de passe admin du serveur de jeu n'apparaît plus dans l'historique de chat public ni dans les logs
- La commande d'élévation
\admin <mot de passe>(envoyée par le bot à chaque reconnexion, ~toutes les 60 s) transitait parsend_chat, qui la journalisait en clair et l'ajoutait au tampon de chat exposé publiquement via/api/live/chat-history— lisible par tout visiteur anonyme de la page classement. - Corrigé : les commandes serveur (préfixe « \ ») ne sont plus jamais ajoutées à l'historique public, et
\adminest rédigé en « \admin *** » dans les logs. Les messages légitimes (bienvenue, réactions spectateurs) restent affichés.
Correction — /api/results/ingest vérifie la signature HMAC avant toute lecture d'état
- Une requête non signée déclenchait auparavant des lectures (fichier d'état, appel Docker
is_running, comptage HTTP des joueurs) avant le rejet. La signature est désormais vérifiée en tout premier.
Correction — l'endpoint de diagnostic /api/live/tcp_debug était cassé et ignorait le serveur courant
- Import invalide (
_build_state_cachedinexistant) provoquant une erreur 500, etserver_idnon propagé. Réparé : bon import et diagnostic sur le serveur réellement sélectionné.
Correction — réutilisation du client Docker partagé pour l'état et le redémarrage du container
container_info(sollicité en polling) etcontainer_restartrouvraient une connexion Docker à chaque appel au lieu de réutiliser le client partagé.
Correction — migration vers l'API SQLAlchemy 2.x et datetime timezone-aware sur les chemins restants
Model.query.get()→db.session.get/db.get_or_404;datetime.utcnow()(déprécié) →datetime.now(timezone.utc).
Ajout — compte à rebours avant le prochain changement de config par inactivité
- Sur la page Programmation et sur l'accueil (étiquette du serveur), affichage d'un compte à rebours "Prochaine config dans MM:SS" quand le roulement par inactivité est actif et qu'aucun joueur n'est connecté.
- N'apparaît que si applicable : masqué si le roulement/seuil est désactivé, ou si des joueurs sont actuellement connectés (le minuteur ne redémarre qu'une fois tout le monde reparti).
- Vérifié en isolation (délai écoulé simulé, avec/sans joueurs) : calcul correct dans les deux cas.
Correction — le comptage de joueurs (et donc le compte à rebours ci-dessus) ne fonctionnait pas sur le serveur 1
- Signalé par un utilisateur : le minuteur de roulement par inactivité n'apparaissait jamais sur son serveur 1 réel.
- Cause n°1 : le hostname par défaut utilisé pour interroger l'API HTTP du serveur de jeu (
ACESERVER_HOST) était figé en dur àaceserverdansDockerfile.panel, alors que le DNS interne Docker ne résout queace-server(lecontainer_nameexplicite du service). Ce défaut erroné faisait silencieusement échouerget_player_count()pour le serveur 1 (jamais pour les serveurs 2+, qui utilisent le nom de container en base de données). - Cause n°2 : le port HTTP interne écrit dans l'état du panel au démarrage (
start_server()) était lu depuis la variable d'environnement globaleACESERVER_HTTP_PORT(8080chez cet utilisateur) au lieu du champhttp_portpropre à chaque serveur en base (8081, le port réellement utilisé pour lancer le jeu). Le panel interrogeait donc le mauvais port. - Les deux bugs sont indépendants de la fonctionnalité de compte à rebours elle-même — ils affectaient déjà silencieusement tout comptage de joueurs sur le serveur 1 avant cet ajout.
- Corrigé : hostname par défaut aligné sur le nom de container réel ;
start_server()accepte désormais unhttp_portexplicite, transmis par les 3 points d'appel (démarrage manuel, démarrage de roulement, lancement programmé) et par l'avance de roulement elle-même. Vérifié en conditions réelles sur le serveur 1 : le comptage de joueurs et le compte à rebours fonctionnent maintenant correctement.
Ajout — bouton "Ignorer" sur le bandeau "Réglages .env ignorés"
- Signalé par un utilisateur : le bandeau reste affiché en continu pour une divergence volontaire/déjà connue (ex.
PANEL_TITLEdifférent entre.envet Paramètres), sans moyen de le faire taire. - Ajout d'un bouton "Ignorer" qui mémorise la valeur
.envexacte actuellement signalée pour chaque clé. Si.envne change plus, le bandeau ne réapparaît pas pour cette clé. S'il change à nouveau (nouvelle édition), c'est une divergence différente et elle réapparaît normalement. - Vérifié en conditions réelles : ignorer fait disparaître la divergence actuelle ; l'éditer à nouveau dans
.envla fait réapparaître.
Ajout — passage automatique au circuit suivant du roulement après inactivité
- Le roulement de configs n'avançait jusqu'ici qu'à la fin d'une session réellement jouée (résultat posté par le serveur de jeu). Si personne ne se connecte, aucune session ne se termine jamais, donc le roulement restait bloqué indéfiniment sur la même config.
- Ajout d'un réglage "Passer à la configuration suivante après (minutes sans joueur)" sur la page Programmation — 0 = désactivé. Le watchdog vérifie désormais aussi le nombre de joueurs connectés, indépendamment de toute session jouée.
- Vérifié en isolation (compteur de joueurs et écoulement du temps simulés) : le déclenchement se comporte comme attendu sans affecter de serveur réel.
Correction — sauvegarder n'importe quel onglet Paramètres réinitialisait les cases à cocher des autres onglets
- Signalé par un utilisateur :
SESSION_COOKIE_SECURE(onglet Panel) repassait àfalse"de temps en temps", sans action directe dessus. - Cause : la sauvegarde d'un onglet Paramètres traitait les cases à cocher de toutes les sections (
_ENV_SECTIONS), pas seulement celles de l'onglet réellement soumis. Une case décochée n'étant jamais envoyée par le navigateur, toute case absente du formulaire soumis (car appartenant à un autre onglet) était silencieusement remise à"false"— y comprisSESSION_COOKIE_SECURE,ACE_BOT_IS_ADMIN,MAIL_USE_TLS,REQUIRE_EMAIL_CONFIRMATION. Sauvegarder l'onglet Serveur ou Notifications suffisait à casser silencieusement les réglages de l'onglet Panel. - C'est aussi ce qui rendait le bandeau "Réglages .env ignorés" (ci-dessous) trompeur : le corriger via Paramètres puis sauvegarder n'importe quel autre onglet le recassait aussitôt.
- Corrigé : chaque case à cocher n'est désormais mise à jour que si elle appartient à l'onglet réellement soumis. Vérifié en conditions réelles (session de test authentifiée) : sauvegarder l'onglet Serveur ne touche plus
SESSION_COOKIE_SECURE.
Correction — le bandeau "Réglages .env ignorés" restait affiché après correction
- Signalé par un utilisateur :
.envet Paramètres avaient bien la même valeur pourSESSION_COOKIE_SECURE, mais le bandeau d'avertissement (ajouté en v1.9.5) continuait de s'afficher. - Cause : la détection de divergence n'était calculée qu'une seule fois, au démarrage du panel (
_ENV_SETTINGS_DRIFT). Corriger.envou sauvegarder depuis Paramètres ne rafraîchissait pas cet instantané tant que le panel n'était pas redémarré. - Corrigé :
get_env_settings_drift()relit désormais.envetsettings.jsonà chaque affichage, sans dépendre d'un état figé au démarrage.
Full Changelog: v1.9.5...v1.9.6
v1.9.5
Correction — mot de passe admin configuré via Paramètres ignoré par le bot ("reopen")
Signalé à nouveau après le correctif v1.9.4 : mot de passe admin bien configuré (via la page Paramètres, pas .env), mais le bot refuse toujours de s'élever en prétendant qu'aucun mot de passe n'est configuré.
Cause racine : deploy_config() (qui écrit le fichier réellement lu par le bot pour l'élévation) relisait la config source directement depuis le disque, sans jamais réappliquer les réglages globaux (SERVER_ADMIN_PASSWORD compris) — contrairement aux arguments de lancement envoyés au jeu, construits séparément à partir d'un dict qui, lui, les avait bien reçus. Résultat : le mot de passe admin global atteignait bien le serveur de jeu, mais jamais le fichier que le bot consulte pour s'élever, peu importe le nombre de redémarrages.
Corrigé et vérifié en reproduisant le scénario exact (mot de passe configuré via Paramètres après le démarrage du serveur) : élévation en échec avant le correctif, réussie après.
Correction — images véhicules/circuits absentes après un clone du repo
Le dossier media/ était entièrement exclu du dépôt Git par erreur (prévu pour les assets uploadés à l'exécution, il excluait aussi les photos par défaut des véhicules/circuits officiels). Tout clone frais du repo récupérait donc un dossier vide — aucune image ne s'affichait nulle part dans le panel, sauf le tracé SVG de la page Live (non concerné, vient d'ailleurs).
Corrigé : .gitignore affiné, les images par défaut des véhicules et circuits sont désormais versionnées.
Ajout — avertissement quand .env est modifié après la première installation sans effet
Une fois settings.json créé (à la première installation), il devient la seule source de vérité — modifier .env après coup est silencieusement ignoré, y compris pour des réglages sensibles (mot de passe admin, cookies sécurisés...). Aucune erreur, aucun avertissement.
Le panel compare désormais .env à settings.json au démarrage et signale toute divergence : un avertissement dans les logs et un bandeau dans l'interface (visible sur toutes les pages admin) listant les clés concernées, avec un lien direct vers Paramètres.
Correction — mise à jour Steam : l'étape "Synchronisation véhicules et circuits" semblait bloquée
Après une mise à jour du serveur de jeu depuis le panel, l'étape de synchronisation des véhicules/circuits pouvait sembler figée pendant jusqu'à 90 secondes, sans aucun message. Un message de progression s'affiche désormais toutes les 15 secondes.
Mise à jour :
git pull
docker compose up -d --build
v1.9.4
v1.9.4 — 13/07/2026
Ajout — images véhicules/circuits manquantes (Kyalami, Brands Hatch GP, Audi R8 LMS GT3 Evo II, Datsun 240Z Standard/Tuned, Porsche 935, Porsche 911 GT2 RS Clubsport Evo, KTM X-Bow GT2/GT4, Golf 8 R)
- Images fournies converties au format et à la convention de nommage attendus (
.webp, slug exact du circuit/véhicule), circuits redimensionnés à 1920px de large comme les images existantes (les sources faisaient jusqu'à 9 Mo), fichiers originaux supprimés après conversion. - Bug trouvé au passage :
_sync_track_meta()ne backfillait jamais l'image d'un circuit déjà existant en DB, contrairement à_sync_car_meta()qui le fait pour les véhicules — un circuit créé sans image avant l'ajout du fichier correspondant (cas de Kyalami) ne la récupérait donc jamais, même après redémarrage. Corrigé pour que les deux fonctions aient le même comportement. - Brands Hatch GP avait déjà une image générique (
brands_hatch.webp, fallback partagé avec Brands Hatch Indy) — mis à jour manuellement vers l'image spécifique désormais disponible (brands_hatch_gp.webp), le mécanisme de synchro ne remplaçant jamais une image déjà définie même par une version plus précise. - Vérifié en conditions réelles (Playwright) : les 8 véhicules et les 2 circuits affichent bien leur nouvelle image sur les pages Véhicules et Classement.
- Bug de doublon trouvé en cours de route : Brands Hatch GP apparaissait deux fois dans le classement. Cause : un fichier de config oublié (
aceserver/configs/server-1/test-config.json) contenait"brands_hatch"(minuscule/underscore) comme valeur de circuit au lieu de"Brands Hatch"(casse du catalogue officiel) —_sync_track_meta()comparait les circuits par chaîne exacte, donc les deux variantes créaient deux lignesTrackMetadistinctes pour le même circuit réel. Corrigé : la déduplication se fait maintenant sur un slug normalisé (insensible à la casse/ponctuation), le nom du catalogue officiel étant toujours préféré. Doublon existant supprimé en base. - Bug de désalignement trouvé en cours de route (page
/tracks— signalé par capture d'écran) : la 1re carte de chaque ligne de grille (circuits, véhicules, sessions) était systématiquement plus haute que les suivantes. Cause : une règle CSS générique.card + .card { margin-top: 16px }, prévue pour espacer des cartes empilées verticalement, s'appliquait aussi aux cartes utilisées comme éléments de grille (où l'espacement est déjà géré pargap) — seule la 1re carte de chaque ligne (sans.cardprécédente dans le DOM) n'avait pas cette marge, la faisant paraître plus grande que ses voisines. Corrigé en neutralisant la marge spécifiquement à l'intérieur d'un conteneur.grid.
Correction — élévation admin du bot ("Aucun mot de passe admin configuré") : bug confirmé, pas une erreur de manipulation
- Signalé via une issue GitHub : mot de passe admin bien configuré côté serveur, mais le bot refuse de s'élever en admin (config ou page Live) en prétendant qu'aucun mot de passe n'est configuré. Vérifié en conditions réelles — c'est un vrai bug, pas une mauvaise manipulation.
- Cause racine :
_get_active_config_name()(ace_tcp_client.py) était censée lire un marqueur.active_configà la racine deCONFIGS_DIRpour savoir quelle config est réellement en cours — ce marqueur n'était écrit nulle part dans tout le code. La fonction retombait donc systématiquement sur "le premier fichier.jsonpar ordre alphabétique" du dossierCONFIGS_DIRracine (la bibliothèque de configs disponibles, pas le dossierserver-{id}/où la config réellement déployée par serveur est écrite), sans lien avec la session réellement lancée ni avecserver_id— les deux serveurs lisaient toujours le même fichier, ignoré deserver_id. Si ce fichier "gagnant" par hasard alphabétique n'a pas de mot de passe admin renseigné (ex: undefault.jsongénérique), l'élévation échoue avec exactement le message remonté dans l'issue, même si la vraie session active en a un. - Corrigé : la config active est maintenant résolue depuis l'état réel du process manager (
process_manager._read_state(server_id), la même source que "quelle config tourne" utilisée ailleurs dans le panel) dans le bon sous-dossierserver-{id}/._get_active_config_name()/_read_active_config()prennent maintenantserver_iden paramètre. - Bug multi-serveur associé trouvé et corrigé au passage : la route
/api/live/bot/elevate-admin(bouton d'élévation de la page Live) n'utilisait aucunserver_id— elle ciblait donc toujours le serveur 1, quel que soit le serveur réellement sélectionné dans le panel. - Vérifié en conditions réelles sur les deux serveurs configurés (requêtes HTTP contre le process en cours, avec le vrai bot TCP connecté) :
server_id=1etserver_id=2résolvent désormais chacun leur propre config déployée (server-1/Practice-spa.jsonvsserver-2/default.json), et l'élévation admin aboutit bien pour le serveur réellement sélectionné (logélévation admin manuelle envoyée (server=1)/(server=2)confirmé pour chacun).
Correction — les commandes admin (kick, to_pit, skip…) cessaient de fonctionner ~60s après l'élévation
- Après le fix ci-dessus, toujours signalé par le rapporteur de l'issue : les commandes admin envoyées après élévation manuelle ne fonctionnaient plus. Cause : ACE EVO Server déconnecte le bot toutes les ~60s ("software timeout" — ce bot minimal n'envoie aucun keepalive), et
_connect_loopne ré-élève automatiquement en admin à la reconnexion que siACE_BOT_IS_ADMIN=true— une élévation manuelle (bouton page Live, avecACE_BOT_IS_ADMIN=false) ne "tenait" donc qu'environ une minute avant de silencieusement redevenir un simple pilote, sans aucune indication dans le panel (le bouton reste affiché comme élevé). - Corrigé : un flag
manual_adminmémorise qu'une élévation manuelle a été demandée pour ce serveur ;_connect_loopla ré-envoie automatiquement à chaque reconnexion, avec ou sansACE_BOT_IS_ADMIN. - Vérifié en conditions réelles : élévation manuelle à 02:12:24, reconnexion automatique du bot ~55s plus tard (déconnexion "software timeout"), ré-élévation automatique confirmée dans les logs à 02:13:19 (
élévation admin envoyée (server=1) [ré-élévation manuelle après reconnexion]) — l'admin reste actif en continu au lieu de retomber après une minute.
Correction — le bot TCP se déconnectait toutes les ~60 secondes ("software timeout")
- Cause racine identifiée en instrumentant temporairement la réception réseau : après le handshake initial (
ConnectToServerHandshakeResponse), ACE EVO Server n'envoie plus rien au bot, et ce client minimal ne renvoie rien non plus tant qu'aucune commande n'est déclenchée — le serveur considère la connexion silencieuse comme morte après ~60s et la coupe (disconnected due to software timeout), le bot se reconnectant aussitôt en boucle. C'est ce cycle qui rendait toute élévation admin ou action éphémère (voir les deux corrections ci-dessus). - Corrigé : en l'absence de tout message serveur pendant 20s, le bot renvoie sa requête de connexion d'origine (
ClientConnectionRequest, déjà acceptée par le serveur, sans effet de bord visible côté jeu) en guise de heartbeat, avant que le délai de 60s ne soit atteint. - Vérifié en conditions réelles sur les deux serveurs : connexion stable et sans aucune reconnexion pendant plusieurs minutes consécutives (auparavant coupée toutes les ~60s), statut
connected: trueconfirmé via requête HTTP contre le process en cours, aucune régression sur le leaderboard live.
Nouveau — historique de tours en direct, indépendant de la fin de session
- Constat : "Derniers résultats" ne se met à jour qu'à la fin d'une session (fichier résultat écrit par ACE EVO Server). En mode Practice continu (
IsCycleEnabled: true, pas de cycle configuré), le jeu ne termine jamais de session — donc plus aucun résultat n'apparaissait, parfois pendant des semaines, alors que les notifications Discord de meilleur temps continuaient d'arriver (mécanisme totalement séparé, basé sur la surveillance TCP en direct). - Vérifié en conditions réelles qu'un arrêt manuel du serveur ne résout pas non plus le problème : ACE EVO Server ne répond pas au signal d'arrêt (SIGTERM) envoyé par Docker, qui le tue en SIGKILL après 10s de délai de grâce (
Exited (137)) sans jamais écrire de résultat. - Solution : chaque tour roulé est maintenant enregistré en direct via le bot TCP déjà connecté en permanence (même mécanisme que les notifs Discord de meilleur temps), sans attendre la fin d'une session. Nouvelle page Mes temps (
/pilot/history, accessible depuis "Mes inscriptions" pour tout pilote ayant lié son compte Steam) affichant l'historique complet : temps, voiture, circuit, type de roulage. - Pour rester léger dans la durée : au-delà d'un délai configurable (Paramètres → Panel → Langue & Fuseau,
LAP_HISTORY_RETENTION_MONTHS, défaut 6 mois), l'historique détaillé n'est pas supprimé mais archivé — regroupé par mois (pilote/circuit/type de roulage), avec le détail temps + voiture de chaque tour conservé, seul l'horodatage précis étant perdu au profit d'un résumé mensuel compact (meilleur temps, moyenne, nombre de tours). - Nouvelles tables
lap_record(détail récent) etlap_archive(résumés mensuels compactés), créées automatiquement au démarrage — aucune action requise pour les installations existantes.
Correction — le toggle "Auto-restart" du widget statut (page Serveur) ne faisait rien
toggleAutoRestart()(app.js) commençait par chercher l'élément#chk-auto-restart(la case à cocher de la barre de contrôle) et s'arrêtait immédiatement si absent — hors ce checkbox n'existe pas dans le widget "Auto-restart" du tableau de bord (#srv-auto-restart-card), donc le cocher/décocher depuis la page statut n'envoyait jamais la requête à l'API et ne changeait rien en réalité, silencieusement. Le contournement (passer par la configuration) fonctionnai...
v1.9.3 — corrections suite à la mise à jour du jeu
v1.9.3 — 11/07/2026
Deux des corrections ci-dessous (rejet du bot, serveur introuvable dans la liste multijoueur) sont directement causées par "la mise à jour du jeu" (build ACE EVO Server 24104623) : bump silencieux de la version de protocole réseau, et validation par Kunos qui filtre les serveurs sur un build trop ancien. Si tu mets à jour ACE EVO Server plus tard et que le bot ou la visibilité multijoueur cassent à nouveau, commence par vérifier ces deux points.
UI — section "Derniers résultats" de la page d'accueil, clarifiée
- Le badge "1er/2e/3e/4e" ne représentait pas un classement mais juste le rang de fraîcheur (les 4 dernières sessions affichées, tous types confondus) — visuellement stylé comme un ruban de podium, ça laissait croire à un vrai classement alors que le contenu pouvait venir d'une simple séance de qualification ou d'essais. Remplacé par un badge sémantique qui dit ce qui est réellement affiché : Vainqueur (Race), Pole position (Qualifying), Meilleur tour (Practice/Warmup).
- Ajout du 2e et 3e (nom + écart au 1er, ex.
+0.849ou+2 toursen course) dans un petit encadré à fond flouté sous le nom du vainqueur/pole — mini-podium compact, sans changer le reste de la mise en page (voiture, date, image inchangés). - 2 nouvelles clés de traduction (Vainqueur, Pole position) dans les 5 langues ; "Meilleur tour" réutilise une clé déjà existante ailleurs dans l'app.
Correction — le bot admin se faisait rejeter par ACE EVO ("incorrect car or parts")
_get_car_model()(ace_tcp_client.py) choisissaitcars[0]de la config active — littéralement la première voiture de la liste, sans vérifier si elle était réellement sélectionnée pour la session. Dans l'ordre de la liste de référence (cars.json), la première voiture est presque toujours désélectionnée par défaut (ex.preset_695b_mech_1, une Abarth 695, alphabétiquement première), donc le bot se connectait quasi systématiquement avec un modèle non autorisé par la session en cours et se faisait rejeter par ACE EVO Server dès la connexion (Ranked server: incorrect car or parts ..., discarding the connection) — jamais de chat in-game, jamais de leaderboard temps réel, jamais de notifications de connexion/déconnexion joueur.- Corrigé : le bot cherche maintenant la première voiture avec
is_selected/IsSelectedàtrue, et ne retombe surcars[0]que si aucune voiture n'est sélectionnée (garde-fou, ne devrait jamais arriver en pratique). - Trouvé en creusant un signalement utilisateur ("serveur introuvable dans la liste multijoueur" — en réalité un problème réseau Docker sans rapport, mais l'investigation a fait remonter ce rejet de connexion bot dans les logs du serveur). Vérifié en conditions réelles : avant le fix,
Ranked server: incorrect car or parts preset_695b_mech_1, discarding the connectionà chaque tentative ; après le fix, le bot choisitpreset_r8gt3_mech_1(première voiture réellement sélectionnée) et la connexion aboutit (ace_tcp_client: connecté à ace-server:9700), plus aucun rejet dans les logs du serveur de jeu.
Correction — bot rejeté après une mise à jour du serveur de jeu ("ConnectToServerResult_ClientOutdated")
- Le build ACE EVO Server 24104623 a bumpé la version de protocole interne de 5 à 6.
_build_connection_request()(ace_tcp_client.py) envoyait toujours la version 5, codée en dur — rejeté par le serveur (Network Version Mismatch. Current: (Server:6, Protocol:8), Requested: (Server:5, Protocol:8)). Corrigé (6au lieu de5), avec un commentaire expliquant le contexte pour la prochaine mise à jour qui rebumpera probablement ce numéro. Ce genre de mise à jour côté jeu peut donc casser silencieusement la connexion du bot (chat in-game, leaderboard live, notifications) sans casser le serveur lui-même — à surveiller après chaque mise à jour Steam.
Investigation — serveur introuvable dans la liste multijoueur du jeu
Cause réelle trouvée en plusieurs étapes, aucune n'étant un bug du panel proprement dit sauf la dernière :
- Le build du serveur était en retard (23658359 installé vs 24104623 disponible) — Kunos filtre silencieusement les serveurs sur un build trop ancien de la liste publique, sans jamais renvoyer d'erreur explicite au niveau de l'enregistrement (
MultiplayerServerListRequestRegisterServerrépondSuccess: truemême filtré). Résolu par la mise à jour Steam. - Après la mise à jour, la synchronisation
CarMeta/TrackMeta(véhicules/circuits) ne se fait qu'au démarrage du panel, jamais automatiquement après une mise à jour Steam — nécessite un redémarrage manuel du panel pour que la nouvelle liste de véhicules/circuits soit prise en compte dans l'UI (97 véhicules et 36 circuits après cette mise à jour, contre 94/35 avant). À automatiser dans une prochaine version (redémarrage auto du panel en fin de mise à jour SteamCMD, ou resynchronisation à chaud sans redémarrage complet). - Bug du bot corrigé ci-dessus (version de protocole), trouvé en marge de cette investigation.
- Deux serveurs configurés avec le même port HTTP externe (8081) en base de données — le second serveur ne pouvait jamais démarrer correctement avec une configuration réseau valide tant que le premier occupait ce port. Pas de garde-fou empêchant deux serveurs de partager le même port HTTP à la création — à ajouter dans une prochaine version (même validation que pour les ports TCP/UDP, qui eux sont déjà vérifiés à la création/modification d'un serveur).
v1.9.2
v1.9.2 — 10/07/2026
Nouveau — support de la nouvelle version du launcher AC EVO Server (véhicules officiels/mods)
- La nouvelle version du launcher ajoute des véhicules, des circuits, et un tag
is_modpar véhicule danscars.json(régénéré automatiquement par la mise à jour Steam — aucune action requise). Le panel propage désormaisis_mod/IsMod/IsModTextdans les configs qu'il écrit (_car_dict()), et le schéma de config connaît la nouvelle cléShowOnlyOfficial(ShowOnlySelecteda son pendant). - Ajout d'un filtre "Véhicules officiels uniquement" + badge MOD sur les deux sélecteurs de véhicules du panel (page Serveur et création/édition d'événement) — le panel a son propre sélecteur, indépendant de celui du launcher.
- Nouveaux véhicules et circuits : rien à faire, ils remontent automatiquement (lecture dynamique de
cars.json/events_*.json). Un nouveau circuit n'aura simplement pas de carte SVG tant quetrack_map.pyn'a pas son entrée (nécessite les assets extraits decontent.kspkg). SelectOnlyOfficialCarsCommand(présent dans le JSON du launcher) n'est volontairement pas reproduit : c'est un artefact de sérialisation MVVM/WPF du launcher (binding de bouton), sans donnée exploitable côté serveur.
Nouveau — bandeau "nouvelles variables .env" après une mise à jour
- Un admin qui se connecte après une mise à jour voit maintenant un bandeau listant les nouvelles variables
.envintroduites depuis sa dernière visite (optionnelles, valeurs par défaut sûres — rien ne casse si elles restent absentes), avec un lien vers Paramètres et un bouton "Compris" qui masque le bandeau définitivement (persisté dansdata/settings.json, survit aux redémarrages). - Cette version l'inaugure avec
STEAM_HOME(voir section DevOps). Pour les prochaines releases : ajouter une entrée dansNEW_ENV_VARS_BY_VERSION(app/routes/admin.py) à chaque nouvelle clé.env, même optionnelle.
Sécurité
/api/resultset/api/results/<id>ne renvoient plus le SteamID64 (player_id/guid) à un pilote non-admin — n'importe quel compte pilote pouvait auparavant scripter ces routes sur toute la plage d'ID et reconstituer la table SteamID → pseudo de la communauté.deploy_config()etsave_rotation()valident maintenant le nom de config (_valid_config_name()), comme tous les autres points d'entrée du même genre.server_idsur/api/results/ingestest casté défensivement (un?server_id=abcrenvoyait un 500 avant la vérification HMAC, renvoie maintenant un 403 propre).
Multi-serveur
ResultsPostUrl(générée pour chaque serveur au déploiement de config) porte désormais?server_id=N: les résultats du serveur 2+ étaient jusqu'ici attribués au serveur 1 (rotation et historique du mauvais serveur avançaient). Vérifié avec un vrai appel HTTP signé HMAC :server_id=2atterrit bien en base avec le bonserver_id.- L'auto-launch d'un événement programmé sur un serveur ≠ 1 déploie maintenant la config et annonce le bon port/nom (
deploy_config()+tcp_listener/udp_listener/server_name), au lieu du port par défaut du serveur 1. - Le bouton "Démarrer le roulement" (
/api/rotation/start) annonçait le port et le nom globaux (SERVER_TCP_PORT/SERVER_NAME) au lieu de ceux du serveur réellement sélectionné — signalé par un utilisateur sur GitHub (le nom configuré par-serveur dans Paramètres → Serveur était silencieusement ignoré au démarrage d'un roulement sur un serveur ≠ 1). Le nom par-serveur (Server.name, éditable dans Paramètres → Serveur → "Nom du serveur") fonctionnait déjà correctement pour un démarrage normal et pour l'avancement automatique du roulement (webhook) ; seul ce point d'entrée avait été oublié. Même correctif que les deux points ci-dessus : port/nom duServerDB passés àbuild_launch_args(). Vérifié par un test qui intercepte les appels (sans toucher aux vrais conteneurs) : le port et le nom du serveur 2 sont bien ceux transmis. - Un kick/mute/etc. déclenché sur le serveur 2 résout maintenant le nom du pilote sur le bon leaderboard pour l'embed Discord.
- Les webhooks Discord configurés par-serveur (onglet Paramètres → Serveur) sont maintenant pris en compte même depuis les threads du bot TCP (qui n'ont pas de contexte Flask par défaut) — ils retombaient silencieusement sur les webhooks globaux.
Performance
- La reconstruction de l'état live (
/timing) ne rescane plus 24h de logs container à chaque appel : la fenêtre de lecture est bornée au démarrage réel du serveur (sûr par construction — aucun pilote connecté ne peut avoir une ligne de log antérieure au démarrage du container), et le client Docker est réutilisé au lieu d'être recréé à chaque appel. - Le cache TTL de cet état passe de 10s à 12s pour rester au-dessus du poll client réel (10s) — avant, le cache expirait quasiment à chaque requête.
- Le cache LRU des résultats parsés est aligné sur la taille du plus gros scan existant (2000, au lieu de 200) — au-delà de 200 résultats en base, chaque affichage de
/resultsréévinçait et re-parsait en boucle. - Page d'accueil publique (
/) : rate limit ajouté (60/min), comme les autres routes publiques. - Endpoint SSE
/api/live/streamsupprimé : confirmé mort (aucunEventSource/extension SSE htmx branchée dessus nulle part dans le projet), il monopolisait un thread Waitress par connexion sans aucun bénéfice actuel.
Fiabilité
- Le watchdog (surveillance crash/rotation des serveurs) ne peut plus mourir silencieusement sur une exception imprévue (ex. erreur disque pendant une rotation) — le corps de la boucle est maintenant protégé par un
try/exceptqui logue et continue, comme le fait déjà le planificateur d'événements. send_chat()(bot TCP) ne tient plus le lock partagé pendant l'envoi réseau (sendall) — évite un blocage deis_connected()/de la reconnexion si le buffer TCP est plein.settings.jsoncorrompu :_read_env_file/_write_env_fileloguent maintenant un avertissement au lieu d'échouer silencieusement.
UI / Traductions
- ~19 clés de traduction manquantes ou vides ajoutées dans les 5 langues (fr/en/de/es/it) : ordinaux (1er/2e/3e/4e), unités de durée (h/j/min), plusieurs libellés de
/timing,/results,/settings. Vérifié en navigateur dans les 4 langues non-françaises. - 11 textes en dur (attributs
aria-label,title,placeholder) passés en clés de traduction. - Les toasts de démarrage/arrêt du cycle de rotation ont leurs propres clés (
cycleStarted/cycleStopped) au lieu de réutiliser celles du serveur (l'utilisateur voyait « Serveur démarré » en lançant un cycle de rotation). car_display_nameéchappé avant injection dans le DOM sur/timing(principe de précaution — la donnée vient du jeu, pas d'un attaquant HTTP).- Rebuild Tailwind : les classes
text-inherit/underline(déjà utilisées dans le HTML) manquaient du CSS compilé, qui était simplement périmé.
Qualité de code
- Nouvelle suite de tests
tests/unit/(pytest,requirements-dev.txtséparé — non installé dans l'image de production) : parser de résultats, migrations DB, authentification. 18 tests. - 8 imports morts retirés ; les défauts des messages du bot TCP (dupliqués à 4 endroits) centralisés dans
config.py.
DevOps
.env.examplecomplété :ACESERVER_HTTP_PORT,ACESERVER_TCP_PORT(déjà lues parconfig.py, non documentées) etSTEAM_HOME(nouvelle, optionnelle — voir ci-dessous).- Port HTTP du serveur de jeu dans
docker-compose.yml: configurable viaACESERVER_HTTP_PORTau lieu de codé en dur. - Montage de la session Steam (
docker-compose.yml) : nouvelle variable optionnelleSTEAM_HOME, à définir dans.envsi le panel est lancé via systemd ousudosans-E($HOMEy est vide, ce qui montait un dossier root vide et dégradait silencieusement la mise à jour SteamCMD). Sans action, comportement inchangé ($HOMEde l'utilisateur courant). CHANGELOG.mdv1.9.1 : ajout d'une ligne « Breaking (URLs) » qui manquait pour le renommage/administration/*→/settings/*.
Base de données
- Nouvel index
ix_event_registration_driver_id(migration additive, automatique au démarrage —filter_by(driver_id=...)était en full-scan sur la page d'accueil, le dashboard pilote, et l'inscription/désinscription aux événements).
Aucune clé .env n'est obligatoire pour cette version (toutes ont un défaut sûr). Migration DB automatique au démarrage, comme d'habitude.
Full Changelog: v1.9.1...v1.9.2
v1.9.1
Nouveau bouton "Vérifier les mises à jour"
- Sépare la vérification de version Steam (interroge
app_info_print, aucun téléchargement, ne touche pas au serveur en cours) de la mise à jour effective. Auparavant, le seul bouton disponible arrêtait le serveur et lançaitapp_updatemême pour une simple vérification. - Le résultat de la dernière vérification (build public connu, date) est persisté (
data/steamcmd_last_check.json, survit aux rebuilds) et affiché sous forme de statut clair : ✓ À jour / ⚠ Mise à jour disponible / Jamais vérifié, avec la vraie date de dernière vérification — auparavant la date affichée provenait du fichier.acflocal (mis à jour uniquement lors d'une installation effective), donc figée à la dernière install et sans rapport avec une simple vérification.
Correction — mise à jour SteamCMD pouvait rester bloquée indéfiniment
steamcmd.shest un script bash qui re-exec/relance en interne (auto-mise à jour au premier lancement). Le processus tué en cas de blocage (Steam Guard, identifiants refusés, timeout) n'était que le PID direct suivi parsubprocess.Popen: les processus petits-enfants issus du re-exec restaient orphelins et gardaient le pipe de sortie ouvert, empêchant toute détection de fin de process côté panel. Résultat côté utilisateur : le serveur de jeu restait arrêté et l'appel HTTP finissait en erreur réseau côté navigateur sans jamais recevoir de réponse.- Le sous-processus est maintenant lancé dans son propre groupe de processus (
start_new_session=True) et le nettoyage cible tout le groupe (os.killpg) plutôt que le seul PID direct. +runscript <fichier>s'est révélé peu fiable dans cet environnement : l'erreurFailed to load script filesurvenait de façon reproductible dès qu'un login était impliqué, y compris sur un fichier de script valide fraîchement écrit. Remplacé par le mode interactif de SteamCMD (commandes envoyées sur son entrée standard,steamcmd.shlancé sans arguments) — la méthode d'automatisation SteamCMD la plus répandue, validée en test contre le vrai binaire. Bénéfice supplémentaire : les identifiants Steam ne transitent plus par un fichier sur disque ni par les arguments de ligne de commande (visibles via/proc/<pid>/cmdline,ps aux, etc. sur cette machine partagée).- Une passe de "préchauffe" (
+quiten argument direct) absorbe l'auto-mise à jour de SteamCMD avant le vrai script. - Le flux SSE de vérification transmet maintenant chaque ligne de sortie au navigateur pendant la connexion (auparavant silencieux plusieurs dizaines de secondes, risquant une coupure de connexion par un proxy intermédiaire pour inactivité).
- Extraction du build public corrigée : la réponse
app_info_printcontient plusieurs blocs"public"imbriqués (un par dépôt sous"manifests", sans buildid) en plus de celui recherché sous"branches". Le premier reconnu par erreur faisait échouer la détection ("Impossible de déterminer le dernier build"). Le nouveau parseur cible spécifiquement"branches"."public"."buildid".
Suppression de la page Administration
- La page
/administrationest supprimée. Son contenu (config email en lecture seule, test SMTP, test des webhooks Discord, aperçu du design des emails) est déplacé dans Paramètres → Notifications, à côté des réglages correspondants. - Les routes
/administration/test-email,/administration/test-webhooket/administration/mail-previewdeviennent/settings/test-email,/settings/test-webhooket/settings/mail-preview.
Correction
- Les fichiers
.mocompilés committés dans git n'étaient plus synchronisés avec leurs.posources depuis plusieurs changements (le conteneur recompile en interne à chaque build Docker, sans jamais remonter le résultat sur le disque hôte). Sans impact sur les déploiements réels (docker compose up -d --buildrecompile toujours les traductions à jour au build), mais corrigé pour la cohérence du dépôt.
v1.9.0
v1.8.3
Badges multi-propriétés sur la page Véhicules
- Chaque voiture affiche maintenant tous ses badges réels : type (Road / Race / Track), époque (Modern / Vintage / Young Timer) et motorisation (ICE / EV / Hybrid)
- Migration DB automatique au démarrage
Filtre voitures en logique OR
- Dans la configuration serveur, sélectionner un badge affiche toutes les voitures qui le possèdent, même si elles ont d'autres badges non sélectionnés
Configuration compatible Portainer ✅
- Les paramètres sauvegardés via l'UI sont désormais stockés dans
data/settings.json(volume persistant) au lieu du fichier.env - Compatible avec Portainer Stack Deploy et tout environnement qui gère ses propres variables d'environnement
- Migration automatique au premier démarrage : aucune action requise pour les installations existantes
SECRET_KEY,ADMIN_PASSWORDetSUPERADMIN_PASSWORDrestent dans.env(variables structurelles de démarrage)
git pull
docker compose up -d --build panel
Full Changelog: v1.8.2...v1.8.3