diff --git a/agent-setup.mdx b/agent-setup.mdx new file mode 100644 index 0000000..51db32e --- /dev/null +++ b/agent-setup.mdx @@ -0,0 +1,81 @@ +--- +title: "Agent Setup" +description: "Point any AI coding agent at Summercraft: one paste-prompt that takes it from empty directory to a multiplayer Godot game submitted for review." +icon: "bot" +--- + +Summercraft is built to be published to by AI coding agents. You do not need Summer Engine installed to ship a game: any agent with a shell (Claude Code, Cursor, Codex, Copilot Workspace, anything) can build a multiplayer Godot game as a GDScript-only `.pck` and submit it through the live publish API. A human reviews every submission before it goes live. + +## Point your agent at Summercraft + +Paste this into any coding agent: + +```text +Fetch https://docs.summerengine.com/agent-setup/prompt.md and follow it exactly, +top to bottom. It is the canonical instruction set for building a multiplayer +Godot 4.5 game against the Summer SDK and publishing it to Summercraft. + +Context you need before you start: +- Summercraft (https://summercraft.ai) is the platform where Summer SDK games + are published and played. The publish API lives at https://summercraft.ai/api/*. +- You will build a GDScript-only game that extends SummerGame, export a .pck + with Godot 4.5 headless (no export templates needed), and publish it in four + API calls: create game -> get presigned upload URL -> PUT the .pck -> + finalize (the server re-verifies your sha256). +- Publishing needs a Summercraft access token in SUMMERCRAFT_ACCESS_TOKEN. + Build and export first; stop and ask me for the token before the publish step. + The prompt contains the instructions to show me for getting one. +- Each publish step is limited to 1 request per hour per account, so validate + everything locally before touching the API. +- After finalize the release is "pending_review" in a manual human review + queue. There is no auto-publish, and browser/desktop play of uploaded Godot + games is not live yet - say so honestly when you report back. +- Any docs page is fetchable as raw markdown by adding .md to its URL; the + index is at https://docs.summerengine.com/llms.txt. + +If the prompt URL is unreachable, stop and tell me instead of improvising. +``` + +The prompt at [/agent-setup/prompt](/agent-setup/prompt) is the canonical, always-current version — served as plain markdown at `https://docs.summerengine.com/agent-setup/prompt.md` so agents can fetch it directly. It contains a complete, export-verified multiplayer game template (king-of-the-hill, host-authoritative, 1–8 players), the local validation steps, the exact publish calls with every error explained, and the honest post-submission status. + +## What the agent will do + +1. **Check prerequisites** — Godot 4.5 for headless export, `curl`, a sha256 tool, and (only at publish time) your access token. +2. **Build a multiplayer-native game** — `extends SummerGame`, all gameplay authority on server paths, player state via `set_synced`, local SDK stubs so everything parses and smoke-runs without the platform runtime. +3. **Validate locally** — headless smoke run, banned-API self-check, pack-content check. The upload budget is 1/hour, so the prompt front-loads every check. +4. **Export a game-only `.pck`** — the preset excludes stubs and project config; ships GDScript as readable source. +5. **Publish through the live API** — create game → presigned upload → server-verified finalize. See [Exporting and Uploading](/api-reference/summer-sdk/exporting-and-uploading-your-game) for the full reference. +6. **Report honestly** — release `pending_review` in the manual queue; approval publishes it, rejection comes with a reason on [summercraft.ai/creator](https://summercraft.ai/creator). + +## The one thing the agent cannot do: sign in + +Agents must never handle your password. Create your account and session yourself: + +1. Sign up / sign in at [summercraft.ai](https://summercraft.ai). +2. In that tab, open dev tools → Application → Cookies → `summercraft.ai` and find the cookie whose name ends in `-auth-token` (possibly chunked into `.0`/`.1` — concatenate the values in order). +3. URL-decode the value; if it starts with `base64-`, base64-decode the rest. The JSON inside has an `access_token` field — that string is the Bearer token. +4. Hand it to your agent as the `SUMMERCRAFT_ACCESS_TOKEN` environment variable. It expires after about an hour — enough for one publish. Re-extract it when it expires. + +There is no dedicated token page yet; this manual step is the current path. The publish API accepts `Authorization: Bearer ` on every endpoint. + +## What is live vs. coming + +| Capability | Status | +|---|---| +| Create Godot game records via API | **Live** | +| Direct-to-storage `.pck` upload (presigned, write-once) | **Live** | +| Server-side sha256 verification at finalize | **Live** | +| Manual human review queue | **Live** — every release, no auto-publish | +| Authenticated release downloads (`download-url`) | **Live** — owner/admins always; others once published | +| Browser `.pck` submit page ([summercraft.ai/submit](https://summercraft.ai/submit)) | **Live** — multipart flow with automated static scanner | +| Playing uploaded Godot games in the browser | Coming — not yet available | +| Playing uploaded Godot games in the desktop shell | Coming — not yet available | +| Hosted dedicated game servers for uploaded games | Coming — not yet available | +| Automated runtime sandbox for submissions | Coming — review is manual today | +| `SummerMultiplayerPeer` transport (stock `@rpc` compatibility) | Coming — contract published, being built in the engine fork; current runtime multiplayer is host-authoritative over ENet | + +Docs on this site never promise capabilities that are not deployed. If a page and reality disagree, trust the API's own error text and mail [founders@summerengine.com](mailto:founders@summerengine.com). + +## For agents reading this page directly + +Fetch `https://docs.summerengine.com/agent-setup/prompt.md` and follow it. Every page here is markdown-addressable: append `.md` to any URL. Index: `https://docs.summerengine.com/llms.txt` (or `llms-full.txt` for full content). diff --git a/agent-setup/prompt.mdx b/agent-setup/prompt.mdx new file mode 100644 index 0000000..a53e79d --- /dev/null +++ b/agent-setup/prompt.mdx @@ -0,0 +1,543 @@ +--- +title: "Agent Prompt" +description: "The canonical instruction set for AI coding agents building and publishing a multiplayer Godot game to Summercraft. Fetch this page raw at /agent-setup/prompt.md." +icon: "bot" +noindex: false +--- + +You are an AI coding agent. Your job is to build a multiplayer Godot game as a GDScript-only `.pck` and publish it to Summercraft for review. Follow this document top to bottom. Everything in it was verified against the live platform; where a capability is not live yet, this document says so explicitly. + +Any page of these docs is fetchable as raw markdown by appending `.md` to its URL. The full page index is at `https://docs.summerengine.com/llms.txt`. + +## 1. What Summercraft is + +Summercraft (`https://summercraft.ai`) is the platform where games built against the Summer SDK are published and played. You write a plain Godot 4.5 game in GDScript, extend the SDK base class `SummerGame`, export a `.pck` that contains only your game files, and submit it through the publish API. A human reviews every submission before it goes live. There is no auto-publish. + +- The publish API lives at `https://summercraft.ai/api/*`. +- Docs live at `https://docs.summerengine.com`. +- The Summer SDK classes (`Summer`, `SummerGame`, `SummerPlayer`, `SummerCharacter3D`) are provided by the Summercraft runtime when your pack loads. They are not files you ship. + +**What is live today:** creating a game record, direct-to-storage artifact upload with server-side sha256 verification, the manual review queue, and authenticated release downloads. + +**What is not live yet (do not promise these to your operator):** playing uploaded Godot games in the browser, playing them inside the desktop shell, hosted dedicated game servers for uploaded games, and the automated runtime sandbox. Multiplayer sessions in the current runtime run host-authoritatively over ENet; the `SummerMultiplayerPeer` transport (stock `@rpc` / `MultiplayerSynchronizer` compatibility) is a published contract still being built in the engine fork. + +## 2. Prerequisites — check all three before writing any code + +1. **Godot 4.5.x.** Run `godot --version` (or find the binary, e.g. `/Applications/Godot.app/Contents/MacOS/Godot` on macOS). Any 4.5 build works; you only need it for headless export, no export templates required. If it is missing, ask your operator to install Godot 4.5 from `https://godotengine.org/download` before continuing. +2. **A Summercraft access token.** Check for a `SUMMERCRAFT_ACCESS_TOKEN` environment variable or ask your operator for one. Without it every publish call returns `401 sign_in_required`. **Stop and ask before step 6 if you do not have it** — you can build and export everything first. Show your operator this if they do not know how to get one: + > Sign in at `https://summercraft.ai` (create the account yourself — never let an agent handle your password). Then open your browser dev tools on that tab: Application → Cookies → `summercraft.ai`, and find the cookie whose name ends in `-auth-token` (it may be split into `.0`/`.1` chunks — concatenate their values in order). URL-decode the value; if the result starts with `base64-`, base64-decode the rest. Inside the JSON is an `access_token` field: that string is the Bearer token. It expires after about an hour, which is enough for one publish. There is no dedicated token page yet; this manual step is the current path. +3. **`curl` and a sha256 tool** (`shasum -a 256` or `sha256sum`). + +Auth model, exactly: every publish endpoint accepts `Authorization: Bearer ` (or a signed-in browser session cookie). A present-but-invalid Bearer token is a hard `401` — it never falls back to a cookie. If you get `401` with a token set, the token has expired: ask your operator for a fresh one. + +## 3. Know the rules before you build + +Your `.pck` must contain **only your game**. The platform enforces, at review and in the browser submit scanner: + +- **GDScript only.** No `.gdextension`, `.so`, `.dll`, `.dylib`, `.framework`, `.res` files. +- **No banned APIs** anywhere in your scripts, even in dead or conditional branches. The blocklist includes `OS.execute`, `OS.shell_open`, `OS.create_process`, `FileAccess`, `DirAccess`, `HTTPRequest`, `HTTPClient`, `JavaScriptBridge`, `ClassDB.instantiate`, `Thread.new`, `Expression`, raw sockets (`StreamPeerTCP`, `PacketPeerUDP`, `TCPServer`, `UDPServer`, `WebSocketPeer`), reflection calls (`.call(`, `.callv(`, `.call_deferred(`, `Callable(`), `Engine.get_singleton`, `Marshalls.base64_to_variant`, `ResourceSaver`, `ProjectSettings.load_resource_pack`, and root-tree escapes (`get_node("/root`, `get_tree().root`). Full list with rationale: `https://docs.summerengine.com/api-reference/summer-sdk/banned-apis-reference.md`. Persistence goes through `Summer.data`, economy through `Summer.economy` — never your own I/O or networking. +- **No reserved paths** in the pack: `res://sdk/`, `res://core/`, `res://server/`, `res://client/`, `res://official_games/`, `res://bootstrap.*`, `res://project.godot`, `res://summer.cfg`, `res://test_runner.*`. The export preset below keeps them out automatically. +- **`manifest.json` at the pack root** with required keys `id`, `name`, `version`, `summer_sdk` (`"1.0"`), `entry_scene`, `min_players`, `max_players`. The entry scene's root script must `extends SummerGame` or the runtime refuses to load the pack. +- **Host-authoritative multiplayer is the only mode.** All gameplay decisions run on server paths guarded by `Summer.is_server()`. Clients render synced state via `set_synced`/`get_synced`; they never decide outcomes. + +## 4. Build the game — multiplayer-native template + +Build multiplayer by default: `min_players: 1` so it works solo, `max_players: 8` so it works with friends, all authority server-side. The template below is a complete king-of-the-hill arena, verified to parse, smoke-run, and export with Godot 4.5.1. Adapt the gameplay; keep the structure. + +Create this exact layout in a fresh directory: + +```text +hold-the-hill/ +├── project.godot # local dev config — never shipped +├── export_presets.cfg # export preset — never shipped +├── manifest.json # shipped at pack root +├── main.gd # your game, extends SummerGame +├── main.tscn # entry scene +├── player.tscn # player scene (SummerCharacter3D template) +└── sdk/ # LOCAL STUBS, excluded from export + ├── summer.gd + ├── summer_game.gd + ├── summer_player.gd + └── summer_character_3d.gd +``` + +The `sdk/` folder holds **local stubs** that mirror the real SDK surface so your code parses and smoke-runs without the Summercraft runtime. The export preset excludes `sdk/*`, so the stubs never ship; on the platform the runtime provides the real classes at the same paths. Do not add behavior to the stubs — they are compile shims, not the SDK. + +`project.godot`: + +```ini +; Engine configuration file. +config_version=5 + +[application] + +config/name="Hold the Hill" +run/main_scene="res://main.tscn" +config/features=PackedStringArray("4.5") + +[autoload] + +Summer="*res://sdk/summer.gd" + +[rendering] + +renderer/rendering_method="mobile" +``` + +`manifest.json`: + +```json +{ + "id": "hold-the-hill", + "name": "Hold the Hill", + "version": "1.0.0", + "summer_sdk": "1.0", + "entry_scene": "main.tscn", + "player_scene": "player.tscn", + "min_players": 1, + "max_players": 8, + "description": "King-of-the-hill arena. Stand in the gold circle to score. First to 100 points wins.", + "genre": "action", + "tags": ["multiplayer", "arena", "king-of-the-hill"] +} +``` + +Change `id`, `name`, and the descriptive fields for your game. `entry_scene` and `player_scene` are relative to the manifest (pack root). + +`main.gd` — the game. Every decision happens on the server path: + +```gdscript +extends SummerGame + +const ROUND_SECONDS := 180.0 +const MOVE_SPEED := 7.0 +const GRAVITY := 20.0 +const WIN_SCORE := 100 +const HILL_RADIUS := 4.0 +const POINT_INTERVAL := 1.0 + +var _hill_center := Vector3.ZERO +var _tick := 0.0 +var _round_over := false + +func _game_init() -> void: + var spawn_root := get_node_or_null("SpawnPoints") + if spawn_root: + for child in spawn_root.get_children(): + if child is Node3D: + spawn_points.append(child.global_position) + var hill := get_node_or_null("Hill") + if hill is Node3D: + _hill_center = hill.global_position + +func _game_start() -> void: + _round_over = false + Summer.set_time_limit(ROUND_SECONDS) + Summer.send_announcement("Hold the hill! First to %d points wins." % WIN_SCORE) + +func _game_end() -> void: + _round_over = true + var best = null + var best_score := -1 + for p in get_players(): + var s := _score_of(p) + if s > best_score: + best_score = s + best = p + if best != null: + Summer.send_announcement("%s wins with %d points." % [str(best.display_name), best_score]) + +func _player_joined(player) -> void: + player.set_synced("score", 0) + if player.has_method("respawn"): + player.respawn(get_random_spawn_point()) + +func _player_left(_player) -> void: + pass + +func _process(delta: float) -> void: + super._process(delta) + if not Summer.is_server() or _round_over: + return + for p in get_players(): + apply_default_movement(p, delta, MOVE_SPEED, GRAVITY) + _tick += delta + if _tick < POINT_INTERVAL: + return + _tick = 0.0 + for p in get_players(): + if p is Node3D and p.global_position.distance_to(_hill_center) <= HILL_RADIUS: + var s := _score_of(p) + 1 + p.set_synced("score", s) + if s >= WIN_SCORE: + Summer.send_announcement("%s takes the crown!" % str(p.display_name)) + end_game() + return + +func _score_of(player) -> int: + var raw = player.get_synced("score") + return int(raw) if raw != null else 0 +``` + +`main.tscn` — entry scene: floor, hill marker, four spawn points, light, camera: + +```text +[gd_scene load_steps=6 format=3] + +[ext_resource type="Script" path="res://main.gd" id="1"] + +[sub_resource type="BoxShape3D" id="floor_shape"] +size = Vector3(40, 1, 40) + +[sub_resource type="BoxMesh" id="floor_mesh"] +size = Vector3(40, 1, 40) + +[sub_resource type="CylinderMesh" id="hill_mesh"] +top_radius = 4.0 +bottom_radius = 4.0 +height = 0.2 + +[sub_resource type="StandardMaterial3D" id="hill_mat"] +albedo_color = Color(0.95, 0.7, 0.2, 1) + +[node name="Main" type="Node3D"] +script = ExtResource("1") + +[node name="Floor" type="StaticBody3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.5, 0) + +[node name="CollisionShape3D" type="CollisionShape3D" parent="Floor"] +shape = SubResource("floor_shape") + +[node name="MeshInstance3D" type="MeshInstance3D" parent="Floor"] +mesh = SubResource("floor_mesh") + +[node name="Hill" type="MeshInstance3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.1, 0) +mesh = SubResource("hill_mesh") +material_override = SubResource("hill_mat") + +[node name="SpawnPoints" type="Node3D" parent="."] + +[node name="Spawn1" type="Node3D" parent="SpawnPoints"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 12, 1, 12) + +[node name="Spawn2" type="Node3D" parent="SpawnPoints"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -12, 1, 12) + +[node name="Spawn3" type="Node3D" parent="SpawnPoints"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 12, 1, -12) + +[node name="Spawn4" type="Node3D" parent="SpawnPoints"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -12, 1, -12) + +[node name="Players" type="Node3D" parent="."] + +[node name="Sun" type="DirectionalLight3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 0.642788, 0.766044, 0, -0.766044, 0.642788, 0, 10, 0) +shadow_enabled = true + +[node name="Camera3D" type="Camera3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 0.819152, 0.573576, 0, -0.573576, 0.819152, 0, 18, 26) +``` + +`player.tscn` — references the runtime's 3D character template script. The reference is expected; the file itself must never be in your pack: + +```text +[gd_scene load_steps=4 format=3] + +[ext_resource type="Script" path="res://sdk/summer_character_3d.gd" id="1"] + +[sub_resource type="CapsuleShape3D" id="player_shape"] + +[sub_resource type="CapsuleMesh" id="player_mesh"] + +[node name="Player" type="CharacterBody3D"] +script = ExtResource("1") + +[node name="CollisionShape3D" type="CollisionShape3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0) +shape = SubResource("player_shape") + +[node name="MeshInstance3D" type="MeshInstance3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0) +mesh = SubResource("player_mesh") +``` + +The four stub files. `sdk/summer.gd`: + +```gdscript +# LOCAL STUB - excluded from export, never uploaded. +extends Node + +func end_game() -> void: pass +func set_time_limit(_seconds: float) -> void: pass +func get_time_remaining() -> float: return 0.0 +func get_players() -> Array: return [] +func get_player_count() -> int: return 0 +func get_max_players() -> int: return 8 +func send_announcement(text: String) -> void: print("[Summer stub] announce: ", text) +func is_server() -> bool: return true +func is_client() -> bool: return false +``` + +`sdk/summer_game.gd`: + +```gdscript +# LOCAL STUB - excluded from export, never uploaded. +class_name SummerGame +extends Node3D + +var spawn_points: Array = [] + +func _ready() -> void: + _game_init() + _game_start() + +func _process(_delta: float) -> void: + pass + +func _game_init() -> void: pass +func _game_start() -> void: pass +func _game_end() -> void: pass +func _player_joined(_player) -> void: pass +func _player_left(_player) -> void: pass + +func end_game(_from_timer: bool = false) -> void: + _game_end() + +func set_time_limit(_seconds: float) -> void: pass +func get_time_remaining() -> float: return 0.0 +func get_players() -> Array: return [] +func get_player_count() -> int: return 0 +func get_max_players() -> int: return 8 + +func get_random_spawn_point() -> Vector3: + if spawn_points.is_empty(): + return Vector3.ZERO + return spawn_points[randi() % spawn_points.size()] + +func apply_default_movement(_player, _delta: float, _move_speed: float = 7.0, _gravity: float = 20.0) -> void: + pass +``` + +`sdk/summer_player.gd`: + +```gdscript +# LOCAL STUB - excluded from export, never uploaded. +class_name SummerPlayer +extends Node + +var peer_id: int = 0 +var player_id: String = "" +var display_name: String = "Player" +var avatar: Dictionary = {} + +var _synced: Dictionary = {} + +func set_synced(key: String, value) -> void: + _synced[key] = value + +func get_synced(key: String): + return _synced.get(key) +``` + +`sdk/summer_character_3d.gd`: + +```gdscript +# LOCAL STUB - excluded from export, never uploaded. +class_name SummerCharacter3D +extends CharacterBody3D + +var peer_id: int = 0 +var player_id: String = "" +var display_name: String = "Player" +var avatar: Dictionary = {} + +var health: float = 100.0 +var max_health: float = 100.0 +var is_alive: bool = true + +var _synced: Dictionary = {} + +func set_synced(key: String, value) -> void: + _synced[key] = value + +func get_synced(key: String): + return _synced.get(key) + +func respawn(position_in: Vector3 = Vector3.ZERO) -> void: + global_position = position_in + health = max_health + is_alive = true + +func damage(amount: float) -> void: + health = maxf(0.0, health - amount) + is_alive = health > 0.0 + +func heal(amount: float) -> void: + health = minf(max_health, health + amount) + +func kill() -> void: + health = 0.0 + is_alive = false + +func teleport(position_in: Vector3) -> void: + global_position = position_in +``` + +`export_presets.cfg` — this is what keeps stubs and project config out of the pack: + +```ini +[preset.0] + +name="Summer Game PCK" +platform="Linux" +runnable=true +advanced_options=false +dedicated_server=false +custom_features="" +export_filter="all_resources" +include_filter="manifest.json" +exclude_filter="sdk/*" +export_path="game.pck" +patches=PackedStringArray() +encryption_include_filters="" +encryption_exclude_filters="" +seed=0 +encrypt_pck=false +encrypt_directory=false +script_export_mode=0 + +[preset.0.options] + +custom_template/debug="" +custom_template/release="" +debug/export_console_wrapper=1 +binary_format/embed_pck=false +texture_format/s3tc_bptc=true +texture_format/etc2_astc=false +binary_format/architecture="x86_64" +ssh_remote_deploy/enabled=false +``` + +`script_export_mode=0` ships your GDScript as readable text — required so review can read your source. If you add asset folders, they are included automatically (`all_resources`); add non-resource data files (e.g. extra `.json`) to `include_filter` as a comma-separated list. + +## 5. Validate and export + +Run all four checks. `$GODOT` is your Godot 4.5 binary path. + +```bash +# 5a. Import resources (first run only; expect exit 0) +$GODOT --headless --path . --import + +# 5b. Smoke run: scene loads, hooks fire, no script errors (expect exit 0, +# and the announcement line printed by the stub) +$GODOT --headless --path . --quit-after 120 + +# 5c. Banned-API self-check: must print nothing +grep -rnE "OS\.(execute|shell_open|create_process|create_instance|kill)|FileAccess|DirAccess|HTTPRequest|HTTPClient|JavaScriptBridge|ClassDB\.instantiate|Thread\.new|Mutex\.new|Semaphore\.new|StreamPeerTCP|PacketPeerUDP|TCPServer|UDPServer|WebSocketPeer|\.callv?\(|\.call_deferred\(|Callable\(|Engine\.get_singleton|Expression|Marshalls\.base64_to_variant|ResourceSaver|ProjectSettings\.load_resource_pack|get_node\(\"/root|get_tree\(\)\.root" \ + --include="*.gd" --include="*.tscn" --include="*.tres" . | grep -v "^\./sdk/" + +# 5d. Export the pack (no export templates needed for --export-pack) +$GODOT --headless --path . --export-pack "Summer Game PCK" game.pck +``` + +Notes on the checks: the pass condition for 5c is **empty output** — `grep` exits non-zero when it finds nothing, so do not treat its exit code as a failure. If the smoke run prints `SCRIPT ERROR`, fix it before exporting — the same error will make the platform runtime refuse your pack. + +Then measure the artifact and keep the values for step 6: + +```bash +ls -l game.pck # must be >= 1024 bytes and <= 536870912 (512 MiB) +SHA256=$(shasum -a 256 game.pck | cut -d' ' -f1) # Linux: sha256sum game.pck | cut -d' ' -f1 +SIZE_BYTES=$(wc -c < game.pck | tr -d ' ') +echo "$SHA256 $SIZE_BYTES" +``` + +## 6. Publish through the live API + +Base URL `https://summercraft.ai`. All requests JSON unless stated. Every step needs `Authorization: Bearer $SUMMERCRAFT_ACCESS_TOKEN`. + +**Budget warning:** each of the three write steps (create game, upload-url, finalize) has its own limit of **1 request per hour per account**. Validation failures (4xx before the limit check) do not spend it, but a successful call does. You get one full publish attempt per hour — do not experiment against these endpoints; get steps 4–5 right first. + +### 6a. Create the game record (once per game, not per version) + +```bash +curl -sS -X POST "https://summercraft.ai/api/games/engine" \ + -H "Authorization: Bearer $SUMMERCRAFT_ACCESS_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"name": "Hold the Hill"}' +``` + +- `201 { "gameId": "", "slug": "hold-the-hill", "kind": "engine", "status": "draft", "uploadUrlEndpoint": "/api/games//releases/upload-url" }`. Save it: `GAME_ID=`. +- Optional body field `"slug"`: an explicit slug fails with `409 slug_taken` if taken; without it the server derives one and walks around collisions. +- `401 sign_in_required` → token missing/expired. (This endpoint's 401 text mentions only the session cookie — Bearer tokens are accepted here all the same.) `429 rate_limited` → body includes `retryAt`; wait. `409 slug_taken` → pick another slug. +- Skip this step for updates to an existing game — reuse its `gameId`. + +### 6b. Request the presigned upload URL + +Declare exactly what you measured in step 5. `version` must match `[A-Za-z0-9][A-Za-z0-9._-]{0,31}` with no `..`; `sha256` is the lowercase hex digest; `sizeBytes` the exact byte count. + +```bash +curl -sS -X POST "https://summercraft.ai/api/games/$GAME_ID/releases/upload-url" \ + -H "Authorization: Bearer $SUMMERCRAFT_ACCESS_TOKEN" \ + -H "Content-Type: application/json" \ + -d "{\"version\": \"1.0.0\", \"sha256\": \"$SHA256\", \"sizeBytes\": $SIZE_BYTES}" +``` + +- `200 { "uploadUrl", "method": "PUT", "headers": { "content-type": "application/octet-stream", "if-none-match": "*" }, "objectKey", "maxBytes", "expiresAt", "finalizeUrl" }`. +- The URL expires in 1 hour. **Both returned headers are folded into the URL's signature** — the PUT must send them exactly, nothing extra that conflicts, or storage answers `403 SignatureDoesNotMatch`. +- Errors: `404 game_not_found_or_forbidden` (wrong `gameId` or not your game) · `409 not_a_godot_game` (that `gameId` is a web game) · `409 version_exists` (bump `version`; released versions are immutable) · `413 artifact_too_large` · `429 rate_limited`. + +### 6c. PUT the artifact + +```bash +curl -sS -X PUT "$UPLOAD_URL" \ + -H "Content-Type: application/octet-stream" \ + -H "If-None-Match: *" \ + --data-binary @game.pck +``` + +- Success is an empty `200`. No `Authorization` header here — the URL itself is the credential. +- `412 Precondition Failed` → an object already exists at this key (the key is write-once). If your earlier PUT half-landed, finalize will detect and delete a corrupt one; a clean object under the same (version, sha256) means this exact artifact was already uploaded — just finalize. +- `403 SignatureDoesNotMatch` → your headers differ from the two returned ones. + +### 6d. Finalize — the server verifies your bytes + +```bash +curl -sS -X POST "https://summercraft.ai/api/games/$GAME_ID/releases/finalize" \ + -H "Authorization: Bearer $SUMMERCRAFT_ACCESS_TOKEN" \ + -H "Content-Type: application/json" \ + -d "{\"version\": \"1.0.0\", \"sha256\": \"$SHA256\", \"changelog\": \"Initial release.\"}" +``` + +The server HEADs the stored object, streams it through sha256, and compares against your declaration. Nothing you claim is trusted. + +- `201 { "releaseId", "gameId", "version", "sha256", "sizeBytes", "pckUrl", "status": "pending_review", "adminNotified", "detail" }` — you are done; the release is queued for human review. +- `409 no_upload_intent` → you skipped 6b, or the declaration differs from what 6b recorded. +- `409 artifact_missing` → the PUT never landed; redo 6c. +- `400 checksum_mismatch` / `400 size_mismatch` → the stored object was corrupt; it has been deleted so the key is free — redo 6c with the same URL if unexpired, then finalize again. +- `429 rate_limited` → finalize has its own hourly budget; wait for `retryAt`. +- `changelog` is optional, max 2000 chars. + +## 7. After submission — what to tell your operator + +Report exactly this, no more: + +1. The game record exists at status `draft`; release `` is `pending_review` in the manual review queue. A human reviews every submission; there is no automatic publish and no guaranteed review time. +2. When approved, the game becomes `published` and the release goes live; if rejected, the reason appears for the creator on `https://summercraft.ai/creator`. +3. The owner (and admins) can verify the stored artifact any time — response includes a short-lived (5 min) download URL plus the server-verified `sha256`: + ```bash + curl -sS "https://summercraft.ai/api/games/$GAME_ID/releases/1.0.0/download-url" \ + -H "Authorization: Bearer $SUMMERCRAFT_ACCESS_TOKEN" + ``` + Other signed-in users can fetch it only after the game is published. +4. Playing uploaded Godot games in the browser or desktop shell, and hosted game servers, are not live yet — publishing today means entering the catalog pipeline, not instant playability. Do not claim otherwise. + +## 8. Updating the game later + +Releases are immutable. To ship a fix: bump `manifest.json` `version` **and** use the same new version string in 6b/6d, re-export, and repeat 6b → 6c → 6d with the same `gameId`. Every update goes through review again. Remember the 1/hour budgets. + +## 9. If something here does not match reality + +Error bodies from this API are self-describing: `{ "error": "", "detail": "" }` — trust the `detail` text. For anything else, fetch the full reference: `https://docs.summerengine.com/api-reference/summer-sdk/exporting-and-uploading-your-game.md` and `https://docs.summerengine.com/api-reference/summer-sdk/submission-guide.md`. Report doc bugs to `founders@summerengine.com`. diff --git a/api-reference/summer-sdk.mdx b/api-reference/summer-sdk.mdx index 2f17c1a..c1be071 100644 --- a/api-reference/summer-sdk.mdx +++ b/api-reference/summer-sdk.mdx @@ -57,6 +57,9 @@ Templates are examples, not requirements. ## API Sections + + Point any AI coding agent at one prompt: build, export, and publish through the live API + Migrating from Crafty: every renamed class, autoload, manifest key, and path diff --git a/api-reference/summer-sdk/ai-agent-playbook.mdx b/api-reference/summer-sdk/ai-agent-playbook.mdx index 24468f4..620bcd2 100644 --- a/api-reference/summer-sdk/ai-agent-playbook.mdx +++ b/api-reference/summer-sdk/ai-agent-playbook.mdx @@ -15,6 +15,10 @@ Fresh AI sessions must be able to generate code that: This page is the strict contract for that. + + This page is the *output contract* (what generated code must look like). The *end-to-end instruction set* — build, validate, export, and publish through the live API — is the canonical agent prompt at [/agent-setup](/agent-setup), raw at `https://docs.summerengine.com/agent-setup/prompt.md`. Point agents there first. + + ## Platform-First Rule Treat Summercraft as a platform layer: @@ -125,11 +129,17 @@ An AI-generated game is "done" only if all checks pass: 5. Player path chosen explicitly (3D template or custom `SummerPlayer` subclass). 6. No blocked APIs appear in any `.gd` file. 7. Server-authoritative gameplay logic is on server paths. -8. Game can be exported to `.pck` and submitted. +8. Game can be exported to `.pck` and published through the [release API](/api-reference/summer-sdk/exporting-and-uploading-your-game) (release reaches `pending_review`). ## Safe Starter Prompt (for AI systems) -Use this prompt when generating a new Summercraft game: +The canonical, always-current agent prompt — including a verified project template, local validation steps, and the live publish calls — is served raw at: + +```text +https://docs.summerengine.com/agent-setup/prompt.md +``` + +Use [/agent-setup](/agent-setup) to hand it to any agent. If you only need the generation rules inline: ```text Create a minimal multiplayer Summer game with these files only: @@ -139,6 +149,7 @@ Rules: - main.gd must extend SummerGame and implement all lifecycle hooks. - Include at least 4 spawn points in main.tscn. - Keep authoritative gameplay state on server paths only. +- Multiplayer-native defaults: min_players 1, max_players 8. - Include player_scene in manifest.json and make sure it matches player.tscn. - Choose one player model: - 3D template: use SummerCharacter3D in player.tscn diff --git a/api-reference/summer-sdk/build-your-first-summer-game.mdx b/api-reference/summer-sdk/build-your-first-summer-game.mdx index 394d197..c20cf96 100644 --- a/api-reference/summer-sdk/build-your-first-summer-game.mdx +++ b/api-reference/summer-sdk/build-your-first-summer-game.mdx @@ -1,17 +1,24 @@ --- title: "Build Your First Summercraft Game" -description: "Golden path: integrate Summercraft platform APIs into a simple game, then test, export, and submit." +description: "Golden path: build a multiplayer-native game on the Summer SDK, test it locally, export a .pck, and submit it for review." icon: "rocket" --- -## What You Build +## Multiplayer by default -A minimal multiplayer integration that: +Every Summercraft game is multiplayer-native. The default template is host-authoritative: the server runs the rules, clients render synced state, and a `min_players` of 1 means the same build is also a solo game. You do not write netcode — you write `SummerGame` hooks and synced state, and the platform runtime carries them over its host-authoritative transport (ENet today; the `SummerMultiplayerPeer` transport with stock `@rpc`/`MultiplayerSynchronizer` compatibility is a published contract still being built in the engine fork). + +What you build here: - extends `SummerGame`, -- uses server-authoritative gameplay updates, -- syncs player-visible state with `set_synced`, -- passes local testing and submission pipeline. +- server-authoritative gameplay updates (`Summer.is_server()`), +- player-visible state via `set_synced`, +- 1–8 players from the same code path, +- passes local testing and the submission pipeline. + + + Want an AI agent to run this whole page for you? Point it at [/agent-setup](/agent-setup) — the prompt there contains a complete verified template (project files, local SDK stubs, export preset) and the publish calls. + ## Step 1: Create `manifest.json` @@ -28,6 +35,8 @@ A minimal multiplayer integration that: } ``` +`min_players: 1, max_players: 8` is the multiplayer-native default — ship it unless your design demands otherwise. Paths are relative to the manifest at the pack root. + ## Step 2: Choose Your Player Path ### 3D Action Template (fastest start) @@ -40,6 +49,8 @@ A minimal multiplayer integration that: - make your own script extending `SummerPlayer` - sync your own state model (`set_synced("hand_count", ...)`, etc.). +Either way the authority rule is identical: the server writes synced state, clients read it. + ## Step 3: Build `main.gd` ```gdscript @@ -83,9 +94,11 @@ func _process(delta: float) -> void: apply_default_movement(p, delta, MOVE_SPEED, GRAVITY) ``` +Give `main.tscn` a `SpawnPoints` node with at least 4 child `Node3D` spawn markers and a `Players` node — the multiplayer defaults assume them. + ## Step 4: Add One Multiplayer Rule -Example scoring rule: +Example scoring rule — note it runs only on the server path, and clients see it through `set_synced`: ```gdscript func score_point(player) -> void: @@ -103,23 +116,22 @@ Use the two-process test runner flow: - starts client and connects to localhost, - validates real multiplayer behavior. -Guide: - -- [/api-reference/summer-sdk/testing-your-game-locally](/api-reference/summer-sdk/testing-your-game-locally) +Guide: [Testing Your Game Locally](/api-reference/summer-sdk/testing-your-game-locally). Building outside Summer Engine (agent/CLI workflow)? The same page covers stub-based validation, and the [/agent-setup](/agent-setup) prompt automates it. -## Step 6: Export and Upload +## Step 6: Export and Publish -Export a game-only `.pck`, then upload with `manifest.json`. +Export a game-only `.pck`, then publish it through the live release API (create game → presigned upload → server-verified finalize → manual review): -Guides: +- [Exporting and Uploading Your Game](/api-reference/summer-sdk/exporting-and-uploading-your-game) +- [Submission Guide](/api-reference/summer-sdk/submission-guide) -- [/api-reference/summer-sdk/exporting-and-uploading-your-game](/api-reference/summer-sdk/exporting-and-uploading-your-game) -- [/api-reference/summer-sdk/submission-guide](/api-reference/summer-sdk/submission-guide) +A human reviews every release. Approval publishes the game; the release becomes downloadable through the authenticated download endpoint. Browser and desktop-shell play of uploaded Godot games are not live yet. -## What “Done” Looks Like +## What "Done" Looks Like - [ ] All lifecycle hooks implemented. - [ ] Server authority enforced (`Summer.is_server()`). - [ ] Player-visible state comes from `set_synced`. +- [ ] Works with 1 player and with several (`min_players: 1`). - [ ] Local test runner path passes. -- [ ] Upload passes scanner + enters review. +- [ ] Export contains only game files; release enters `pending_review`. diff --git a/api-reference/summer-sdk/exporting-and-uploading-your-game.mdx b/api-reference/summer-sdk/exporting-and-uploading-your-game.mdx index b1a62b4..8a4c74c 100644 --- a/api-reference/summer-sdk/exporting-and-uploading-your-game.mdx +++ b/api-reference/summer-sdk/exporting-and-uploading-your-game.mdx @@ -1,130 +1,207 @@ --- title: "Exporting and Uploading Your Game" -description: "Export a game-only .pck, upload it for review, and understand exactly what validation runs on submission." +description: "Export a game-only .pck, then publish it through the live Summercraft release API: create game, presigned upload, server-verified finalize, manual review." icon: "upload" --- ## Goal -Produce a `.pck` that contains only your game content, then submit it for review. +Produce a `.pck` that contains only your game content, then publish it for review. There are two live publish paths: -The safest flow is: +- **The release API** (this page, recommended — what agents and CI use): four HTTP calls against `https://summercraft.ai`, artifact limit 512 MiB. +- **The browser submit page** at [summercraft.ai/submit](https://summercraft.ai/submit): upload `.pck` + `manifest.json` in the UI, artifact limit 2 GB, runs an automated static scanner at upload time. See [Submission Guide](/api-reference/summer-sdk/submission-guide). -1. Export with the `Summer Game PCK` preset. -2. Upload `.pck` + `manifest.json` at `/submit`. -3. Fix any validation findings. -4. Re-submit with bumped `manifest.version`. +Both end in the same place: a human reviews the release before anything goes live. No auto-publish. -## 1) Configure Export Preset + + Working agent-first? [/agent-setup](/agent-setup) has a paste-prompt that walks any coding agent through this entire page, including a verified project template. + -Use the starter template preset: +## 1) Export a game-only .pck + +Use a preset that ships your game and nothing else: - name: `Summer Game PCK` -- export filter: `resources` -- include filter: `my_game/*,manifest.json` -- exclude filter: infrastructure directories (`sdk/*`, `core/*`, `server/*`, etc.) +- `export_filter="all_resources"` +- `include_filter="manifest.json"` (non-resource files must be listed here) +- `exclude_filter` covering infrastructure and local stubs (`sdk/*`, plus `core/*,server/*,client/*` if you develop inside the Summer Engine starter template) +- `script_export_mode=0` — ship GDScript as readable text so review can read your source -You must update `include_filter` to your actual game folder name. +Export headlessly (no export templates required for a pack export): - - If `include_filter` is wrong, your exported `.pck` can miss required scenes or include unintended files. - +```bash +godot --headless --path . --export-pack "Summer Game PCK" game.pck +``` -## 2) Export The .pck +Checks after export: -In Summer/Godot: +- `manifest.json` is at the pack root and current ([reference](/api-reference/summer-sdk/manifest-json-reference)), +- the entry scene's root script `extends SummerGame`, +- no [reserved paths or banned APIs](/api-reference/summer-sdk/banned-apis-reference) in the pack, +- size between 1024 bytes and 536870912 bytes (512 MiB) for the API path. -1. Open **Project -> Export**. -2. Select `Summer Game PCK`. -3. Verify include/exclude filters. -4. Export to a file like `my_game.pck`. +Then measure what you will declare: -Quick checks after export: +```bash +shasum -a 256 game.pck # 64-char lowercase hex digest (Linux: sha256sum) +wc -c game.pck # exact byte count +``` -- file extension is `.pck`, -- `manifest.json` exists and is current, -- entry/player scenes referenced by manifest are present in the exported game scope. +## 2) Authentication -## 3) Upload On summercraft.ai +Every endpoint accepts two forms, Bearer first: -Go to `/submit` on the Summercraft web app and upload: +- `Authorization: Bearer ` — what agents, CI, and native clients use. See [/agent-setup](/agent-setup#the-one-thing-the-agent-cannot-do-sign-in) for how a signed-in human extracts their token. +- The web session cookie — what the browser uses. -- `.pck` file (required) -- `manifest.json` file (required) +A present-but-invalid Bearer token is a hard `401`; it never falls through to a cookie session. Tokens expire after about an hour. -The page sends a multipart request to: +## 3) Rate limits — read before calling -- `POST https://crafty-production-5a7c.up.railway.app/games/submit` -- `Authorization: Bearer ` +Create-game, upload-url, and finalize each have **their own budget of 1 request per hour per account** (window and count are policy values and may widen). Order of checks is deliberate: validation and ownership run first, so a typo never spends your budget — but a successful call does. A `429` body tells you when to come back: -## 4) What Validation Runs +```json +{ "error": "rate_limited", "detail": "…", "limit": 1, "windowSeconds": 3600, "retryAt": "2026-07-28T12:00:00.000Z" } +``` -Submission validates: +Validate everything locally before touching these endpoints. -- auth and creator identity, -- required manifest fields and basic typing, -- max upload size (2 GB), -- static analysis scanner (dangerous APIs, reserved paths, banned binary/native file types). +## 4) `POST /api/games/engine` — create the game record -If validation fails, the API returns structured error details and scanner violations (file, line, pattern). +Once per game, not per version. Creates a Godot game (`kind: "engine"`) as metadata; the artifact arrives through the release endpoints. -## 5) Review Lifecycle +Request: -On successful upload: +```json +{ "name": "Hold the Hill", "slug": "hold-the-hill" } +``` -- submission status becomes `review`, -- admins review the build, -- game is either: - - `published`, or - - `rejected` (with a reason). +- `name` required, 1–80 chars. `slug` optional: explicit slugs collide loudly (`409 slug_taken`); omitted, the server derives one from the name and walks around collisions. -## 6) Updating Your Game +Success `201`: -For updates: +```json +{ + "gameId": "9f0c1e6a-…", + "slug": "hold-the-hill", + "kind": "engine", + "status": "draft", + "uploadUrlEndpoint": "/api/games/9f0c1e6a-…/releases/upload-url" +} +``` -1. Keep `manifest.id` stable. -2. Bump `manifest.version`. -3. Re-export `.pck`. -4. Re-upload `.pck` + `manifest.json`. +(`uploadUrlEndpoint` is included when the slug was derived.) -Each update goes through review again. +Errors: `401 sign_in_required` · `400 invalid_request` · `409 slug_taken` · `429 rate_limited` · `502 create_failed`. + +## 5) `POST /api/games/{gameId}/releases/upload-url` — mint the presigned PUT + +Declare exactly what you measured: + +```json +{ "version": "1.0.0", "sha256": "<64-char lowercase hex>", "sizeBytes": 10536 } +``` + +- `version`: 1–32 chars of `[A-Za-z0-9._-]`, first char alphanumeric, no `..`. Immutable once released. +- `sha256`: lowercase hex, no `sha256:` prefix. +- `sizeBytes`: positive integer, 1024–536870912. +- `contentType` optional; if sent it must be `"application/octet-stream"`. + +Success `200`: + +```json +{ + "uploadUrl": "https://…r2.cloudflarestorage.com/…", + "method": "PUT", + "headers": { "content-type": "application/octet-stream", "if-none-match": "*" }, + "objectKey": "releases///.pck", + "maxBytes": 536870912, + "expiresAt": "…", + "finalizeUrl": "/api/games//releases/finalize" +} +``` + +- The URL expires after 1 hour. +- **Both returned headers are folded into the URL's signature.** Send them verbatim on the PUT or storage answers `403 SignatureDoesNotMatch`. +- `if-none-match: *` makes the object key **write-once**: a release artifact can never be swapped after it has been checksummed. A second PUT to the same key answers `412 Precondition Failed`. + +Errors: `401 sign_in_required` · `400 invalid_request` · `413 artifact_too_large` · `404 game_not_found_or_forbidden` (wrong id or not yours) · `409 not_a_godot_game` (web games publish via their own flow) · `409 version_exists` (bump the version) · `429 rate_limited` · `503 r2_not_configured` · `502 upload_url_failed`. + +## 6) PUT the artifact to `uploadUrl` + +```bash +curl -sS -X PUT "$UPLOAD_URL" \ + -H "Content-Type: application/octet-stream" \ + -H "If-None-Match: *" \ + --data-binary @game.pck +``` -## Common Upload Errors +Success is an empty `200`. No `Authorization` header — the presigned URL is the credential. `412` means the key already holds an object (see write-once above); if that object is the same verified artifact, skip to finalize. -### `Uploaded file must be a .pck` +## 7) `POST /api/games/{gameId}/releases/finalize` — server-verified record -- Wrong file type. -- Renamed file without real `.pck` export. +```json +{ "version": "1.0.0", "sha256": "", "changelog": "Initial release." } +``` -### `PCK file too large (... max 2GB)` +Nothing you claim is trusted: the server checks its own presign-time intent record, HEADs the stored object, streams it through sha256, and compares digest and size against the declaration. Only then does a release row exist — and it is immutable from that moment. -- Reduce textures/audio size. -- Remove non-game assets from export filter. -- For builds that still exceed 2 GB, contact [founders@summerengine.com](mailto:founders@summerengine.com) for large-upload onboarding. +Success `201`: -### `Invalid manifest. Required: ...` +```json +{ + "releaseId": "…", + "gameId": "…", + "version": "1.0.0", + "sha256": "…", + "sizeBytes": 10536, + "pckUrl": "r2:///releases///.pck", + "status": "pending_review", + "adminNotified": true, + "detail": "Release recorded and queued for review. It goes live when an admin approves it." +} +``` -- Missing required keys. -- Wrong types. -- Invalid JSON syntax. +The release is **not live**: it serves only after an admin approves it and the game's active version moves. On a `checksum_mismatch` or `size_mismatch` the stored object is deleted (so the write-once key is free again for a correct re-upload) and no row is written. -### `Static analysis failed` +Errors: `401 sign_in_required` · `400 invalid_request` · `404 game_not_found_or_forbidden` · `409 not_a_godot_game` · `409 no_upload_intent` (call upload-url first; declarations must match it exactly) · `409 artifact_missing` (the PUT never landed) · `413 artifact_too_large` · `400 size_mismatch` · `400 checksum_mismatch` · `409 version_exists` · `429 rate_limited` (its own budget) · `503 r2_not_configured` · `502 finalize_failed`. -- Scanner found banned APIs, reserved paths, or blocked resource types. -- Open violation list and fix each issue before re-uploading. +Errors are self-describing — `{ "error", "detail" }` where `detail` names the next action. Trust it. -## Recommended Release Checklist +## 8) `GET /api/games/{gameId}/releases/{version}/download-url` — verify what you shipped -- [ ] `manifest.json` required fields valid. -- [ ] Creator gameplay scripts authored in `GDScript`. -- [ ] `entry_scene` and `player_scene` paths load. -- [ ] Export includes only game files. -- [ ] `.pck` at or under 2 GB, or large-upload onboarding confirmed. -- [ ] No banned APIs or reserved paths. -- [ ] Local test runner flow passes before upload. +Auth required, never anonymous. The game's owner and admins can fetch any release, including pending ones; any other signed-in user only the approved release of a published game. Everything unauthorized answers `404` (not `403`), so release existence cannot be probed. + +Success `200`: + +```json +{ "url": "…", "sha256": "…", "sizeBytes": 10536, "version": "1.0.0", "releaseId": "…", "isActive": false, "expiresAt": "…" } +``` + +`url` is a presigned GET valid for ~5 minutes; `sha256` is read back from the content-addressed key so you can re-verify the bytes after download. + +## 9) Review lifecycle + +1. Finalize → release `pending_review`; the admin queue is pinged. +2. A human reviews. Approval re-verifies the stored bytes against the checksum pinned at finalize, then points the game's active version at the release and publishes the game. Rejection records a reason, shown to you on [summercraft.ai/creator](https://summercraft.ai/creator); a live game stays live on its previously approved release. +3. There is no automated publish, no runtime sandbox yet, and no guaranteed review time. + +**What approval does not (yet) mean:** browser play, desktop shell play, and hosted game servers for uploaded Godot games are not live. Approved releases are distributed through the authenticated download endpoint; play surfaces are in development. + +## 10) Updating your game + +Releases are immutable — updating means a new one: + +1. Keep the same `gameId` (and `manifest.id`). +2. Bump `manifest.version`, and use that same new string as `version` in upload-url and finalize. +3. Re-export, then repeat steps 5–7. + +Each update goes through review again. ## Related Docs +- [Agent Setup](/agent-setup) — the paste-prompt that automates this page +- [Submission Guide](/api-reference/summer-sdk/submission-guide) — review lifecycle and the browser flow - [Testing Your Game Locally](/api-reference/summer-sdk/testing-your-game-locally) - [manifest.json Reference](/api-reference/summer-sdk/manifest-json-reference) - [Banned APIs Reference](/api-reference/summer-sdk/banned-apis-reference) diff --git a/api-reference/summer-sdk/manifest-json-reference.mdx b/api-reference/summer-sdk/manifest-json-reference.mdx index 0cc03fd..fb48048 100644 --- a/api-reference/summer-sdk/manifest-json-reference.mdx +++ b/api-reference/summer-sdk/manifest-json-reference.mdx @@ -6,9 +6,12 @@ icon: "file-json" ## Overview -Every Summercraft game must include a `manifest.json` file in the game pack. +Every Summercraft game must include a `manifest.json` file at the root of the game pack. -The submission API validates required keys and types before a game can enter review. +The browser submission flow validates required keys and types at upload time; on the API release path the same requirements are enforced by the runtime and by human review. Two runtime rules worth knowing: + +- `entry_scene` and `player_scene` are resolved **relative to the manifest's location** in the pack (manifest at the root means `"main.tscn"` → `res://main.tscn`). +- The entry scene's root script must `extends SummerGame`, or the runtime refuses to load the pack. ## Required Fields @@ -87,6 +90,6 @@ When you submit a new `.pck`: - Keep the same `id` for the same game - Increase `version` -- Re-submit through the submission endpoint +- Re-submit through the [publish flow](/api-reference/summer-sdk/exporting-and-uploading-your-game) -The platform stores versions and routes review/publishing from your newest submission. +On the API release path, use the manifest's `version` string as the release `version` too — it must match `[A-Za-z0-9][A-Za-z0-9._-]{0,31}` (no `..`), and released versions are immutable: an update is always a new version. The platform stores versions and routes review/publishing from your newest submission. diff --git a/api-reference/summer-sdk/submission-guide.mdx b/api-reference/summer-sdk/submission-guide.mdx index 1f3bce8..6b6ee40 100644 --- a/api-reference/summer-sdk/submission-guide.mdx +++ b/api-reference/summer-sdk/submission-guide.mdx @@ -1,122 +1,94 @@ --- title: "Submission Guide" -description: "How to export .pck, pass static analysis, upload to summercraft.ai, and understand review/update flow." +description: "The two live ways to submit a Summercraft game, what validation runs where, and how the manual review lifecycle works." icon: "upload" --- -## Submission Checklist +## Before you submit -Before uploading: +- Creator gameplay scripts authored in GDScript +- Valid [`manifest.json`](/api-reference/summer-sdk/manifest-json-reference) at the pack root +- Entry scene root `extends SummerGame` +- No [banned APIs or reserved paths](/api-reference/summer-sdk/banned-apis-reference) +- [Local testing](/api-reference/summer-sdk/testing-your-game-locally) passes -- Creator gameplay scripts authored in `GDScript` -- Valid `manifest.json` -- One exportable `.pck` package + `manifest.json` -- `.pck` package size at or under **2 GB** -- No banned API usage in GDScript +## The two live submission paths -If your package exceeds 2 GB, contact [founders@summerengine.com](mailto:founders@summerengine.com) for large-upload onboarding. +### Path A — the release API (recommended; agents and CI) -## 1) Export `.pck` from Godot / Summer Engine +Four JSON/HTTP calls against `https://summercraft.ai`: create game → presigned upload URL → PUT the `.pck` → finalize with server-side sha256 verification. Artifact limits: 1024 bytes – 512 MiB. Each write step is limited to 1 request per hour per account. -Use standard export flow: +Full request/response reference: [Exporting and Uploading Your Game](/api-reference/summer-sdk/exporting-and-uploading-your-game). Agent bootstrap: [/agent-setup](/agent-setup). -1. Open your game project. -2. Go to **Project -> Export**. -3. Choose/create export preset for your build target. -4. Export and generate a `.pck`. -5. Confirm your game includes `manifest.json`. +### Path B — the browser submit page -## 2) Static Analysis Scanner Rules (Blocking) +Go to [summercraft.ai/submit](https://summercraft.ai/submit) signed in, and upload two files: -During submission, the platform scans: +- the `.pck` (multipart field `pck`) +- `manifest.json` (multipart field `manifest`, file or JSON text) -- GDScript files (`.gd`) -- embedded scripts in `.tscn` and `.tres` -- reserved infrastructure paths -- blocked native/binary resource extensions - -If scanner violations are found, submission fails with: - -- `error: "Static analysis failed"` -- `violations`: file, line, and blocked pattern - -See the complete up-to-date list here: - -- [Banned APIs Reference](/api-reference/summer-sdk/banned-apis-reference) - -### Important For AI Code Generation - -Do not generate "fallback" code paths that use blocked APIs, even if wrapped in conditions. - -Example of **still invalid** output: - -```gdscript -if OS.has_feature("editor"): - var f = FileAccess.open("user://debug.txt", FileAccess.WRITE) -``` - -The scanner is pattern-based and blocks usage regardless of runtime branch. - -## 3) Upload on summercraft.ai - -V1 submission requires two files: - -- `.pck` as multipart field `pck` -- `manifest.json` as multipart field `manifest` (file or JSON text) - -Authenticated request: - -- `POST https://crafty-production-5a7c.up.railway.app/games/submit` -- `Authorization: Bearer ` - -### Example Request (cURL) +The page posts a multipart request to the platform API: ```bash curl -X POST "https://crafty-production-5a7c.up.railway.app/games/submit" \ -H "Authorization: Bearer " \ - -F "pck=@./build/my-game.pck" \ + -F "pck=@./game.pck" \ -F "manifest=@./manifest.json;type=application/json" ``` -## 4) What Happens During Review +(The Railway hostname is the deployed platform-api identity — it is correct as written.) -On success, API returns: +This path validates at upload time: -- `ok: true` -- `gameId` -- `submissionId` -- `status: "review"` -- `scan` metadata +- auth and creator identity, +- required manifest fields and basic typing, +- max upload size (2 GB on this path), +- **automated static analysis** of `.gd` files and scripts embedded in `.tscn`/`.tres`: banned APIs, reserved paths, blocked binary/native extensions. Failures return `error: "Static analysis failed"` with a `violations` list (file, line, pattern). -Game status lifecycle: +On success it returns `ok: true`, `gameId`, `submissionId`, `status: "review"`. -- `review` -> waiting for manual/admin review -- `published` -> approved and live -- `rejected` -> rejected with reason + + The static scanner is pattern-based. A banned API inside a dead branch, an editor-only check, or a comment-adjacent string still fails. Do not generate "fallback" code paths that use blocked APIs. On Path A there is no automated scanner at upload time — the same rules are enforced by human review instead, so a violation costs you a review round-trip rather than an instant error. + -## 5) Updating an Existing Game +## Review lifecycle — both paths -To update: +Every submission is reviewed by a human. No auto-publish. -1. Keep `manifest.id` the same. -2. Increase `manifest.version`. -3. Upload new `.pck` + `manifest`. +| State | Meaning | +|---|---| +| `draft` | Game record exists, nothing submitted or approved yet | +| `pending_review` / `review` | A release/submission is in the admin queue | +| `published` | Approved — the game is live on the platform with that release active | +| `rejected` | Rejected with a written reason, shown to you on [summercraft.ai/creator](https://summercraft.ai/creator) | -The platform: +On Path A, approval re-verifies the stored artifact against the checksum pinned at finalize before the game's active version moves — verification at upload is not trusted at approval time. A rejected release never takes down a live game: the previously approved release stays active. -- updates the game metadata for same creator + slug -- stores a new version row -- sets status back to `review` for that update +**Honest status:** approval publishes your game on the platform and makes its release downloadable through the authenticated [download endpoint](/api-reference/summer-sdk/exporting-and-uploading-your-game). Playing uploaded Godot games in the browser or the desktop shell, and hosted dedicated game servers, are not live yet — do not tell players otherwise. -## Related Docs +## Updating an existing game -- [Testing Your Game Locally](/api-reference/summer-sdk/testing-your-game-locally) -- [Exporting and Uploading Your Game](/api-reference/summer-sdk/exporting-and-uploading-your-game) -- [Banned APIs Reference](/api-reference/summer-sdk/banned-apis-reference) +Released versions are immutable. + +1. Keep `manifest.id` (and on Path A the `gameId`) stable. +2. Bump `manifest.version`; on Path A pass the same new version string to upload-url and finalize. +3. Re-export and re-submit. + +Each update goes through review again. A live game stays live while its update waits. -## Common Upload Errors +## Common submission errors + +Path A errors are self-describing JSON (`{ "error", "detail" }`) — the `detail` names the fix. The full table is in [Exporting and Uploading](/api-reference/summer-sdk/exporting-and-uploading-your-game). Path B errors: - `Missing .pck file (multipart field: pck)` - `Uploaded file must be a .pck` +- `PCK file too large (... max 2GB)` — trim assets, or contact [founders@summerengine.com](mailto:founders@summerengine.com) for large-upload onboarding - `Invalid manifest. Required: id, name, version, summer_sdk, entry_scene, min_players, max_players` - `Static analysis failed` (with violations list) + +## Related Docs + +- [Exporting and Uploading Your Game](/api-reference/summer-sdk/exporting-and-uploading-your-game) +- [Testing Your Game Locally](/api-reference/summer-sdk/testing-your-game-locally) +- [Banned APIs Reference](/api-reference/summer-sdk/banned-apis-reference) +- [Agent Setup](/agent-setup) diff --git a/api-reference/summer-sdk/testing-your-game-locally.mdx b/api-reference/summer-sdk/testing-your-game-locally.mdx index 3ea8acb..902f3d2 100644 --- a/api-reference/summer-sdk/testing-your-game-locally.mdx +++ b/api-reference/summer-sdk/testing-your-game-locally.mdx @@ -30,6 +30,17 @@ This gives you a real multiplayer loop in one click. If it works in this local test-runner flow, it is much closer to how it will behave in production than single-process editor-only testing. +## Building Outside Summer Engine (Agent / CLI Workflow) + +The test runner above ships with Summer Engine's Summercraft template. If you are building standalone — a coding agent, CI, or a bare Godot 4.5 install — the real SDK is not on disk, so full runtime testing is not available locally. What works instead, verified with Godot 4.5.1: + +1. Keep local **SDK stubs** in `sdk/` (excluded from export) that mirror the documented class surfaces, so every script parses. The [/agent-setup](/agent-setup) prompt contains ready-made stubs. +2. **Headless smoke run:** `godot --headless --path . --quit-after 120` must exit cleanly with no `SCRIPT ERROR` lines. A parse error here is the same error that will make the platform runtime refuse your pack. +3. **Banned-API self-check:** grep your game scripts (not the stubs) against the [blocked pattern list](/api-reference/summer-sdk/banned-apis-reference) before every upload — the API path has no upload-time scanner, so a violation costs you a manual review round-trip. +4. **Pack-content check:** export, then confirm the `.pck` stores only your game files plus `manifest.json` (the export log lists every stored file). + +This validates structure, parsing, and packaging — not gameplay against the real SDK. The review process runs your game on the real runtime; keep gameplay logic simple enough to survive the difference, and treat the smoke run as the floor, not the ceiling. + ## What Happens Under The Hood When you run the test runner: diff --git a/docs.json b/docs.json index 31c3d1e..d0f5c3d 100644 --- a/docs.json +++ b/docs.json @@ -186,6 +186,13 @@ "tab": "Summer SDK", "icon": "code", "groups": [ + { + "group": "Agent Setup", + "pages": [ + "agent-setup", + "agent-setup/prompt" + ] + }, { "group": "Creating Games on Summercraft", "pages": [ diff --git a/index.mdx b/index.mdx index d04e017..68e7f0b 100644 --- a/index.mdx +++ b/index.mdx @@ -19,6 +19,9 @@ Instead of bouncing between ChatGPT, VS Code, and your game engine, you work in Get started in 5 minutes. Pick your path: + + Point Claude Code, Cursor, Codex, or any agent at one prompt: it builds a multiplayer Godot game and submits it to Summercraft for review + Use Summer Agent in Summer Engine: build a complete game with full AI assistance, no coding required