Create a BoredBundle mod
A BoredBundle mod combines a Godot host screen with a small browser app that runs on each player's phone. The SDK handles players, connections, reconnection, and message delivery; your mod supplies the game rules and interface.
addons/bored_bundle/ folder from the BoredBundle source tree.Add the SDK, a manifest, host-side scenes and scripts, and a phone web app.
Extend the SDK classes and exchange game-specific actions and messages with phones.
Compile its static HTML, CSS, and JavaScript into the directory named by the manifest.
Use the SDK's Godot editor tools to produce a distributable folder.
1. Set up the project
Create this basic layout. Only src/ is packed into the mod's PCK; phone files and content folders are shipped as loose files.
my_mod/
├── project.godot
├── game.json
├── addons/
│ └── bored_bundle/
├── src/
│ ├── my_game_manager.gd
│ ├── my_play_screen.gd
│ └── play_screen.tscn
├── web_app/
│ └── dist/
│ └── index.html
├── avatars/ # optional player avatars
├── assets/ # optional images, music, etc.
└── data/ # optional game data
- Copy
addons/bored_bundle/into the project. - In Godot, open Project → Project Settings → Plugins and enable Bored Bundle SDK.
- If your lobby scene uses
QRCodeRect, also copyaddons/qr_code/.
The plugin installs the BoredBundle autoload and adds validation and build commands to the Project → Tools menu.
2. Describe the mod in game.json
The manifest sits at the project root. Paths below are authoring paths; the builder rewrites files under res://src/ into the final mod namespace.
{
"schema_version": 1,
"min_host_version": 1,
"min_sdk_version": 1,
"game_id": "MyGame",
"display_name": "My Game",
"description": "A short description of the game.",
"kind": "code",
"game_manager_script": "res://src/my_game_manager.gd",
"play_screen_scene": "res://src/play_screen.tscn",
"intro_scene": "",
"web_app_dir": "web_app/dist",
"avatar_base_path": "avatars",
"data_dir": "data",
"assets_dir": "assets",
"min_players": 2,
"max_players": 8,
"supports_cpu": false
}
game_id must be a valid Godot identifier: use letters, numbers, and underscores, do not begin with a number, and do not use spaces. Set min_sdk_version to the oldest SDK that provides every API your mod calls.
3. Build the Godot host
Your manager owns the rules. Extend the SDK manager, call the parent _ready(), listen for the start request, and handle actions sent by phones.
# src/my_game_manager.gd
extends "res://addons/bored_bundle/runtime/game_manager.gd"
func _ready() -> void:
super._ready()
start_game_requested.connect(_start_game)
func _start_game() -> void:
game_active = true
broadcast_to_all({"type": "game_started"})
func handle_game_action(player_id: String, action: String, data: Dictionary) -> bool:
if action == "answer":
broadcast_to_all({
"type": "answer_received",
"player_id": player_id
})
return true
return false
func send_game_state_to_player(player_id: String) -> void:
# Restore important state when this player reconnects.
send_to_player(player_id, {"type": "game_state", "active": game_active})
The play-screen script should extend the matching SDK screen:
# src/my_play_screen.gd
extends "res://addons/bored_bundle/runtime/play_screen.gd"
Attach that script to the root of play_screen.tscn. Build the host UI as you would any Godot scene. These are the core manager APIs:
send_to_player() and broadcast_to_all()
get_player(), get_players(), and get_playing_players()
apply_settings(), send_game_state_to_player(), and run_game_cleanup()
change_scene() plus the built-in pause overlay
For editor previews without a running host, use BoredBundle.set_preview_players(). Network sends safely return false when the standalone project is not attached to the engine runtime.
4. Create the phone app
The phone interface can use any web framework—or none—as long as its production build is static and lands in web_app/dist/ (or the directory configured by web_app_dir). It must connect through the BoredBundle web client, render host messages, and send player actions.
import { createWebAppClient } from "@bored-bundle/web-app";
const client = createWebAppClient();
client.on("server_message_received", (message) => {
if (message.type === "game_started") showGame();
});
document.querySelector("#answer").addEventListener("click", () => {
client.sendAction("answer", { value: "my-answer" });
});
client.connect();
If you are developing inside the BoredBundle source repository, install the shared package into your web project with a relative path:
cd path/to/my_mod/web_app
npm install path/to/web_packages/web-app
npm run build
The client manages the WebSocket handshake, persistent player identity, join code, and reconnection. Host messages you invent and phone actions you send form your game's own small protocol.
5. Validate, build, and install
- Build the phone app and confirm
web_app/dist/index.htmlexists. - Let Godot finish importing any images, audio, and fonts used under
src/. - Run Project → Tools → Validate Bored Bundle Mod.
- Run Project → Tools → Build Bored Bundle Mod.
The finished distributable is written to build/MyGame/. It contains the rewritten manifest, MyGame.pck, the built phone app, and any loose data, asset, or avatar folders.
You can also build from the command line:
godot --headless --path /path/to/my_mod \
--script res://addons/bored_bundle/tools/build_mod.gd -- \
res:// /path/to/my_mod/build
To test the result, copy the entire build/MyGame/ folder into the BoredBundle executable's user://mods/ directory. Publish that same folder through Workshop when it is ready.
src/ PCK and loose web/content files. The BoredBundle executable supplies the SDK at runtime.SDK feature versions
Raise min_sdk_version only when you use a feature introduced by that version. Hosts reject incompatible mods before loading their scripts.
Sub-mod content overlays and content-root helpers.
Drawing sessions, drawing canvas, and quit helpers.
Player-aware phone controller sessions.
Shuffled background-music playlists.
Play BoredBundle