Angon Cloud
Angon CloudDocs
Économie & CompétitionTemps réel

Multijoueur : Matchmaking & Salons

Matchmaking serveur atomique, modes par équipes (1v1, 2v2...) et salon temps-réel pour synchroniser les coups.

Le module Multijoueur d'Angon Cloud fournit un matchmaking atomique côté serveur, des salons de jeu configurables par équipes (1v1, 2v2, ...) et un canal temps-réel pour synchroniser les coups quasi instantanément entre les joueurs.

1. Activer le module sur la console

  1. Dans le Dashboard de votre jeu, ouvrez l'onglet Modules.
  2. Activez Gestion des matchs (salons et parties).
  3. Choisissez le matchmaking Angon pour laisser le serveur regrouper les joueurs automatiquement (ou « externe » si votre jeu gère ses salons lui-même).
  4. Optionnel : activez Classement ELO pour noter les joueurs et afficher des divisions (voir Classements).
  5. Ouvrez l'onglet Multijoueur : les tables techniques sont créées et mises à jour automatiquement à l'ouverture.
Migration automatique des anciennes parties
Les salons créés avant l'arrivée des modes multi-équipes sont convertibles en un clic via le bouton « Convertir les anciennes parties » visible dans le sous-onglet Matchs. Les colonnes historiques 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églageRôle
codeIdentifiant utilisé par le jeu (ex: "1v1", "2v2")
nb_equipesNombre d'équipes du salon (2 minimum, 4 = chacun pour soi si taille 1)
taille_equipeJoueurs par équipe
classeLe mode fait varier l'ELO des joueurs
actifUn 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.

Cycle complet de recherche de partie
// 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)
Encore plus simple : le panneau de matchmaking intégré
L'action 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.
ExpressionTypeDescription
AngonCloud::PositionFileAttente()NombrePosition dans la file (1 = prochain apparié)
AngonCloud::IdPartieMultijoueur()TexteUUID de la partie dans la table parties_multijoueur
AngonCloud::CodeSalonMultijoueur()TexteCode lisible du salon (ex: SALON_EF5D0)
AngonCloud::MonEquipe()NombreNuméro d'équipe attribué (1, 2, ...)
AngonCloud::ParticipantsSalonJson()TexteListe 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.

Envoyer et recevoir les coups en direct
// 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::QuitterSalonTempsReel
Le broadcast ne remplace pas la sauvegarde
Le canal temps-réel ne fait que notifier. Pour les coups qui changent l'état officiel du match, utilisez le relais serveur (section suivante) — il persiste dans parties_multijoueur et rediffuse l'état à tout le salon.
Sécurité du canal
Le canal est nommé d'après l'identifiant UUID de la partie (angon_partie_<partie_id>) et utilise uniquement la clé publique du projet — aucune clé privée n'est exposée au jeu.
Nettoyage automatique des salons
Pendant qu'un joueur est dans le salon, le jeu signale sa présence toutes les 30 s (automatique avec l'extension). Si plus personne ne répond pendant 90 s, ou dès que le dernier joueur quitte, le salon passe en « abandonné ». Les recherches de partie oubliées depuis 10 min sont annulées.

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.

Exemple : tour par tour avec validation serveur
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)
Côté jeu (SDK angon.js ou GDevelop)
// 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.

Manipuler le plateau sans ambiguïté
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

FonctionTypeRôle
OuvrirPanneauMatchmaking(mode)ActionOuvre le panneau intégré et lance la recherche
FermerPanneauMatchmakingActionFerme le panneau (annule la recherche en cours)
PanneauMatchmakingOuvertConditionVrai tant que le panneau est affiché
RejoindreFileAttente(mode)ActionInscrit le joueur dans la file du mode
QuitterFileAttenteActionRetire le joueur de toutes les files
VerifierFileAttenteActionRafraîchit le statut du matchmaking
EnFileDAttenteConditionVrai pendant l'attente d'un salon
PartieTrouveeConditionVrai quand le salon est formé
RejoindreSalonTempsReelActionOuvre le canal Broadcast du salon
QuitterSalonTempsReelActionFerme le canal du salon
SalonTempsReelConnecteConditionVrai quand le canal est prêt
EnvoyerActionSalon(var)ActionDiffuse une variable aux autres joueurs
ActionSalonRecueConditionVrai une fois par action reçue
DerniereActionSalon()Expr. texteJSON de la dernière action reçue
EnvoyerActionPartie(nom, données)ActionEnvoie un coup au relais serveur (règles action_partie)
ChargerEtatPartieActionRecharge l'état officiel depuis le serveur
EtatPartieJson()Expr. texteJSON de l'état officiel de la partie