Skip to main content

Spectateurs

Endpoint proposé : /ws/spectate, sans authentification. Seules spectate.join et spectate.leave sont acceptées comme commandes. Toute tentative de modifier une partie, déplacer un joueur ou combattre sur ce canal produit UNAUTHORIZED.

Le backend réutilise son Match et ses Player existants pour la carte ; aucune simulation parallèle n’est maintenue. Proposition simple : un abonnement par connexion, remplacé par une nouvelle inscription après validation du nouveau UID.

ÉvénementDirectionFonction, comportement serveur et restrictions
spectate.joinWeb → ServeurVérifier la partie, abonner puis envoyer l’instantané. Aucun Player ajouté, aucune place occupée.
spectate.snapshotServeur → WebEnvoyer phase, joueurs, HP, positions, rotations, équipes et scores à l’inscription, à la réinitialisation d’un round et à la fin de partie.
spectate.leaveWeb → ServeurDésabonner cette connexion sans modifier la partie ; répétition sans abonnement sans effet.
spectate.playerMovedServeur → WebRelayer un mouvement accepté avec username et séquence pour déplacer le marqueur de carte.
spectate.playerDiedServeur → WebMarquer le joueur éliminé avec hp: 0, sans supprimer son identité.
spectate.stateChangedServeur → WebActualiser la phase et le numéro de round ; le spectateur ne décide jamais de transition.
spectate.scoreUpdatedServeur → WebDiffuser les équipes et scores actualisés après un résultat de round.

Charges utiles​

Abonnement, puis instantané :

{ "type": "spectate.join", "matchUid": "match_123", "data": {} }
{
"type": "spectate.snapshot",
"matchUid": "match_123",
"roundNumber": 2,
"data": {
"state": "ROUND_ONGOING",
"players": [
{
"username": "alice",
"teamId": 1,
"position": { "x": 12.5, "y": 0, "z": 8.2 },
"rotation": { "x": 0, "y": 90, "z": 0 },
"hp": 100
},
{
"username": "bob",
"teamId": 1,
"position": { "x": 10, "y": 0, "z": 8 },
"rotation": { "x": 0, "y": 90, "z": 0 },
"hp": 100
},
{
"username": "chloe",
"teamId": 2,
"position": { "x": 20, "y": 0, "z": 8 },
"rotation": { "x": 0, "y": 270, "z": 0 },
"hp": 0
},
{
"username": "david",
"teamId": 2,
"position": { "x": 22, "y": 0, "z": 8 },
"rotation": { "x": 0, "y": 270, "z": 0 },
"hp": 40
}
],
"teams": [
{ "teamId": 1, "color": "bleu", "players": ["alice", "bob"], "score": 1 },
{ "teamId": 2, "color": "rouge", "players": ["chloe", "david"], "score": 0 }
]
}
}

Désabonnement :

{ "type": "spectate.leave", "matchUid": "match_123", "data": {} }

Mouvement et élimination :

{
"type": "spectate.playerMoved",
"matchUid": "match_123",
"roundNumber": 2,
"seq": 152,
"data": {
"username": "alice",
"position": { "x": 12.5, "y": 0, "z": 8.2 },
"rotation": { "x": 0, "y": 90, "z": 0 }
}
}
{ "type": "spectate.playerDied", "matchUid": "match_123", "roundNumber": 2, "data": { "username": "chloe", "hp": 0 } }

Phase et scores :

{ "type": "spectate.stateChanged", "matchUid": "match_123", "roundNumber": 2, "data": { "state": "ROUND_ENDING" } }
{
"type": "spectate.scoreUpdated",
"matchUid": "match_123",
"roundNumber": 2,
"data": {
"teams": [
{ "teamId": 1, "color": "bleu", "players": ["alice", "bob"], "score": 2 },
{ "teamId": 2, "color": "rouge", "players": ["chloe", "david"], "score": 0 }
]
}
}

L’élimination reste visible jusqu’au prochain instantané de round réinitialisé ; un mouvement isolé ne signifie pas une réapparition. Après reconnexion web, renvoyer spectate.join et remplacer la vue par l’instantané. Les résultats historiques sont consultables via REST ; l’abonnement direct exige encore un état disponible en mémoire, sinon MATCH_NOT_FOUND.

Les messages suivent les conventions communes. La machine à états décrit les phases affichées ; l’API REST donne accès aux résultats historiques.