Retour aux projets

Comment fonctionne Galactic Games

Un site de jeux dans le navigateur où vous créez un compte, voyez qui est en ligne, défiez quelqu'un et jouez en direct. Tout tourne sur un seul Cloudflare Worker, sans aucun serveur à maintenir moi-même.

Rôle
Seul, de la conception au déploiement
Stack
JavaScript, three.js, Cloudflare Workers, Durable Objects, SQLite
Statut
En ligne sur games.galacticstudios.net
Le hub de Galactic Games, avec la grille des jeux et les onglets joueurs et multijoueur

L'objectif

Je voulais un endroit pour les petits jeux que je construis, où jouer avec un ami tient en un clic et non un téléchargement. Cela demandait de vrais comptes, un lobby qui se met à jour en direct, et des parties en ligne qui paraissent justes. Il fallait aussi que ça coûte presque rien à faire tourner tant que c'est petit.

La structure

Un seul Worker sert le site statique et une petite API. Tout ce qui doit garder un état vit dans un Durable Object : une instance unique d'une classe que Cloudflare garde active, avec son propre stockage.

Les navigateurs se connectent à un Worker, qui route vers les Durable Objects Hub, Room et Arena NavigateursHTTP et WebSocket Workersite et routes /api Hubcomptes, lobby, résultats Roomun par code, deux joueurs ArenaKnife Duels, jusqu'à 4v4
  • Hub est une instance unique avec sa propre base de données SQLite. Elle contient les comptes, les sessions, le lobby en direct, l'historique des parties et les résultats.
  • Room est créé pour chaque code à 5 caractères. Il relaie les messages entre exactement deux sockets, l'hôte et l'invité. Un troisième socket se fait dire que la salle est pleine.
  • Arena fait tourner les parties de Knife Duels, du 1v1 jusqu'au 4v4.

Du défi au match

  1. Vous ouvrez l'onglet Joueurs. Votre navigateur garde un WebSocket ouvert vers le Hub, qui indique à tout le monde qui est en ligne.
  2. Vous défiez quelqu'un. Cette personne le voit tout de suite et accepte ou refuse.
  3. En cas d'acceptation, le Hub crée un code de salle et envoie le feu vert aux deux joueurs.
  4. Les deux jeux rejoignent cette Room, qui relaie les entrées entre eux.
  5. Quand le match se termine, les jeux des deux joueurs rapportent le résultat. Il ne compte que s'ils sont d'accord.

Le socket du lobby utilise l'API d'hibernation des WebSockets, si bien qu'un lobby calme ne garde pas le Hub réveillé, ni facturé, pendant que les gens restent simplement sur la page.

Garder ça équitable

Faire tourner chaque match sur le serveur coûte trop cher pour un petit site, alors j'ai plutôt réparti la confiance. Dans Oathsteel, le duel à l'épée, l'hôte décide des coups et des rounds, chaque joueur possède son propre mouvement, et l'autre combattant est piloté par des entrées relayées. Le mouvement reste réactif des deux côtés, et un camp a le dernier mot sur tout ce qui détermine un vainqueur.

Les résultats n'atteignent le classement que pour les matchs faits dans le lobby, et seulement quand les jeux des deux joueurs rapportent le même résultat. Knife Duels a plus de joueurs par match, donc un résultat compte une fois que la majorité des joueurs humains est d'accord dessus.

Deux combattants qui s'affrontent dans l'arène de duel Oathsteel
Oathsteel, le duel à l'épée. Les matchs à code de salle et les défis en lobby passent tous les deux par le même Room.

Des comptes faits proprement

Les mots de passe sont hachés avec PBKDF2-SHA256 à 100 000 itérations. Les jetons de session sont eux aussi stockés hachés, si bien qu'une base de données volée ne donnerait accès à aucun compte.

Tester le multijoueur

Room et Hub ont des vérifications automatisées qui tournent contre un Worker local : 8 pour le relais, et 21 pour les comptes, le lobby et les résultats. Knife Duels garde sa logique de jeu séparée de three.js et de la page, si bien qu'elle peut être testée unitairement à part, avec 178 tests.

Pour l'épreuve réelle, un script fait passer deux navigateurs headless à travers un duel en ligne complet. Le hic, c'est que deux navigateurs à rendu logiciel sur une même machine se privent l'un l'autre, donc ils tournent bien en dessous de la vitesse réelle et chaque attente du test doit être généreuse.

Ce que j'en ai retenu

  • Les Durable Objects rendent l'état du multijoueur facile à raisonner : un objet, un seul endroit où vit la vérité.
  • Ajouter un jeu tient en un dossier et une ligne dans une liste, ce qui explique pourquoi le hub continue de grandir.
  • Pas d'étape de build et un three.js auto-hébergé signifient que le site se déploie en quelques secondes et n'a rien qui puisse casser entre deux versions.