Reference

Platforms

What Google Play Games, Game Center and Steam each support, how the adapters map onto them, and how to submit the generated files.

Summary

Google Play GamesGame CenterSteam
AdaptergameServicesAchievements(plugin, 'play')gameServicesAchievements(plugin, 'game-center')steamAchievements(client)
Unlockidempotentidempotentidempotent
Progressadd whole stepsadd percentage pointsset a stat to the absolute value
Progress needs a durable storeyesyesno
Addressed bythe console's generated idyour permanent idACH_<KEY> and STAT_<KEY>
Signed inthe plugin's isSignedInthe plugin's isSignedIna client exists
Bulk importCSV and icons in a flat ZIPJSON records and iconsnone

Google Play Games

The adapter calls isSignedIn, unlockAchievement, incrementAchievement and showAchievements. The plugin's progress call only adds, so the adapter sends steps − acknowledged. That is why progress is held back unless the store is durable.

Play ids are generated by the Play Console after you import the files, so a freshly resolved list has playId: null and the adapter reports those achievements as unaddressed rather than failing. After the import, download the console's resources file and feed it back:

import { playIdsFromResources } from 'platform-achievements/export'

const playIds = playIdsFromResources(resourcesXml, achievements) // { 'first-win': 'CgkI…' }
const withIds = resolveAchievements(definitions, { playIds })

Keep playIds in a JSON file in your repository.

Import file: submission().files holds play/AchievementsMetadata.csv, play/AchievementsLocalizations.csv and play/AchievementsIconsMappings.csv, all headerless, and the icons are 512 px PNGs. The console wants everything in one flat ZIP: playConsoleZip takes the files and the drawn icons by their play/… paths and zips them without directories.

Game Center

Same plugin, same four methods, but incrementAchievement counts percentage points here, so the adapter converts steps to percent of the achievement's total before taking the difference.

Game Center ids are permanent and chosen by you. gameCenterNamespace plus the key gives com.example.game.first_win; letters, digits, periods and underscores only, at most 100 characters. Release is one-way: an achievement cannot be deleted once it ships, and an app is limited to 100 achievements and 1,000 points in total, which is why validateAchievements checks both.

Import file: app-store-connect/app-store-connect.json, one record per achievement in the shape of the App Store Connect API's Game Center achievement and localization attributes, with 1024 px PNGs.

Steam

Steam's progress is absolute. An incremental achievement has a progress stat (STAT_<KEY>); the adapter sets it to the steps reached, and Steam shows the progress bar. Unlocking sets the stat to its maximum, activates the achievement and stores. Every change is stored (stats.store) before it counts as acknowledged. Pass null when Steam is not running and the adapter reports signed out.

Steam has no bulk import. steamworks/steamworks.json lists the achievements and stats in the shape the App Admin page asks for, and the 256 px JPEG icons come in earned and locked pairs.

The web

webAchievements is never signed in and sends nothing. The player's achievements stay outstanding for whichever platform they sign in to later, which is the point of deriving rather than queueing.

Localisation

The definitions carry one language's text. For another locale, call submission again with the translated definitions and the locale: submission(translated, { locale: 'fr-FR' }).