so I've been reading articles and the Apple developer library for a while but I'm not able to solve this.
I want to have a Game Center Leaderboard for my app, that's accessible through my game menu. However, since I'm using Sprite Kit for creating my game this causing me some problems.
I registered my app in iTunes Connect, enabled Game Center and the Identity information within Xcode matches the ones in iTunes Connect.
I created a button, that should open the Game Center Leaderboard. When it's tapped this is called:
GKLeaderboardViewController *leaderboardController = [[GKLeaderboardViewController alloc] init];
if (leaderboardController != NULL)
{
leaderboardController.timeScope = GKLeaderboardTimeScopeAllTime;
leaderboardController.leaderboardDelegate = self;
GKGameCenterViewController *gameCenterController = [[GKGameCenterViewController alloc] init];
if (gameCenterController != nil)
{
gameCenterController.viewState = GKGameCenterViewControllerStateLeaderboards;
UIViewController *vc = self.view.window.rootViewController;
[vc presentViewController: gameCenterController animated: YES completion:nil];
}
}
Now I get the message, that Game Center is unavailable because no player is signed in.
To fix that I tried using the authentication method from apple:
- (void) authenticateLocalPlayer
{
GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer];
localPlayer.authenticateHandler = ^(UIViewController *viewController, NSError *error){
if (viewController != nil)
{
//showAuthenticationDialogWhenReasonable: is an example method name.
[self showAuthenticationDialogWhenReasonable: viewController];
}
else if (localPlayer.isAuthenticated)
{
//authenticatedPlayer: is an example method name.
[self authenticatedPlayer: localPlayer];
}
else
{
[self disableGameCenter];
}
}];
}
I'm now having trouble defining these methods being called in the authentication method.
My explicit question: How do I authenticate a local player?
Related
I have a singleton class that handles all my game center stuff. I have set it to be a delegate of GKAchievementViewControllerDelegate.
I call the following method showAchievements
- (void) showAchievements
{
GKAchievementViewController *achievements = [[GKAchievementViewController alloc] init];
if (achievements != nil)
{
achievements.achievementDelegate = self.delegate;
[self.viewController presentModalViewController: achievements animated: YES];
}
}
and I implement the following
- (void)achievementViewControllerDidFinish:(GKAchievementViewController *)achievements
{
[self.viewController dismissModalViewControllerAnimated: YES];
}
I set self.viewController with the following code before calling showAchievements
AppController* appDelegate = (AppController*)[[UIApplication sharedApplication] delegate];
[GameCenterManager instance].viewController = appDelegate.window.rootViewController;
The app crashes and the following shows up in the console log when I call showAchievements.
Jul 16 12:36:28 imac-3 myApp[17103] <Info>: 12:36:28.287698 com.apple.AVConference: GKSConnSettings: set server: {
"gk-cdx" = "17.173.254.218:4398";
"gk-commnat-cohort" = "17.173.254.220:16386";
"gk-commnat-main0" = "17.173.254.219:16384";
"gk-commnat-main1" = "17.173.254.219:16385";
}
At the point where I call showAchievements the user has been logged in successfully.
Any idea what I might be doing wrong here?
My suspicions were correct, I was trying to access the viewController in the wrong way. I should have been using the CCDirectorIOS object instead of the UIWindow object.
GKAchievementViewController is deprecated. Try using GKGameCenterViewController.
I have been developing a game which allows for multiplayer matches. I had previous tested the multiplayer invitations and they had all worked. Sending a request from one device displayed a banner on the other and if the invite was accepted the game started.
Just before submitting the app, two nights ago, I tested this functionality again only to find that it has stopped working.
- (void)authenticateLocalUser:(UIViewController *)viewController :(id<GCHelperDelegate>)theDelegate
{
delegate = theDelegate;
self.presentingViewController = viewController;
if (!gameCenterAvailable) {
// Game Center is not available.
userAuthenticated = FALSE;
}
else{
GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer];
/*
The authenticateWithCompletionHandler method is like all completion handler methods and runs a block
of code after completing its task. The difference with this method is that it does not release the
completion handler after calling it. Whenever your application returns to the foreground after
running in the background, Game Kit re-authenticates the user and calls the retained completion
handler. This means the authenticateWithCompletionHandler: method only needs to be called once each
time your application is launched. This is the reason the sample authenticates in the application
delegate's application:didFinishLaunchingWithOptions: method instead of in the view controller's
viewDidLoad method.
Remember this call returns immediately, before the user is authenticated. This is because it uses
Grand Central Dispatch to call the block asynchronously once authentication completes.
*/
//ios 6
[localPlayer setAuthenticateHandler:(^(UIViewController* viewcontroller, NSError *error) {
if (viewcontroller != nil){
userAuthenticated = FALSE;
[self.presentingViewController presentViewController: viewcontroller animated: YES completion:nil];
}
else if (localPlayer.isAuthenticated){
// Enable Game Center Functionality
userAuthenticated = TRUE;
[self checkForInvite:self.presentingViewController :delegate];
if (! self.currentPlayerID || ! [self.currentPlayerID isEqualToString:localPlayer.playerID]) {
// Current playerID has changed. Create/Load a game state around the new user.
self.currentPlayerID = localPlayer.playerID;
// get friends of local player
[localPlayer loadFriendsWithCompletionHandler:^(NSArray *friends, NSError *error) {
if (friends != nil)
{
[self loadPlayerData: friends];
}
}];
}
}
else{
userAuthenticated = FALSE;
}
[scoreHandler setGameCentreAvailable:userAuthenticated];
})];
}
}
- (void)checkForInvite :(UIViewController *)viewController :(id<GCHelperDelegate>)theDelegate
{
delegate = theDelegate;
self.presentingViewController = viewController;
NSLog(#"Invite handler installed");
[GKMatchmaker sharedMatchmaker].inviteHandler = ^(GKInvite *acceptedInvite, NSArray *playersToInvite) {
// Insert application-specific code here to clean up any games in progress.
if (acceptedInvite){
NSLog(#"Accepted");
GKMatchmakerViewController *mmvc = [[[GKMatchmakerViewController alloc] initWithInvite:acceptedInvite] autorelease];
mmvc.matchmakerDelegate = self;
[viewController presentViewController: mmvc animated: YES completion:nil];
} else if (playersToInvite) {
NSLog(#"Match Request");
GKMatchRequest *request = [[[GKMatchRequest alloc] init] autorelease];
request.minPlayers = 2;
request.maxPlayers = 2;
request.playersToInvite = playersToInvite;
GKMatchmakerViewController *mmvc = [[[GKMatchmakerViewController alloc] initWithMatchRequest:request] autorelease];
mmvc.matchmakerDelegate = self;
[viewController presentViewController: mmvc animated: YES completion:nil];
}
};
}
The debug window in xcode shows the following:
2013-03-27 18:06:20.112 MyApp[791:907] Authentication changed: player authenticated.
2013-03-27 18:06:21.219 MyApp[791:907] Invite handler installed
Mar 27 18:06:21 Neils-iPhone MyApp[791] <Notice>: 18:06:21.356712 com.apple.GameKitServices: -[GKDiscoveryManager startAdvertisingLocalPlayer:discoveryInfo:]: I am [<nil>] [7989F444CF2BDA83] discoveryInfo [{
e = 2;
h = A42FD7FD;
}]
Is the "I am []..." significant in the lines above?
I have even downloaded and run the tutorial from Ray Wenderlich's site for creating a multiplayer game and tried that. That exhibits the same issues, unless it is running in the foreground on both devices. My app does not display invitation requests even if running in the foreground.
Has anyone else experienced this problem or have any ideas what is going on? authenticateLocalUser is called from applicationDidFinishLaunching
The only way I could make invites-by-name work is by going to Settings/Notifications/Game Center and making Game Center display Alerts, not Banners.
If you have GC display alerts, you get a popup box like this:
This dialog acts like a big parent. If the user hits Accept, then your [GKMatchmaker sharedMatchmaker].inviteHandler gets invoked.
If the user hits Decline, then your game never knows that he was invited to any party ever. User hitting Decline means the parent rips up the invitation and never tells his child he got invited to a game at all.
This is the only way I could get invite-by-name to work.
I called the code below to add Game center user banner pop up at the top of the screen, but it says Game is not recognized by Game Center:
I added this to my addDidFinishLaunching:
- (void)applicationDidFinishLaunching:(UIApplication *)application {
[[GKLocalPlayer localPlayer] authenticateWithCompletionHandler:^(NSError *error){
if (error ==nil) {
NSLog(#"Success");
} else {
NSLog(#"Fail");
}
}];
For viewing leader boards, what code do I need to add to properly call it?
-(void)viewscores:(SPEvent*)event{
CODE HERE
}
You could try it like that :
GKLeaderboardViewController *leaderboardVC = [[[GKLeaderboardViewController alloc]init]autorelease];
if (leaderboardVC !=nil) {
leaderboardVC.leaderboardDelegate = self;
[self presentViewController:leaderboardVC];
}
I hope it helps :-)
How do you prevent the Game Center "Welcome Back" message from displaying every time your app wakes up from sleep? Some apps (like Scramble CE, Jetpack Joyride, and Bubblin) handle it correctly (just one welcome message on launch), while others (like Backgammon NJ) don't (welcome message every time the device wakes up).
The block code that's running is no longer in my control (sent to authenticateWithCompletionHandler), and the welcome message appears even if the block is empty anyway.
This behavior started happening with iOS 5.0 (was fine in 4.x), and happens in both the Simulator and real devices, in the Sandbox and not.
Thanks!
I'm from BivisSoft. We've developed Bubblin.
We have a Singleton that controls GameCenter.
Here is our code...
It's based on tutorial by Jacob Gundersen - http://www.raywenderlich.com/5480/beginning-turn-based-gaming-with-ios-5-part-1.
You can try it!
#pragma mark Singleton
static BSGameCenterManager *sharedHelper = nil;
+ (BSGameCenterManager *) sharedInstance {
if (!sharedHelper) {
sharedHelper = [[BSGameCenterManager alloc] init];
}
return sharedHelper;
}
#pragma mark Initializers
- (id)init {
if ((self = [super init])) {
gameCenterAvailable = [self isGameCenterAvailable];
if (gameCenterAvailable) {
self.localPlayerId = #"";
NSNotificationCenter *nc =
[NSNotificationCenter defaultCenter];
[nc addObserver:self
selector:#selector(authenticationChanged)
name:GKPlayerAuthenticationDidChangeNotificationName
object:nil];
}
}
return self;
}
// Check if GameCenter is Avaiable
- (BOOL)isGameCenterAvailable {
// check for presence of GKLocalPlayer API
Class gcClass = (NSClassFromString(#"GKLocalPlayer"));
// check if the device is running iOS 4.1 or later
NSString *reqSysVer = #"4.1";
NSString *currSysVer = [[UIDevice currentDevice] systemVersion];
BOOL osVersionSupported = ([currSysVer compare:reqSysVer
options:NSNumericSearch] != NSOrderedAscending);
return (gcClass && osVersionSupported);
}
#pragma mark Authentication
- (void)authenticationChanged {
if ([GKLocalPlayer localPlayer].isAuthenticated &&
!self.userAuthenticated) {
NSLog(#"GameCenter authentication changed: player authenticated.");
self.userAuthenticated = TRUE;
self.localPlayerId = [[GKLocalPlayer localPlayer] playerID];
[[NSNotificationCenter defaultCenter] postNotificationName: #"gameCenterPlayerAuthenticated" object: nil userInfo: nil];
[self checkNotSentScores];
} else if (![GKLocalPlayer localPlayer].isAuthenticated &&
self.userAuthenticated) {
NSLog(#"GameCenter authentication changed: player not authenticated");
self.userAuthenticated = FALSE;
self.localPlayerId = #"";
}
}
- (void)authenticateLocalUser {
if (!gameCenterAvailable) return;
NSLog(#"GameCenter authenticating local user...");
if ([GKLocalPlayer localPlayer].authenticated == NO) {
[[GKLocalPlayer localPlayer]
authenticateWithCompletionHandler:nil];
} else {
NSLog(#"GameCenter already authenticated!");
}
}
In the Game Kit programming guide I found this bit of documentation:
All games that support Game Center must authenticate the local player
before using any of Game Center’s features. Your game should
authenticate the player as early as possible after launching. Ideally,
authentication should happen as soon as your game can present a user
interface to the player. When your game authenticates a player, Game
Kit first checks to see whether there is already an authenticated
player on the device. If there is an authenticated player, Game Kit
briefly displays a welcome banner to the player.
It doesn't seem as if you can change this behavior with the public API's.
I have been trying to implement Game Center multiplayer and struggling to get anything beyond a GKMatchViewController.
I have two views, one is my main menu, this is where multiplayer is launched and the player gets the GKMatchViewController. Then behind this, the user doesn't know it but the view changes to the multiplayer view where they actually play so when the GKMatchViewController is dismissed they are in the game view rather than menu.
Here I launch multiplayer (yes I am using cocos2d):
-(void)mpGo:(id)sender{
GKMatchRequest *request = [[[GKMatchRequest alloc] init] autorelease];
request.minPlayers = 2;
request.maxPlayers = 2;
[[GKMatchmaker sharedMatchmaker] findMatchForRequest:request
withCompletionHandler:^(GKMatch *returnedMatch, NSError *error)
{
if (error) NSLog(#"match error: %#", error);
else if (returnedMatch != nil)
{
match = [returnedMatch retain];
match.delegate = self; // start!
}
}];
tempVC = [[UIViewController alloc] init];
GKMatchmakerViewController *mmvc = [[[GKMatchmakerViewController alloc] initWithMatchRequest:request] autorelease];
if (mmvc != nil)
{
mmvc.matchmakerDelegate = self;
[[[CCDirector sharedDirector] openGLView] addSubview:tempVC.view];
[tempVC presentModalViewController:mmvc animated: YES];
}
[[CCDirector sharedDirector] replaceScene:[CCTransitionFade transitionWithDuration:1.0 scene:[mpView node] withColor:ccWHITE]];
}
When pressing 'Play Now' I get this error:
match error: Error Domain=GKErrorDomain Code=2 "The requested operation has been cancelled." UserInfo=0x2248d0 {NSLocalizedDescription=The requested operation has been cancelled.}
Then it just stays on that 'Finding Players...' view.
I never done something with GameCenter but maybe I can help anyway. When I get the error
"The requested operation has been
cancelled"
in reverse geocoder I have figured out, that the app had not the time to perform the request.
You get the error by executing findMatchForRequest:, so maybe your request variable is not set completely, check that. You can also try to put a NSLog() in first line (in the block) and look in your passed variable returnedMatch and error