Skip to main content
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 <access token> (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:
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:
manifest.json:
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:
main.tscn — entry scene: floor, hill marker, four spawn points, light, camera:
player.tscn — references the runtime’s 3D character template script. The reference is expected; the file itself must never be in your pack:
The four stub files. sdk/summer.gd:
sdk/summer_game.gd:
sdk/summer_player.gd:
sdk/summer_character_3d.gd:
export_presets.cfg — this is what keeps stubs and project config out of the pack:
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.
Notes on the checks: the pass condition for 5c is empty outputgrep 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:

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)

  • 201 { "gameId": "<uuid>", "slug": "hold-the-hill", "kind": "engine", "status": "draft", "uploadUrlEndpoint": "/api/games/<uuid>/releases/upload-url" }. Save it: GAME_ID=<gameId from the response>.
  • 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.
  • 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

  • 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

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 <version> 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:
    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": "<code>", "detail": "<what to do next>" } — 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.