The game center bridge gives the page the platform’s game service: Apple Game Center on iOS, Mac Catalyst and
macOS, and Google Play Games Services v2 on Android. It’s built on Shiny.GameCenter . Unlocks,
progress and scores are recorded on the device first and sent once the player is signed in and online, so a page can
record them offline without retrying.
Platform
Backed by
iOS, Mac Catalyst, macOS
GameKit
Android
Play Games Services v2
Windows, Linux
nothing: every route answers 501
dotnet add package Shiny.AppDeviceBridge.GameCenter
builder. UseAppDeviceBridge (
bridge => bridge. AddGameCenterBridge ( o => o
. AddAchievement ( "first-win" , appleId : "com.acme.firstwin" , googleId : "CgkI…" )
. AddAchievement ( "ten-wins" , appleId : "com.acme.tenwins" , googleId : "CgkI…" , totalSteps : 10 )
. AddLeaderboard ( "high-score" , appleId : "com.acme.highscore" , googleId : "CgkI…" )
The page names achievements and leaderboards by these keys, so it never carries App Store Connect or Play Console ids.
A key that isn’t registered goes to the service as a platform id. Incremental achievements must be registered with
the step count set in the Play Console, because Apple counts progress as a percentage and the device converts it. An app
that already calls services.AddGameCenter(…) keeps its registration, and the options passed to the bridge are not used.
Platform setup is Shiny.GameCenter’s: the Game Center entitlement and capability on Apple, and the Play Games
APP_ID and signing-key SHA-1s on Android. See Shiny.GameCenter .
import { GameCenterBridge } from "@shinyorg/appdevicebridge" ;
const games = new GameCenterBridge ();
const { player } = await games. signIn (); // null when the player declines
await games. unlock ( "first-win" );
await games. setProgress ( "ten-wins" , { steps: 4 }); // absolute, never a delta
await games. submitScore ( "high-score" , { score: 1200 });
const top = await games. getScores ( "high-score" , { scope: "Friends" , timeScope: "Week" , maxResults: 10 });
await games. showLeaderboard ({ leaderboardKey: "high-score" });
In Blazor, register AddGameCenterBridgeClient() and inject IGameCenterBridge.
unlock, progress, increment, reveal and scores never fail because the player is offline or signed out. They
are stored on the device, kept across restarts, and sent after sign-in or when connectivity returns. Progress is an
absolute step count, so a retry never counts twice, and progress never goes backwards. GET games reports how many
items are still waiting in pendingCount; POST games/flush sends them now and answers the new status.
Reading — achievements, scores and friends — needs a signed-in player and answers 401 not_authenticated otherwise.
Shiny signs the player in silently at launch. POST games/signin shows the service’s sign-in UI when it’s needed and
answers { player }, which is null when the player declines. On Apple, after repeated cancels Game Center stops
offering its sheet, and the player has to sign in from Settings. games.player fires whenever the player signs in,
signs out or switches accounts.
GET games/friends asks for consent the first time. Apple shows a one-time prompt, and needs
NSGKFriendListUsageDescription in Info.plist. Google shows a dialog and answers ConsentRequested: ask again once the
player is back in the app.
Status
Code
When
401
not_authenticated
No player is signed in, for a read
404
invalid_id
The service doesn’t know the achievement or leaderboard
409
no_user_interface
No window to show the service’s UI in
503
network, unavailable, not_configured
The service can’t be reached, isn’t on the device, or the app isn’t set up for it
502
game_service_failed
Anything else
GET /_bridge/ gamesThe service on this device, the signed-in player, and how much is waiting to be sent. POST /_bridge/ games/signinSigns the player in, showing the service's sign-in UI when it is needed. GET /_bridge/ games/achievementsEvery achievement, merged with progress not sent yet. POST /_bridge/ games/achievements/{key} /unlockCompletes an achievement. POST /_bridge/ games/achievements/{key} /progressSets an incremental achievement to an absolute step count. POST /_bridge/ games/achievements/{key} /incrementAdds steps to an incremental achievement, counted from the furthest progress this device knows of. POST /_bridge/ games/achievements/{key} /revealMakes a hidden achievement visible. POST /_bridge/ games/achievements/showOpens the service's achievements UI. POST /_bridge/ games/leaderboards/{key} /scoresSubmits a score. POST /_bridge/ games/leaderboards/{key} /queryThe top scores of a leaderboard, for everyone or for the player's friends. POST /_bridge/ games/leaderboards/showOpens one leaderboard, or all of them. GET /_bridge/ games/friendsThe player's friends who can see this game, asking for consent the first time. POST /_bridge/ games/flushSends what is queued now and answers with what is left. The service on this device, the signed-in player, and how much is waiting to be sent.
C# IGameCenterBridge.GetStatusAsyncTS GameCenterBridge.getStatus
Response 200 GameCenterStatus GameCenterStatus · 3 fields platformGameServicePlatform playerGamePlayer | null The signed-in player, or null. pendingCountnumber Unlocks, progress and scores recorded on the device that the service has not accepted yet.
POST /_bridge/ games/signin# Signs the player in, showing the service's sign-in UI when it is needed. Declining is a result, not an error: player is null.
C# IGameCenterBridge.SignInAsyncTS GameCenterBridge.signIn
Response 200 SignInResult SignInResult · 1 field playerGamePlayer | null Null when the player declined — or, on Apple, when Game Center has stopped offering its sheet after repeated cancels, and the player has to sign in from Settings.
GET /_bridge/ games/achievements# Every achievement, merged with progress not sent yet. Needs a signed-in player.
C# IGameCenterBridge.GetAchievementsAsyncTS GameCenterBridge.getAchievements
Parameter In Type forceReloadquery boolean | null optional
Response 200 Achievement []Achievement · 9 fields keystring The app's logical key, or the platform id when the app did not register the achievement. platformIdstring The Game Center or Play Games achievement id. titlestring | null Localized, when the service supplied one. descriptionstring | null Localized, when the service supplied one. currentStepsnumber Never lower than what was recorded on this device. totalStepsnumber 1 for an unlock-once achievement. percentCompletenumber 0 to 100. isUnlockedboolean isHiddenboolean Hidden from the player until it has progress (Apple) or is revealed (Google).
POST /_bridge/ games/achievements/{key} /unlock# Completes an achievement.
C# IGameCenterBridge.UnlockAsyncTS GameCenterBridge.unlock
Parameter In Type keypath string required
Response 204 No contentPOST /_bridge/ games/achievements/{key} /progress# Sets an incremental achievement to an absolute step count. Progress never goes backwards.
C# IGameCenterBridge.SetProgressAsyncTS GameCenterBridge.setProgress
Parameter In Type keypath string required
Body application/json · AchievementProgressRequest AchievementProgressRequest · 1 field stepsnumber An absolute step count, never a delta. A value at or below the current progress is ignored; one above the total is clamped.
Response 204 No contentPOST /_bridge/ games/achievements/{key} /increment# Adds steps to an incremental achievement, counted from the furthest progress this device knows of.
C# IGameCenterBridge.IncrementAsyncTS GameCenterBridge.increment
Parameter In Type keypath string required
Body application/json · AchievementIncrementRequest AchievementIncrementRequest · 1 field stepsnumber Steps to add, at least 1.
Response 204 No contentPOST /_bridge/ games/achievements/{key} /reveal# Makes a hidden achievement visible. Google only; Game Center has no reveal, so it does nothing there.
C# IGameCenterBridge.RevealAsyncTS GameCenterBridge.reveal
Parameter In Type keypath string required
Response 204 No contentPOST /_bridge/ games/achievements/show# Opens the service's achievements UI. Completes once it is open, not when the player closes it.
C# IGameCenterBridge.ShowAchievementsAsyncTS GameCenterBridge.showAchievements
Response 204 No contentPOST /_bridge/ games/leaderboards/{key} /scores# Submits a score. The service decides whether it is a new best.
C# IGameCenterBridge.SubmitScoreAsyncTS GameCenterBridge.submitScore
Parameter In Type keypath string required
Body application/json · SubmitScoreRequest SubmitScoreRequest · 1 field Response 204 No contentPOST /_bridge/ games/leaderboards/{key} /query# The top scores of a leaderboard, for everyone or for the player's friends. Needs a signed-in player, and friends consent for Friends.
C# IGameCenterBridge.GetScoresAsyncTS GameCenterBridge.getScores
Parameter In Type keypath string required
Body application/json · LeaderboardScoresRequest LeaderboardScoresRequest · 3 fields Response 200 LeaderboardEntry []LeaderboardEntry · 5 fields playerGamePlayer scorenumber formattedScorestring | null The score formatted the way the leaderboard is set up to show it. ranknumber isLocalPlayerboolean The signed-in player's own row.
POST /_bridge/ games/leaderboards/show# Opens one leaderboard, or all of them.
C# IGameCenterBridge.ShowLeaderboardAsyncTS GameCenterBridge.showLeaderboard
Body application/json · ShowLeaderboardRequest ShowLeaderboardRequest · 1 field leaderboardKeystring | null One leaderboard, or every leaderboard when null.
Response 204 No contentGET /_bridge/ games/friends# The player's friends who can see this game, asking for consent the first time. Apple needs NSGKFriendListUsageDescription in Info.plist. Google shows a dialog and answers ConsentRequested: ask again when the player is back.
C# IGameCenterBridge.GetFriendsAsyncTS GameCenterBridge.getFriends
Response 200 FriendsResult FriendsResult · 2 fields POST /_bridge/ games/flush# Sends what is queued now and answers with what is left. It runs on its own after every change, at sign-in and when connectivity returns, so call it only to wait for the queue to drain.
C# IGameCenterBridge.FlushAsyncTS GameCenterBridge.flush
Response 200 GameCenterStatus GameCenterStatus · 3 fields platformGameServicePlatform playerGamePlayer | null The signed-in player, or null. pendingCountnumber Unlocks, progress and scores recorded on the device that the service has not accepted yet.
The player signed in, signed out or switched accounts.
C# IGameCenterBridge.OnPlayerChangedAsyncTS GameCenterBridge.onPlayerChanged
Payload PlayerChanged
JSON property names are camelCase and enums travel as their names. A ? marks a property the payload may omit;| null one that may be null.
The game service on this device.
platformGameServicePlatform playerGamePlayer | null The signed-in player, or null. pendingCountnumber Unlocks, progress and scores recorded on the device that the service has not accepted yet.
The game service behind the bridge on this device.
"AppleGameCenter""GooglePlayGames""Custom"
A player of the game service.
idstring Apple gamePlayerID (stable for this game) or Google playerId. displayNamestring The player's display name or alias. platformGameServicePlatform
How a sign-in ended.
playerGamePlayer | null Null when the player declined — or, on Apple, when Game Center has stopped offering its sheet after repeated cancels, and the player has to sign in from Settings.
An achievement, merged with progress recorded on this device that the service has not accepted yet.
keystring The app's logical key, or the platform id when the app did not register the achievement. platformIdstring The Game Center or Play Games achievement id. titlestring | null Localized, when the service supplied one. descriptionstring | null Localized, when the service supplied one. currentStepsnumber Never lower than what was recorded on this device. totalStepsnumber 1 for an unlock-once achievement. percentCompletenumber 0 to 100. isUnlockedboolean isHiddenboolean Hidden from the player until it has progress (Apple) or is revealed (Google).
AchievementProgressRequestobject # stepsnumber An absolute step count, never a delta. A value at or below the current progress is ignored; one above the total is clamped.
AchievementIncrementRequestobject # steps?number Steps to add, at least 1.
SubmitScoreRequestobject # LeaderboardScoresRequestobject # Whose scores to load from a leaderboard.
"Global""Friends"
LeaderboardTimeScopeenum # The window of scores to load from a leaderboard.
"Today""Week""AllTime"
One row of a leaderboard.
playerGamePlayer scorenumber formattedScorestring | null The score formatted the way the leaderboard is set up to show it. ranknumber isLocalPlayerboolean The signed-in player's own row.
ShowLeaderboardRequestobject # Which leaderboard UI to open.
leaderboardKey?string | null One leaderboard, or every leaderboard when null.
Whether the player has let this game see their friends.
"Granted""Denied""Restricted""ConsentRequested""Unavailable"
The signed-in player changed: signed in, signed out or switched accounts.
Like every bridge, /_bridge/games is behind the bridge policy: callers on this device only, plus the launch session.
See Security .