Skip to content

About

A multiplayer game workshop using Flame and Supabase

Resources

Stars

0 stars

Watchers

0 watching

Forks

 
 

Latest commit

 

History

21 Commits

Folders and files

Repository files navigation

Building a Real-Time Multiplayer Space Game with Flame and Supabase

Workshop monorepo for building a last-player-standing multiplayer game. The game runs entirely on Flutter and serverless primitives: the Flame engine drives the 2D canvas, and Supabase Realtime powers the netcode with Broadcast events and Presence. There is no game server.

The database layer runs on the typesafe Supabase v3 groundwork, merged into supabase-flutter and published to pub.dev:

  • #1634 typed table access with PostgrestTable and PostgrestColumn, in supabase_flutter 3.0.0-dev.9
  • #1635 the supabase_typegen code generator, released as supabase_typegen 0.5.1

Everything resolves from pub.dev. There are no git dependencies and no dependency_overrides anywhere in the workspace.

Packages

Package Description
packages/skeleton The starting point for the exercises: dependencies wired up, no game
packages/game The finished multiplayer game (Flutter web and macOS)
packages/slides The workshop slide deck, built with flutter_deck

Prerequisites

Setup

dart pub get
supabase start

supabase start boots the local stack (API on port 54621) and applies the scores table migration. The game defaults to the local URL and the standard local publishable key, so no configuration is needed for local play.

Start the exercises

packages/skeleton is where attendees write their code. It carries the dependencies, the dart-define configuration, the tuning constants, and the web scaffolding, and nothing else. The game itself is what the exercises build:

melos run skeleton

See packages/skeleton/README.md for the details.

Run the game

melos run game

Or manually, with explicit configuration:

cd packages/game
flutter run -d chrome \
  --dart-define=SUPABASE_URL=http://127.0.0.1:54621 \
  --dart-define=SUPABASE_KEY=sb_publishable_... \
  --dart-define=ROOM=main

Open two instances (run the command twice) to play against yourself. Players sharing the same ROOM value meet in the same arena; split large groups into separate rooms to stay under the Realtime message limits.

Run the slides

melos run slides

Navigate with the arrow keys. Press the period key for the navigation drawer. The presenter view with speaker notes opens from the deck controls.

Tests

The integration smoke test exercises Broadcast, Presence, and the typed scores table against the local stack:

supabase start
cd packages/game
flutter test

Regenerating the typed database models

packages/game/lib/src/db/supabase_schema.g.dart is generated by supabase_typegen from supabase/schema.json, a snapshot of the postgres-meta introspection document. After changing a migration, update the snapshot to match and regenerate:

cd packages/game
dart run supabase_typegen \
  --output lib/src/db/supabase_schema.g.dart \
  --import package:supabase_flutter/supabase_flutter.dart \
  < ../../supabase/schema.json

The types can also be generated straight from a database, without the snapshot. A recent Supabase CLI (2.120 has it) supports Dart directly. Use --local for the running local stack, or --project-id your-project-ref for a hosted project after supabase login:

cd packages/game
supabase gen types --lang dart --local > lib/src/db/supabase_schema.g.dart

The CLI runs the supabase_typegen dev dependency of the package it is called from. On an older CLI, call the generator yourself:

cd packages/game
dart run supabase_typegen --local --output lib/src/db/supabase_schema.g.dart

Deploying

supabase link --project-ref your-project-ref
supabase db push

cd packages/game
flutter build web \
  --dart-define=SUPABASE_URL=https://your-project-ref.supabase.co \
  --dart-define=SUPABASE_KEY=sb_publishable_...

Enable anonymous sign-ins for the hosted project under Authentication settings, then serve build/web from any static host.

How the netcode works

  • One Realtime channel per room carries five broadcast events: state, shoot, hit, death, and roundStart.
  • The netcode is peer-authoritative: every client simulates its own player and bullets, and the victim of a hit applies its own damage before broadcasting the result. Each player has exactly one authority, so there are no conflicts.
  • A round is defined by {seed, startedAt}: every client generates an identical world from the seed and derives the round clock from the start time. The world costs zero bandwidth.
  • Player state is throttled to 20 packets per second and remote players smooth over the gaps with dead reckoning.
  • Presence powers the lobby roster, disconnect handling, and match discovery: players in a match advertise the seed so late joiners can spectate.
  • The winner records the round through the typed scores table, and the lobby leaderboard is a typed Postgres Changes stream.

About

A multiplayer game workshop using Flame and Supabase

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages