Le payload cible (fpschallenge.eu) nécessite 3 sources de données combinées :
- Config du match — vient de la plateforme :
match_id,team1_id,team2_id, noms, UUIDs joueurs, format - Événements de manche — vient de PAM/GSC : round end, scores, bomb state
- Stats par joueur — vient de PAM/mémoire : kills, deaths, assists, damage, grenades, plants, defuses
Ces 3 sources sont sur des systèmes différents et doivent être fusionnées avant l'envoi HTTP.
fpschallenge.eu
│ (match config payload au démarrage)
▼
Backend Node.js (port 3005)
│ stocke la config en RAM/fichier
│
│◄── HTTP POST (stats par round) ── LD_PRELOAD (cod1plus.so)
│
lit les événements PAM
depuis stdout CoD1
│
CoD1 + PAM (sd.gsc modifié)
print("[STATS_EVENT] {...}")
à chaque fin de manche
Le payload cible contient match_id, team1_id, team2_id, les noms d'équipes, et surtout les UUIDs joueurs — données qui n'existent pas dans CoD1.
Le backend reçoit la config du match avant le lancement (via un POST de la plateforme ou manuellement). Elle est stockée et consultable par le LD_PRELOAD.
POST /api/match/config— reçoit la config complète de la plateforme (match_id, teams, players+UUIDs)GET /api/match/current— retourne la config active (consommée par cod1plus.so)
Les UUIDs plateforme doivent être associés aux joueurs CoD1. Deux méthodes possibles :
- Par nom (
forceNickNames=truedans le payload → les joueurs sont forcés à jouer sous leur vrai nom) - Par GUID (
cl_guiddansclient_t.userinfo, déjà lisible — ex:243D3280F01F0DC9F49E5B22BC124428)
Le plus fiable : matching par nom (avec forceNickNames) + fallback GUID.
PAM GSC ne peut pas faire de requêtes HTTP. La solution : faire print() dans sd.gsc à chaque fin de manche avec une ligne JSON taguée. Le LD_PRELOAD capture stdout de CoD1 et parse ces lignes.
PAM a déjà un système d'événements (events.gsc). Le point d'injection est dans sd.gsc au moment où le round est résolu :
- Quand toute l'équipe adverse est morte
- Quand la bombe explose
- Quand la bombe est désamorcée
- Quand le temps expire
Ces 4 cas convergent dans la logique de fin de round de sd.gsc.
[STATS_EVENT] {"round":1,"allies_score":0,"axis_score":1,"bomb_planted":false,"bomb_exploded":true,"bomb_defused":false,"players":[{"name":"wsx","team":"axis","kills":1,"deaths":0,"assists":0,"damage":100,"grenades":0,"plants":0,"defuses":0,"score":1.0},...]}
| Variable | Contenu |
|---|---|
game["roundsplayed"] |
Numéro de manche |
game["allies_score"] |
Score équipe allies |
game["axis_score"] |
Score équipe axis |
game["bombplanted"] |
Timestamp plant (ou undefined) |
level.bombplanted |
Booléen bombe plantée |
game["playerstats"][i].kills |
Kills joueur i |
game["playerstats"][i].deaths |
Deaths joueur i |
game["playerstats"][i].assists |
Assists joueur i |
game["playerstats"][i].damage |
Damage joueur i |
game["playerstats"][i].grenades |
Grenades joueur i |
game["playerstats"][i].plants |
Plants joueur i |
game["playerstats"][i].defuses |
Defuses joueur i |
game["playerstats"][i].score |
Score joueur i |
game["playerstats"][i].team |
Équipe joueur i |
game["playerstats"][i].name |
Nom joueur i |
Important : PAM track déjà les stats depuis le début du match, pas seulement par round. Pour avoir les stats par round, il faut soit :
- Option A : Lire les stats cumulées + calculer le delta depuis la manche précédente (côté backend)
- Option B : Ajouter un reset des compteurs dans la GSC à chaque début de round (plus propre, plus simple)
→ Option A recommandée : moins de modifications GSC, le backend fait le diff.
CoD1 utilise printf() / write() pour sa console. Le LD_PRELOAD peut hooker write(fd=1, ...) pour intercepter stdout et parser les lignes [STATS_EVENT].
- Au démarrage : charger la config du match depuis
GET /api/match/current - Surveiller stdout pour les lignes
[STATS_EVENT] - À chaque événement reçu :
- Fusionner avec la config du match (noms équipes, IDs, format)
- Mapper
player.name→player.uuidvia la config - Calculer
team1_score/team2_score(en tenant compte du changement de côté à mi-match) - Construire le JSON complet au format fpschallenge.eu
- POST vers l'endpoint cible
| Champ payload | Source |
|---|---|
type |
Constante "data" |
start_time |
Timestamp au démarrage du match (stocké dans config) |
match_id |
Config match |
team1_id / team2_id |
Config match |
team1_name / team2_name |
Config match |
format |
Config match ("BO1", "BO3", etc.) |
forceNickNames |
Config match |
playersCount |
Nombre de joueurs dans config |
team1_score / team2_score |
Événement GSC (allies/axis score + mapping équipe↔team1/team2) |
map |
Lire depuis svs ou cvar sv_mapname en mémoire |
round |
`"Round X |
state |
"playing" / "finished" selon score et limite |
debug |
"sd endround" (fixe) |
players[].uuid |
Mapping nom→uuid depuis config |
players[].name |
Événement GSC |
players[].team |
Événement GSC (allies/axis) + mapping vers team1/team2 |
players[].kills |
Événement GSC |
players[].deaths |
Événement GSC |
players[].assists |
Événement GSC |
players[].damage |
Événement GSC |
players[].grenades |
Événement GSC |
players[].plants |
Événement GSC |
players[].defuses |
Événement GSC |
players[].score |
Événement GSC |
À mi-match, les équipes changent de côté (halftime dans PAM). team1 peut être allies au départ puis axis ensuite. Il faut tracker quel côté correspond à quelle team_id dans la config.
- Ajouter
POST /api/match/configqui stocke la config en mémoire - Ajouter
GET /api/match/currentqui la retourne - Prévoir un champ
sidespour tracker le mapping allies/axis ↔ team1/team2 avec halftime
- Trouver le point exact de fin de round dans
sd.gsc - Ajouter une fonction
PrintStatsEvent()qui formate et print le JSON taguée - Appeler cette fonction juste avant le
waitpost-round ou la préparation de la prochaine manche - Tester sur serveur et vérifier la ligne dans stdout
- Hooker
write(fd=1, ...)via LD_PRELOAD - Parser les lignes contenant
[STATS_EVENT] - Extraire le JSON embarqué
- Logger pour validation
- Au démarrage du thread : GET
/api/match/current - Stocker la config en mémoire dans cod1plus.c
- À chaque événement capturé : fusionner GSC data + config match → payload complet
- POST le payload vers l'URL de la plateforme (ou vers backend local qui forward)
- Gérer les erreurs réseau (retry ou log)
- Détecter le halftime dans les événements GSC (
game["is_halftime"]) - Swapper le mapping allies/axis ↔ team1/team2 dans la config en RAM
-
Comment la config du match arrive-t-elle ? — Est-ce que fpschallenge.eu peut envoyer un POST au backend au moment où le match est créé ? Ou est-ce manuel (fichier config) ?
-
forceNickNames = true est garanti ? — Les joueurs jouent-ils forcément sous leur nom de plateforme ? Sinon il faut le matching par GUID.
-
Le payload est envoyé où exactement ? — Directement vers
fpschallenge.eu/api/v2/...ou vers votre backend local qui relaie ? -
Stats cumulées ou par round ? — Le payload montré semble contenir les stats depuis le début du match (kills cumulés), pas uniquement du round en cours. À confirmer.
-
PAM
_player_stat.gscest-il actif par défaut en S&D comp ? — Vérifier quegame["playerstats"]est bien populé en jeu compétitif.