gplay games (Play Games Services configuration)
gplay games configures a game's Play Games Services resources,
achievement and leaderboard configurations, each with the full CRUD set
(list / view / create / update / delete). It is an Admin API surface:
the developer configures the game. It is not the Play Games Services
runtime the game itself calls (sign-in, score submission); that is out of
scope. Shared conventions (auth, output, exit codes, --dry-run/--confirm)
are in gplay-cli-usage. The whole namespace is [experimental].
Addressing: --application-id, not --package
This is the trap. Play Games resources are keyed by the numeric Play Games
application ID (a distinct ID space), not the Android package name.
--application-id <numeric-id> is required on list and create; the
per-resource commands view / update / delete are addressed by the
achievement/leaderboard id alone (no --application-id). The
.gplay/config.json package pin does not apply anywhere here.
gplay games achievements list --application-id 1234567890
gplay games leaderboards list --application-id 1234567890
Draft-only: there is no publish
The single most important gotcha: every write here edits the draft
configuration. The published copy players see is read-only, and the API
exposes no publish method; pushing a draft live to players is Play
Console-only. So gplay games … create/update stages the change; a human
finishes it in the Console. Don't expect a CLI command to make it live.
Achievements
gplay games achievements view <achievementId> --output json # read one
gplay games achievements create --application-id 123 \
--name "First Blood" --description "Win your first match" \
--type STANDARD --initial-state REVEALED --point-value 10
- Field flags:
--name/--description(localized, under--locale, defaulten-US),--type STANDARD|INCREMENTAL,--initial-state HIDDEN|REVEALED,--point-value, and--steps-to-unlock(INCREMENTAL only). - Or
--from-json <file|->for a fullAchievementConfigurationbody, the round-trip ofview --output json, and the way to set multiple locales at once.--from-jsonand the field flags are mutually exclusive.
Leaderboards
gplay games leaderboards view <leaderboardId> --output json # read one
gplay games leaderboards create --application-id 123 \
--name "High Scores" --score-order LARGER_IS_BETTER \
--score-min 0 --score-max 1000000
gplay games leaderboards update <leaderboardId> --name "Top Scores"
- Field flags:
--name(localized,--locale),--score-order LARGER_IS_BETTER|SMALLER_IS_BETTER,--score-min/--score-max. - Or
--from-json <file|->for a fullLeaderboardConfiguration, the way to setscoreFormator multiple locales. Mutually exclusive with the field flags.
update replaces: fetch, edit, resend
update <id> is a full PUT replace: the body replaces the draft. For a
partial edit, read the current config, edit it, and resend it whole:
gplay games achievements view <id> --output json > ach.json
# edit ach.json …
gplay games achievements update <id> --from-json ach.json
The field flags on update send only what they name, so --name alone is fine
for a one-field change; reach for the fetch-edit-resend round-trip when you need
to preserve a rich body (multiple locales, nested fields).
delete is irreversible
gplay games achievements delete <achievementId> --dry-run
gplay games achievements delete <achievementId> --confirm
gplay games leaderboards delete <leaderboardId> --confirm
Deleting a draft configuration cannot be undone, so both delete commands sit
on the --confirm tier: missing the flag exits 3 naming it, --dry-run
rehearses with no HTTP call, and GPLAY_READONLY=1 refuses it (exit 4).
Safety
create/updateare routine draft writes; no--confirm. Rehearse with--dry-run(no HTTP;--output jsonprints the request body).delete <id>is irreversible → requires--confirm(missing → exit3);CI=truenever auto-confirms.GPLAY_READONLYrefuses every live write (exit4);--dry-runis exempt.--output jsonmirrors the API response verbatim (ADR-0003);listpaging is--max-results+--page-token(readnextPageTokenfrom the response).