-
Notifications
You must be signed in to change notification settings - Fork 1
Authentication
Adding Game Center support requires more than simple coding changes. Game Center imposes specific requirements on the design of your game. For example, to implement leaderboards, your game must have a way to quantitatively measure a player's performance. But more than that, Game Center changes how you design and test your game. You aren't simply adding code to your app; you are also configuring assets used by Game Center. These assets are configured separately from your app bundle but are intimately tied to the code in your game. You need to test these pieces to ensure that all of the pieces work correctly together.
To create a Game Center-aware game, you need to understand these basics before you begin writing code.
Here’s a reasonable process to follow when designing a game that supports Game Center:
-
Decide which Game Center features you plan to support.
-
Define your game mechanics, keeping in mind the requirements and limitations of Game Center.
-
Implement your game engine, ignoring Game Center for now. Focus on developing the other aspects of your game, such as your game engine and gameplay. You do this to avoid needing to create an iTunes Connect record earlier than you need to.
-
When you need to implement Game Center:
-
Create an iTunes Connect record for your game, using the game’s bundle ID. Enable Game Center in the iTunes Connect record; setting this flag in the iTunes Connect record authorizes the Game Center service to allow your game to connect to it.
-
Create an explicit App ID using your game’s bundle ID.
-
Enable Game Center in this App ID; this authorizes the app on the device to contact Game Center’s servers.
-
Create a new provisioning profile using this new explicit App ID.
-
Test to ensure you can build and sign your game using this profile.
-
Enable the Game Kit to your project.
-
Implement authentication.
-
Implement other Game Center features.
-
Test your Game Center features using the Game Center development environment
-
Your game should authenticate the player as early as possible after launching, ideally as soon as you can present a user interface to the player. For example, your game may be launched because the player accepted an invitation to join a match or to take a turn in a turn-based match, so you want your game to authenticate the player and process the match invitation as quickly as possible. After you set a handler, authentication begins automatically and is repeated when your game moves to the background and then back to the foreground.
If the device does not have an authenticated player, Game Kit passes a view controller to your authenticate handler. When presented, this view controller displays the authentication user interface. Your game should pause other activities that require user interaction (such as your game loop), present this view controller and then return. When the player finishes interacting with it, the view controller is dismissed automatically.
If the authentication process succeeded, the ISN_GKLocalPlayer singleton object’s Authenticated property is set to TRUE
and the object’s other properties are set to match those of the connected player.
If the authentication process failed, the ISN_GKLocalPlayer singleton object’s Authenticated property is set to FALSE
and the object’s other properties are cleared.
See an example of how to authenticate the local player:
ISN_GKLocalPlayer.Authenticate((SA_Result result) => {
if (result.IsSucceeded) {
Debug.Log("Authenticate is succeeded!");
}
else {
Debug.Log("Authenticate is failed! Error with code: " + result.Error.Code + " and description: " + result.Error.Message);
}
});
Two common errors that can be received by your game are worth describing in more detail:
- Receiving a
GKErrorGameUnrecognized
error means that you have not enabled Game Center for your app in iTunes Connect. Sign in to your iTunes Connect account and verify that your app has Game Center enabled. Also, confirm that the bundle identifier in your Xcode project matches the bundle identifier you assigned to your app in iTunes Connect. - Receiving a
GKErrorNotSupported
error means that the device your game is running on does not support Game Center. You should disable all Game Center related features.
Warning: The Authenticate method above is deprecated. Use the setAuthenticateHandler instead.
ISN_GKLocalPlayer.SetAuthenticateHandler(result => {
if (result.IsSucceeded) {
Debug.Log("Authenticate is succeeded!");
var player = ISN_GKLocalPlayer.LocalPlayer;
Debug.Log($"player id: {player.PlayerID}");
Debug.Log($"player Alias: {player.Alias}");
Debug.Log($"player DisplayName: {player.DisplayName}");
Debug.Log($"player Authenticated: {player.Authenticated}");
Debug.Log($"player Underage: {player.Underage}");
player.GenerateIdentityVerificationSignatureWithCompletionHandler(signatureResult => {
if(signatureResult.IsSucceeded) {
Debug.Log($"signatureResult.PublicKeyUrl: {signatureResult.PublicKeyUrl}");
Debug.Log($"signatureResult.Timestamp: {signatureResult.Timestamp}");
Debug.Log($"signatureResult.Salt.Length: {signatureResult.Salt.Length}");
Debug.Log($"signatureResult.Signature.Length: {signatureResult.Signature.Length}");
} else {
Debug.LogError($"IdentityVerificationSignature has failed: {signatureResult.Error.FullMessage}");
}
});
}
else {
Debug.LogError($"Authenticate is failed! Error with code: {result.Error.Code} and description: {result.Error.Message}");
}
});
The reason it was changed is the following.
When people see the Authenticate method name they assume it can be called several times per application session. Wich is wrong.
Let's talk about a few scenarios that may happen and how your implementation should react to it.
The implementation first.
All you need to do is to subscribe to the AuthenticateHandler (see the code snippet above) as soon as possible after your game launches.
This method call will start auth flow for the user.
You should never call setAuthenticateHandler again during the application session.
But user status may change, so expect the callback you set with using setAuthenticateHandler method will be called several times (in case user auth status is changed) and you need to react accordingly (update your UI, set code flags, whatever else you need to update based on the user GC auth state)
- Game launched and setAuthenticateHandler called.
- The user auth is succeeded.
- All Good
This is the most common scenario and the most simple one. Let's now talk about a few more complicated items.
- Game launched and setAuthenticateHandler called.
- The user auth is failed.
- The user plays the game without GC.
- User clicks Leaderboards button (any other piece of GC related UI in your game)
You probably want to restart the auth and give the user an ability to auth in GC without leaving your app.
Well, you can't do that. You can't control when GC auth dialog appears.
BUT if a user is failed in the first place, there is only one way how it could happen.
The GC auth state is configured in the iOS settings application and shared across all the installed apps. So if a user has signed in GC once, your game will no show ANY DIALOG and the user will be signed right away without any additional UI, warnings, permissions requests, etc. The user CAN'T ban ONLY your game from using GC. This is a global system state.
Now, if you got the failed auth state, here is what happened. The user hasn't configured the GC account on his device. When he launched your game, the GC sing-in dialog appeared, and the user has declined it. And if the user declined in once, Apple won't allow to bother him with the same dialog again during the active app session.
So what should you do if the user specifically requested some GC related feature using your game UI (at this point you UI should kind of acknowledge that functionality isn't available by its appearance).
The only thing you can do is to show the dialog with the explanation that the user has to sing in the system GC application. You may also launch a setting app for the user and open it on the GC page (but that's app to you)
TL; DR;
User need to leave your application and sing using iOS settings app, or restart your app (fold/unfold would work as well)
Nex scenario.
- Game launched and setAuthenticateHandler called.
- The user auth is failed.
- The user plays the game without GC.
- User exists your app (without closing)
- User opens your app again -> the GC sing in the dialog will be present
You don't need to do anything in order to implement this case. If the user wasn't singed with GC, every time he will open your application, the GC sing in the dialog will show up. And since you already subscribed to Auth Handler, you will get an update every time it happens.
So you only need to be aware that HandlerUserAuthentication (see the snippet above) may be called several times per session and act accordingly.
-
Game launched and setAuthenticateHandler called.
-
The user auth is failed.
-
The user plays the game without GC.
-
User exists your app (without closing)
-
User sing in into GC using the settings app.
-
User opens your app -> You are getting auth callback with success state.
-
Game launched and setAuthenticateHandler called.
-
User auth is succeeded.
-
The user plays the game.
-
User exists your app (without closing)
-
The user decided to change the GC account or sing out using the GC settings app.
-
User opens your app -> You are getting auth callback with new user info or failed auth state.
And in conclusion, apart from the Apple documentation:
That's about it.
Let's talk about a few scenarios that may happen and how your implementation should react to it.
Let's talk about the implementation first.
All you need to do is to subscribe to the AuthenticateHandler (see the code snippet above) as soon as possible after your game launches.
This method call will start auth flow for the user.
You should never call setAuthenticateHandler again during the application session.
But user status may change, so expect the callback you set with using setAuthenticateHandler method will be called several times (in case user auth status is changed) and you need to react accordingly (update your UI, set code flags, whatever else you need to update based on the user GC auth state)
- Game launched and setAuthenticateHandler called.
- The user auth is succeeded.
- All Good
This is the most common scenario and the most simple one. Let's now talk about a few more complicated items.
- Game launched and setAuthenticateHandler called.
- The user auth is failed.
- The user plays the game without GC.
- User clicks Leaderboards button (any other piece of GC related UI in your game)
You probably want to restart the auth and give the user an ability to auth in GC without leaving your app.
Well, you can't do that. You can't control when GC auth dialog appears.
BUT if a user is failed in the first place, there is only one way how it could happen.
The GC auth state is configured in the iOS settings application and shared across all the installed apps. So if a user has signed in GC once, your game will no show ANY DIALOG user will be singed right away without any additional UI, warnings, permissions requests, etc. and user CAN'T ban ONLY your game from using GC. This is a global system state.
Now, if you got the failed auth state, here is what happened. The user hasn't configured the GC account on his device. When he launched your game, the GC sing-in dialog appeared, and the user has declined it. And if the user declined in once, Apple won't allow to bother him with the same dialog again during the active app session.
So what should you do if the user specifically requested some GC related feature using your game UI (at this point you UI should kind of acknowledge that functionality isn't available by its appearance).
The only thing you can do is to show the dialog with the explanation that the user has to sing in the system GC application. You may also launch a setting app for the user and open it on the GC page (but that's app to you)
TL; DR; User need to leave your application and sing using iOS settings app, or restart your app (fold/unfold would work as well)
Nex scenario.
- Game launched and setAuthenticateHandler called.
- The user auth is failed.
- The user plays the game without GC.
- User exists your app (without closing)
- User opens your app again -> the GC sing in the dialog will be present
You don't need to do anything in order to implement this case. If the user wasn't singed with GC, every time he will open your application, the GC sing in the dialog will show up. And since you already subscribed to Auth Handler, you will get an update every time it happens.
So you only need to be aware that HandlerUserAuthentication (see the snippet above) may be called several times per session and act accordingly.
-
Game launched and setAuthenticateHandler called.
-
The user auth is failed.
-
The user plays the game without GC.
-
User exists your app (without closing)
-
User sing in into GC using the settings app.
-
User opens your app -> You are getting auth callback with success state.
-
Game launched and setAuthenticateHandler called.
-
User auth is succeeded.
-
The user plays the game.
-
User exists your app (without closing)
-
The user decided to change the GC account or sing out using the GC settings app.
-
User opens your app -> You are getting auth callback with new user info or failed auth state.
And in conclusion, a part from the Apple documentation:
That's about it.
TL; DR;
- Call setAuthenticateHandler only once on the application launch.
- Be prepared to receive handler callback at any time and even a few times per session. User auth state or user info may change.
- Make sure you can update your app UI / State accordingly when the callback is received.
After successful authentication, you may use any of the local player fileds. The local player is represented as ISN_GKLocalPlayer singleton object. See the example below how to print local player info after authentication:
using SA.iOS.GameKit;
...
var player = ISN_GKLocalPlayer.LocalPlayer;
Debug.Log($"player id: {player.PlayerID}");
Debug.Log($"player Alias: {player.Alias}");
Debug.Log($"player DisplayName: {player.DisplayName}");
Debug.Log($"player Authenticated: {player.Authenticated}");
Debug.Log($"player Underage: {player.Underage}");
You can also grab the player photo using the LoadPhoto method. See the example below:
using SA.iOS.GameKit;
...
ISN_GKLocalPlayer player = ISN_GKLocalPlayer.LocalPlayer;
player.LoadPhoto(GKPhotoSize.Normal, (result) => {
if(result.IsSucceeded) {
Debug.Log("Player photo: " + result.Image);
} else {
Debug.Log("Failed to load player's photo: " + result.Error.FullMessage);
}
});
The GenerateIdentityVerificationSignature generates a signature that allows a third party server to authenticate the local player.
When this method is called, it creates a new background task to handle the request. The method then returns control to your game. Later, when the task is complete, Game Kit calls your completion handler. The completion handler is always called on the main thread.
Invoke this method to verify the identity of the local player. Use the following steps to generate a signature on your server:
-
Call ISN_GKLocalPlayer.LocalPlayer.GenerateIdentityVerificationSignature in your app.
-
Send the
publicKeyURL
,signature
,salt
, andtimestamp
parameters to the third party server used for authentication. -
Use the
publicKeyURL
on the third party server to download the public key. -
Verify with the appropriate signing authority that the public key is signed by Apple.
-
Retrieve the player’s PlayerID and BundleID .
-
Concatenate into a data buffer the following information, in the order listed:
-
Generate a SHA-256 hash value for the buffer.
-
Using the public key downloaded in step 3, verify that the hash value generated in step 7 matches the
signature
parameter provided by the API.
If the generated and retrieved signatures match, the local player has been authenticated.
See the example below
player.GenerateIdentityVerificationSignatureWithCompletionHandler(signatureResult => {
if(signatureResult.IsSucceeded) {
Debug.Log($"signatureResult.PublicKeyUrl: {signatureResult.PublicKeyUrl}");
Debug.Log($"signatureResult.Timestamp: {signatureResult.Timestamp}");
Debug.Log($"signatureResult.Salt.Length: {signatureResult.Salt.Length}");
Debug.Log($"signatureResult.Signature.Length: {signatureResult.Signature.Length}");
} else {
Debug.LogError($"IdentityVerificationSignature has failed: {signatureResult.Error.FullMessage}");
}
});
Reliable and high-quality Unity Development service. Let's Talk!
Website | AssetStore | LinkedIn | Youtube | Scripting Reference
- Getting Started
- Authentication
- Game Center UI
- Leaderboards
- Default Leaderboard
- Achievements
- Saving A Game
- Access Point
- iTunes Connect Setup
- StoreKit Initialization
- Purchase flow
- Receipt Validation
- Store Review Controller
- Storefront API
- Subscription Offers