Skip to main content

Godot + Supabase

Geometry Survival is a multiplayer Vampire Survivors-like game that demonstrates how to build a complete online game with Godot and Supabase, hosted on GameFlow. Players choose a geometric shape and survive waves of enemies using a rock-paper-scissors kill mechanic.

Repository: geometryvival

Architecture​

When the lobby owner starts a match, a Supabase Edge Function calls the GameFlow API to allocate a dedicated server. The server's IP and port are written back to the database. Supabase Realtime notifies all clients, who then connect directly to the game server via ENet (UDP).

Stack​

TechnologyPurpose
Godot 4.6Game client + dedicated server
ENet (UDP)Multiplayer protocol
Supabase AuthAnonymous player authentication
Supabase Edge FunctionsLobby API + GameFlow server allocation
Supabase RealtimePush server connection details to all clients
GameFlow Godot SDKServer lifecycle, health reporting, and player tracking

Prerequisites​

Step 1: Clone the Repository​

git clone https://github.com/GameFlowGG/geometryvival
cd geometryvival

Step 2: Start Supabase Locally​

supabase start

This starts a local Supabase stack via Docker and applies the database migration automatically. Once ready, it prints your local credentials:

API URL:   http://127.0.0.1:54321
anon key: eyJhbGci...

Step 3: Configure the Supabase Addon​

Edit addons/supabase/.env with your local credentials:

[supabase/config]

supabaseUrl="http://127.0.0.1:54321"
supabaseKey="<your local anon key>"

Step 4: Configure GameFlow Credentials​

Create supabase/functions/.env.local:

GAMEFLOW_GAME_ID=your-game-id
GAMEFLOW_API_KEY=your-api-key

Get these from the GameFlow Dashboard → your game → Settings.

Step 5: Start Edge Functions​

supabase functions serve --env-file supabase/functions/.env.local

Leave this running in a terminal. Functions hot-reload on file changes.

Step 6: Open the Project in Godot​

Open project.godot in Godot 4.6. Press Play to run the game as a client (the login screen appears).

Step 7: Export the Dedicated Server​

To deploy to GameFlow, export the game as a Linux Server PCK:

  1. In Godot, go to Project → Export
  2. Select the Linux Server preset (already configured in export_presets.cfg)
  3. Click Export PCK/Zip → save as build/game.pck

The export template must be installed first: Editor → Manage Export Templates → Download.

Step 8: Upload to GameFlow​

  1. Go to the GameFlow Dashboard → your game
  2. Upload build/game.pck as a new build
  3. Copy your Game ID
  4. Update GAMEFLOW_GAME_ID in supabase/functions/.env.local

Step 9: Run a Match​

  1. Start Edge Functions (supabase functions serve ...)
  2. Press Play in Godot, enter a username and click Play Online
  3. Create a lobby, share the 6-digit code with other players
  4. All players mark Ready → the lobby owner clicks Start
  5. The Edge Function allocates a GameFlow server → Realtime notifies all clients → game begins

Re-export and re-upload game.pck every time you change server-side code.

Deploying to Production​

Supabase​

# Link to your remote project
supabase link --project-ref <your-project-ref>

# Push the database schema
supabase db push

# Deploy all Edge Functions
supabase functions deploy

# Set GameFlow secrets
supabase secrets set GAMEFLOW_GAME_ID=your-game-id
supabase secrets set GAMEFLOW_API_KEY=your-api-key

Then update addons/supabase/.env with your production Supabase URL and anon key (found in Dashboard → Settings → API):

[supabase/config]

supabaseUrl="https://<your-project-ref>.supabase.co"
supabaseKey="<your anon key>"

Rebuild and redistribute the game client.

Project Structure​

geometryvival/
├── scripts/
│ ├── game_constants.gd # Shared: shape names, colors, kill rule
│ ├── network_manager.gd # Autoload: ENet host/join
│ ├── supabase_manager.gd # Autoload: auth, lobbies, Realtime
│ ├── game.gd # Game loop: spawning, collisions, difficulty
│ ├── player.gd # Movement, shape cycling, damage
│ └── enemy.gd # AI: moves toward nearest player
├── supabase/
│ ├── migrations/
│ │ └── 20260326000001_initial.sql # Full DB schema
│ └── functions/
│ ├── auth-anonymous/ # Create account after anonymous sign-in
│ ├── lobbies-list/ # List public lobbies (with stale cleanup)
│ ├── lobbies-create/ # Create a lobby
│ ├── lobbies-join/ # Join by ID or invite code
│ ├── lobbies-ready/ # Toggle ready state
│ ├── lobbies-leave/ # Leave lobby (auto-deletes if empty)
│ └── lobbies-start/ # Validate ready → allocate via GameFlow → Realtime
└── addons/
├── supabase/ # godot-engine/supabase addon
└── gameflow/ # GameFlow Godot SDK addon

Gameplay​

  • WASD / Arrow keys, move
  • SPACE, cycle shape (Circle → Square → Triangle)
  • ESC, surrender (press twice) or exit if already dead

Kill rule: Circle kills Square → Square kills Triangle → Triangle kills Circle.

Enemies spawn from screen edges and increase in speed over time. The match ends when all players are dead.

Troubleshooting​

"GAMEFLOW_GAME_ID and GAMEFLOW_API_KEY must be set"​

The Edge Function is not picking up your .env.local. Make sure you are running:

supabase functions serve --env-file supabase/functions/.env.local

Players can't move​

Each player node is named after its peer ID. The authority is set in _enter_tree() in player.gd, make sure you haven't moved this to _ready(), as that is too late for MultiplayerSynchronizer to initialize correctly.

Supabase Realtime not triggering​

Confirm that both tables are added to the Realtime publication in your migration:

ALTER PUBLICATION supabase_realtime ADD TABLE public.lobbies;
ALTER PUBLICATION supabase_realtime ADD TABLE public.lobby_players;