diff --git a/README.md b/README.md index b1ee97c..09087f4 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,40 @@ npx @gamemaker/gm-cli@latest login gm-cli manual open "sprites" --language=es ``` +### Publish to GX.Games +Use `gm-cli gxgames` to publish a game to [gx.games](https://gx.games). Run `gm-cli gxgames --help` for the full list of subcommands. + +The flow: + +1. **Package** the game as an OperaGX zip: + ```sh + npx @gamemaker/gm-cli@latest package --target=operagx --output=game.zip + ``` +2. **Link** the project to a studio and game (opens the browser to sign in on first use): + ```sh + npx @gamemaker/gm-cli@latest gxgames link --studioid= --gameid= + ``` + Omit the flags to pick interactively. +3. **Upload** the zip (prompts for a `X.Y.Z.B` version if `--version` is omitted): + ```sh + npx @gamemaker/gm-cli@latest gxgames upload --file=game.zip --version=1.0.0.0 + ``` +4. **Set metadata** required for publishing — title (must be unique across GX.Games), description, age rating, platforms, 16:9 cover, and 16:9 screenshot. Cover and graphic must have an **exact** 16:9 aspect ratio (e.g. 1920×1080): + ```sh + npx @gamemaker/gm-cli@latest gxgames meta \ + --title="My Game" \ + --description="A short description" \ + --age-rating=EVERYONE \ + --platforms=DESKTOP,MOBILE \ + --cover=cover_1920x1080.png \ + --graphic=screenshot_1920x1080.png + ``` + Each flag overrides one field; omitted flags keep the current server value. +5. **Publish** to make the game public (opens the published page on success): + ```sh + npx @gamemaker/gm-cli@latest gxgames publish + ``` + ## Near future > Note: The GameMaker CLI is made for the community and we welcome your feedback! Please suggest features or tell us about any issues you are having. @@ -81,10 +115,6 @@ The `ask` subcommand displays an article directly in your terminal, gm-cli manual ask "data structures" ``` -### Publishing games to the web - -Direct integration with [gx.games](https://gx.games) to share your games! - ### A library to make your own tools `gm-cli` will expose its internals as a TypeScript library that you can use to more easily make your own editor tools. diff --git a/src/commands/gxgames/command.ts b/src/commands/gxgames/command.ts index 1879a5f..1099f1a 100644 --- a/src/commands/gxgames/command.ts +++ b/src/commands/gxgames/command.ts @@ -70,6 +70,7 @@ const gxgamesUploadCommand = buildCommand({ brief: "Upload a game bundle to GX.Games", fullDescription: "Uploads a zip bundle to GX.Games. " + + "Produce the zip with `gm-cli package --target operagx`. " + "Prompts for the version number if --version is not provided.", }, }); @@ -85,7 +86,7 @@ const gxgamesMetaCommand = buildCommand({ brief: "Game title", optional: true, }, - agerating: { + ageRating: { kind: "parsed", parse: String, brief: @@ -124,8 +125,7 @@ const gxgamesMetaCommand = buildCommand({ brief: "Set or update game metadata required for publishing", fullDescription: "Set or update metadata for your GX.Games game. " + - "Run without flags for an interactive prompt, or pass flags to set specific fields. " + - "All flags are optional — omit any flag to be prompted for it interactively.", + "Each flag overrides the corresponding field; omitted flags keep their current server value.", }, }); diff --git a/src/commands/gxgames/commands/meta-impl.ts b/src/commands/gxgames/commands/meta-impl.ts index f769eae..7f58dd5 100644 --- a/src/commands/gxgames/commands/meta-impl.ts +++ b/src/commands/gxgames/commands/meta-impl.ts @@ -29,7 +29,7 @@ import type { interface MetaFlags { title?: string; - agerating?: string; + ageRating?: string; description?: string; platforms?: string; cover?: string; @@ -59,7 +59,7 @@ export default async function ( title: flags.title ?? game.title, shortDescription: flags.description ?? game.shortDescription, ageRating: - (flags.agerating?.toUpperCase() as + (flags.ageRating?.toUpperCase() as | GameDevUpdateGameRequestAgeRatingEnum | undefined) ?? (game.ageRating as GameDevUpdateGameRequestAgeRatingEnum), diff --git a/src/commands/gxgames/commands/upload-impl.ts b/src/commands/gxgames/commands/upload-impl.ts index f076959..685b30a 100644 --- a/src/commands/gxgames/commands/upload-impl.ts +++ b/src/commands/gxgames/commands/upload-impl.ts @@ -24,6 +24,12 @@ import type { ProjectPath } from "~/project"; import { LinkStorage } from "../api"; +const VERSION_RE = /^\d+\.\d+\.\d+\.\d+$/; +const validateVersion = (v: string | undefined): string | undefined => + v && VERSION_RE.test(v) + ? undefined + : "Version must be X.Y.Z.B (e.g. 1.0.0.0)"; + export default async function ( this: Context, flags: { file: string; version?: string }, @@ -49,12 +55,17 @@ export default async function ( let version: string; if (flags.version != null) { + const err = validateVersion(flags.version); + if (err) { + throw new KnownError(err); + } version = flags.version; } else { const v = await p.text({ message: "Version", placeholder: "0.0.1.0", defaultValue: previousVersion, + validate: validateVersion, }); if (p.isCancel(v)) { return process.exit(0);