Multijoueur : Matchmaking & Salons
Matchmaking serveur atomique, modes par équipes (1v1, 2v2...) et salon temps-réel pour synchroniser les coups.
1. Activer le module sur la console
- Dans le Dashboard de votre jeu, ouvrez l'onglet Modules.
- Activez Gestion des matchs (salons et parties).
- Choisissez le matchmaking Angon pour laisser le serveur regrouper les joueurs automatiquement (ou « externe » si votre jeu gère ses salons lui-même).
- Optionnel : activez Classement ELO pour noter les joueurs et afficher des divisions (voir Classements).
- Ouvrez l'onglet Multijoueur : les tables techniques sont créées et mises à jour automatiquement à l'ouverture.
joueur_1_id / joueur_2_id restent lisibles mais sont remplacées par la liste participants.2. Modes de jeu configurables
Chaque mode est défini par un nombre d'équipes et une taille d'équipe — la capacité d'un salon est donc nb_equipes × taille_equipe. Deux modes sont créés par défaut : 1v1 (2 équipes × 1 joueur) et 2v2 (2 équipes × 2 joueurs). Le modèle accepte n'importe quelle configuration (3v3, 4v4, chacun pour soi à 4, etc.).
| Réglage | Rôle |
|---|---|
| code | Identifiant utilisé par le jeu (ex: "1v1", "2v2") |
| nb_equipes | Nombre d'équipes du salon (2 minimum, 4 = chacun pour soi si taille 1) |
| taille_equipe | Joueurs par équipe |
| classe | Le mode fait varier l'ELO des joueurs |
| actif | Un mode inactif est refusé par le matchmaking (mode_inconnu) |
Gérez les modes depuis le sous-onglet Modes de jeu de l'onglet Multijoueur : création, édition, activation/désactivation et suppression.
3. Matchmaking depuis GDevelop
Le regroupement est effectué atomiquement par le serveur : impossible que deux joueurs forment des salons incompatibles en rejoignant en même temps. Les équipes sont équilibrées automatiquement par ELO décroissant en distribution tourniquet.
// 1. Le joueur demande une partie (une seule fois)
Action : AngonCloud::RejoindreFileAttente("2v2", VariableLog)
// 2. Tant qu'aucun salon n'est formé, vérifier régulièrement (ex: toutes les 2 s)
Condition : AngonCloud::EnFileDAttente
Condition : Chronomètre "poll" > 2 secondes
Action : AngonCloud::VerifierFileAttente(VariableLog)
Action : Remettre à zéro le chronomètre "poll"
// 3. Le salon est prêt : récupérer les infos puis ouvrir le canal temps-réel
Condition : AngonCloud::PartieTrouvee
Action : AngonCloud::RejoindreSalonTempsReel(VariableLog)OuvrirPanneauMatchmaking(mode) affiche un panneau clé en main (même habillage que le panneau de connexion) : avatar du joueur pendant la recherche, puis avatars et pseudos des équipes en face-à-face quand le match est trouvé, et un statut « en attente du lancement ». Le panneau rejoint la file et interroge le serveur tout seul ; il se ferme automatiquement dès que le jeu appelle RejoindreSalonTempsReel. Ses textes se personnalisent dans la console, onglet Multijoueur → Panneau de matchmaking.| Expression | Type | Description |
|---|---|---|
| AngonCloud::PositionFileAttente() | Nombre | Position dans la file (1 = prochain apparié) |
| AngonCloud::IdPartieMultijoueur() | Texte | UUID de la partie dans la table parties_multijoueur |
| AngonCloud::CodeSalonMultijoueur() | Texte | Code lisible du salon (ex: SALON_EF5D0) |
| AngonCloud::MonEquipe() | Nombre | Numéro d'équipe attribué (1, 2, ...) |
| AngonCloud::ParticipantsSalonJson() | Texte | Liste JSON des participants : joueur_id, pseudo, avatar, equipe, statut, score |
4. Salon temps-réel : synchronisation des coups
Une fois le salon trouvé, chaque joueur ouvre un canal Broadcast dédié à la partie. Les actions diffusées arrivent aux adversaires en quelques dizaines de millisecondes — sans attendre un cycle de polling.
// Le joueur pose son pion : diffuser le coup instantanément
Action : AngonCloud::EnvoyerActionSalon(Variable(CoupJoue))
// Réception : vrai une fois par nouvelle action reçue
Condition : AngonCloud::ActionSalonRecue
Action : Définir la variable CoupAdverse = ToJSON(AngonCloud::DerniereActionSalon())
// Fin de partie ou retour au menu
Action : AngonCloud::QuitterSalonTempsReelparties_multijoueur et rediffuse l'état à tout le salon.angon_partie_<partie_id>) et utilise uniquement la clé publique du projet — aucune clé privée n'est exposée au jeu.5. Logique de match côté serveur (relais autoritaire)
Le broadcast direct est parfait pour les données cosmétiques (positions, animations), mais chaque client y peut envoyer ce qu'il veut. Pour les coups qui comptent — un déplacement validé, une victoire, un score — envoyez l'action au serveur via POST /match/action : les règles du jeu (onglet Règles de la console, événement action_partie) la valident, maj_partie() persiste l'état officiel, et le serveur le rediffuse à tout le salon.
sur action_partie
quand action == "coup" et partie.tour_joueur == joueur.id et non cle_existe(partie.etat_plateau, donnees.case)
faire maj_partie("etat_plateau", definir_cle(partie.etat_plateau, donnees.case, joueur.equipe))
faire maj_partie("numero_tour", partie.numero_tour + 1)
faire diffuser("coup", { "par": joueur.pseudo, "case": donnees.case })
sur action_partie
quand action == "coup" et partie.tour_joueur != joueur.id
faire refuser("pas_ton_tour")
# partie_fin : "joueur" = dernier acteur, pas forcément le gagnant —
# ciblez explicitement avec le 3e argument de ajouter_score.
sur partie_fin
faire ajouter_score("global", partie.numero_tour, partie.gagnant_id)// SDK : envoyer un coup validé par les règles du jeu
const r = await Angon.match.action("coup", { plateau: nouveauPlateau });
if (!r.ok) console.warn("Refusé :", r.reason); // 403 action_refusee
// État officiel rediffusé automatiquement au salon (event "etat")
Angon.match.onState((partie) => majPlateau(partie.etat_plateau));
// Reconnexion / rafraîchissement
const { partie } = await Angon.match.state();
// GDevelop : action « Envoyer l'action de partie ... au serveur »,
// puis « Charger l'état officiel de la partie » / EtatPartieJson()Si aucune règle action_partie n'est définie, les actions sont relayées telles quelles (le relais reste utile comme unique source de messages). Quand partie.statut devient termine, les règles sur partie_fin s'exécutent automatiquement — idéal pour distribuer scores ELO et récompenses.
etat_plateau est un vrai objet JSON dans les règles : préférez cle_existe / definir_cle et l'indexation par clé exacte à contient(), qui fait une recherche de sous-chaîne (une clé "3,10" matcherait aussi "13,10").6. Rangs ELO personnalisables
Quand le classement ELO est activé, un classement « elo » est créé automatiquement dans l'onglet Classements, et quatre rangs par défaut (Bronze, Argent, Or, Diamant) sont proposés dans le sous-onglet Rangs ELO. Chaque rang est personnalisable : nom, seuil ELO, couleur et icône image téléversée.
Pour enregistrer les points ELO d'un joueur après une partie classée, soumettez un score au classement "elo" — le serveur écrase l'ancienne valeur (règle « dernière valeur »).
7. Récapitulatif des fonctions Multijoueur
| Fonction | Type | Rôle |
|---|---|---|
| OuvrirPanneauMatchmaking(mode) | Action | Ouvre le panneau intégré et lance la recherche |
| FermerPanneauMatchmaking | Action | Ferme le panneau (annule la recherche en cours) |
| PanneauMatchmakingOuvert | Condition | Vrai tant que le panneau est affiché |
| RejoindreFileAttente(mode) | Action | Inscrit le joueur dans la file du mode |
| QuitterFileAttente | Action | Retire le joueur de toutes les files |
| VerifierFileAttente | Action | Rafraîchit le statut du matchmaking |
| EnFileDAttente | Condition | Vrai pendant l'attente d'un salon |
| PartieTrouvee | Condition | Vrai quand le salon est formé |
| RejoindreSalonTempsReel | Action | Ouvre le canal Broadcast du salon |
| QuitterSalonTempsReel | Action | Ferme le canal du salon |
| SalonTempsReelConnecte | Condition | Vrai quand le canal est prêt |
| EnvoyerActionSalon(var) | Action | Diffuse une variable aux autres joueurs |
| ActionSalonRecue | Condition | Vrai une fois par action reçue |
| DerniereActionSalon() | Expr. texte | JSON de la dernière action reçue |
| EnvoyerActionPartie(nom, données) | Action | Envoie un coup au relais serveur (règles action_partie) |
| ChargerEtatPartie | Action | Recharge l'état officiel depuis le serveur |
| EtatPartieJson() | Expr. texte | JSON de l'état officiel de la partie |