I am trying to create a real time multiplayer game using game center, programatically. My problem is the lack of documentation since the old inviteHandler has been deprecated.
The first thing I do is the authentication:
- (void) authenticateLocalPlayer
{
GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer];
__weak GKLocalPlayer *blockLocalPlayer = localPlayer;
localPlayer.authenticateHandler = ^(UIViewController *receivedViewController, NSError *error)
{
if (receivedViewController != nil)
{
[self presentViewController:receivedViewController animated:YES completion:nil];
}
else if (blockLocalPlayer.isAuthenticated)
{
[blockLocalPlayer registerListener:self];
}
};
}
After that, I start the invitation process as it says on apple docs.
- (void)invitePlayers:(NSArray *)friendsArray
{
GKMatchRequest *request = [[GKMatchRequest alloc] init];
request.minPlayers = 2;
request.maxPlayers = 2;
request.playersToInvite = friendsArray;
request.inviteMessage = #"Your Custom Invitation Message Here";
request.inviteeResponseHandler = ^(NSString *playerID, GKInviteeResponse response)
{
[self updateUIForPlayer: playerID accepted: (response == GKInviteeResponseAccepted)];
};
}
I implemented the following methods, but they are never called:
- (void)player:(GKPlayer *)player didAcceptInvite:(GKInvite *)invite
- (void)player:(GKPlayer *)player didRequestMatchWithPlayers:(NSArray *)playerIDsToInvite
How can I let the other player receive the invite and respond to it? On the apple docs the old deprecated way is demonstrated.
Try to register first for a GKLocalPlayer listener in order to get called via this methods:
- (void)registerListener:(id<GKLocalPlayerListener>)listener
which should conforms to this protocol:GKLocalPlayerListener
Related
Quickblox webrtc video call receive method is not called .I am call to someone he accept the call and we can communicate but while he is calling me i am not geting that call.
`
- (void)didReceiveNewSession:(QBRTCSession *)session userInfo:(NSDictionary *)userInfo {
if (self.session ) {
[session rejectCall:#{#"reject" : #"busy"}];
return;
}
self.session = session;
[QBRTCSoundRouter.instance initialize];
NSParameterAssert(!self.nav);
IncomingCallViewController *incomingViewController =
[self.storyboard instantiateViewControllerWithIdentifier:#"IncomingCallViewController"];
incomingViewController.delegate = self;
incomingViewController.session = session;
incomingViewController.usersDatasource = self.dataSource;
self.nav = [[UINavigationController alloc] initWithRootViewController:incomingViewController];
[self presentViewController:self.nav animated:NO completion:nil];
}
The Quickblox webrtc video call receive method only called when the user is online so make sure you add in Your -
(Void)ViewDidLoad{
[QBRequest logInWithUserLogin:#"xxxxxx"
password:#"xxxxx"
successBlock:^(QBResponse * _Nonnull response, QBUUser * _Nullable user)
{
}];
[[QBChat instance] connectWithUser:self.user completion:^(NSError * _Nullable error) {
NSLog(#"User%#",self.user);
}];
}
It will be called.
FBSDKGameRequestContent *content = [[FBSDKGameRequestContent alloc]init];
content.message = #"Great FB";
content.title = #"Invite Friends";
FBSDKGameRequestDialog *gameDialog = [[FBSDKGameRequestDialog alloc]init];
gameDialog.content = content;
gameDialog.frictionlessRequestsEnabled = YES;
gameDialog.delegate = self;
if ([gameDialog canShow]) {
[gameDialog show];
}
I am using the above code for showing FBFriends. The dialog is opened, but I want to perform some of my custom functionality after user hits send/cancel.
How should I do it?
You're doing:
gameDialog.delegate = self;
So, why don't you use the delegate methods (FBSDKGameRequestDialogDelegate): gameRequestDialogDidCancel: and
gameRequestDialog:didCompleteWithResults: to know if user has cancelled of sent its invitation?
Source
In your YourCurrentClass.h:
#interface YourCurrentClass : NSObject < FBSDKGameRequestDialogDelegate >
In your YourCurrentClass.m:
- (void)gameRequestDialog:(FBSDKGameRequestDialog *)gameRequestDialog
didCompleteWithResults:(NSDictionary *)results
{
//User has done something.
//Check "results" and do something.
}
- (void)gameRequestDialogDidCancel:(FBSDKGameRequestDialog *)gameRequestDialog
{
//User has cancelled
//Do somathing
}
- (void)gameRequestDialog:(FBSDKGameRequestDialog *)gameRequestDialog
didFailWithError:(NSError *)error
{
//An error happened
NSLog(#"Error: %#", error);
//Do something
}
How can I display the GameCenter Login view again, if a user has canceled during first login attempt.
I am using the following code from raywenderlich tutorial,
-(void) authenticateLocalPlayer {
__weak GKLocalPlayer* localPlayer = [GKLocalPlayer localPlayer];
localPlayer.authenticateHandler = ^(UIViewController *viewController, NSError *error) {
[self setLastError:error];
if (localPlayer.authenticated) {
_gameCenterFeaturesEnabled = YES;
currentPlayer = localPlayer;
//NSLog(#"Authenticated User");
[self.delegate2 enableButtonOnAuthentication];
} else if(viewController) {
[self presentViewController:viewController];
} else {
_gameCenterFeaturesEnabled = NO;
}
};
}
The above method is called from viewDidLoad,
[[GameKitHelper sharedGameKitHelper] authenticateLocalPlayer];
[[GameKitHelper sharedGameKitHelper] setDelegate2:self];
If a user cancels the login, I am displaying a message, "to play you must login".
And if tries to call the above method again on a button click the block code does not seems to execute.
Can you point out to what am I doing wrong here?
I was trying to implement an event listener in a turn based game so a player can receive when his turn is active or when he is invited by a friend. GKTurnBasedEventHandler is deprecated in IOS 7 and i read in the documentation that i should use GKLocalPlayerListener; but that's the extend of it. Is there someone who used it already, because there is no info anywhere.
This is what i tried before, and it does not work.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer];
[localPlayer authenticateWithCompletionHandler:^(NSError *error)
{
if (localPlayer.isAuthenticated)
{
GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer];
[localPlayer registerListener:self];
}
}];
return YES;
}
-(void)handleInviteFromGameCenter:(NSArray *)playersToInvite
{
NSLog(#"test");
}
- (void)player:(GKPlayer *)player receivedTurnEventForMatch:(GKTurnBasedMatch *)match didBecomeActive:(BOOL)didBecomeActive
{
NSLog(#"test");
}
Here is some code that I use in order to register GKLocalPlayerListener
__weak GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer];
localPlayer.authenticateHandler = ^(UIViewController *viewController, NSError *error) {
if (viewController) {
[authenticateFromViewController presentViewController:viewController animated:YES completion:^{
[localPlayer registerListener:self];
NSLog(#"Authenticated. Registering Turn Based Events listener");
}];
} else if (localPlayer.authenticated) {
[localPlayer registerListener:self];
NSLog(#"User Already Authenticated. Registering Turn Based Events listener");
} else {
NSLog(#"Unable to Authenticate with Game Center: %#", [error localizedDescription]);
}
};
The documentation states that you should only register for an GKLocalPlayerEventListener once so you could improve this code by checking if you've already registered.
Note that authenticateWithCompletionHandler is deprecated in iOS 6 and they recommend setting the authenticateHandler property like I did above.
I believe you were there. Just this time do a couple of things. Make sure you dont add multiple listeners also before you add a listener, just incase unregister all listeners.
I made sure I only did this once in my whole project, but I get the local player multiple times.
-(void) onLocalPlayerAuthChanged:(GKLocalPlayer*)authPlayer {
[authPlayer unregisterAllListeners];
[authPlayer registerListener:_Whatever_];
}
I might be a little late, but hopefully it will help someone out there...
This is what I do. According to Apple's documentation I create [my] own method that displays an authentication view when appropriate for [my] app.
- (void)authenticateLocalUser
{
if ([GKLocalPlayer localPlayer].authenticated == NO) {
__weak typeof(self) weakSelf = self;
__weak GKLocalPlayer *weakPlayer = [GKLocalPlayer localPlayer];
weakPlayer.authenticateHandler = ^(UIViewController *viewController, NSError *error) {
if (viewController != nil) {
[weakSelf showAuthenticationDialogWhenReasonable:viewController];
} else if (weakPlayer.isAuthenticated) {
// Player has been authenticated!
[weakPlayer registerListener:weakSelf];
} else {
// Should disable Game Center?
}
};
} else {
// Already authenticated
[[GKLocalPlayer localPlayer] registerListener:self];
}
}
-(void)showAuthenticationDialogWhenReasonable:(UIViewController *)controller
{
[[[[[UIApplication sharedApplication] delegate] window] rootViewController] presentViewController:controller animated:YES completion:nil];
}
This code is inside a singleton helper class, it might be simplified if you have it on your own class.
I am trying to develop a realtime multiplayer game for IOS by using cocos2d by using the tutorial on http://www.raywenderlich.com/3325/how-to-make-a-simple-multiplayer-game-with-game-center-tutorial-part-22
Everything works fine including auto matching with a random player but inviting a friend doesn't work because other device cannot receive an invitation.
When I clicked on invite friends button and then selected a friend by using the standard game center interface, it says waiting (forever) and nothing happens. My friend cannot receive an invitation from game center (no notifications).
I can invite a friend by using nearby friends functionality (when this functionality is enabled on both devices) but no invitation notification when nearby friends is disabled.
I spent hours and hours for searching on Google, found similar cases but no solution.
Some early feedback about possible answers:
I use two devices (one iPhone and one iPad), no simulator
All settings on iTunes connect are fine including multiplayer settings
I validated that both devices are connected to sandbox by using different test accounts
I've already checked the notification settings for Game center on both devices
I've already checked all proxy/firewall issues and tried on both WiFi and Cellular for both devices
Game invitations are enabled for both of the devices/accounts
I've already checked the bundle IDs, app version IDs, etc...
Both of the devices are IOS 6.x and App target version os IOS 5.0
I have no other issues about game center (leaderboards, random matchmaking, etc... all fine)
I call the inviteHandler method as soon after I authenticated a user as possible as mentioned in Apple documentation.
Here is my Game center helper class Header file:
#import <Foundation/Foundation.h>
#import <GameKit/GameKit.h>
#protocol GCHelperDelegate
- (void)matchStarted;
- (void)matchEnded;
- (void)match:(GKMatch *)match didReceiveData:(NSData *)data
fromPlayer:(NSString *)playerID;
- (void)inviteReceived;
#end
#interface GCHelper : NSObject <GKMatchmakerViewControllerDelegate, GKMatchDelegate>{
BOOL gameCenterAvailable;
BOOL userAuthenticated;
UIViewController *presentingViewController;
GKMatch *match;
BOOL matchStarted;
id <GCHelperDelegate> delegate;
NSMutableDictionary *playersDict;
GKInvite *pendingInvite;
NSArray *pendingPlayersToInvite;
NSMutableArray *unsentScores;
}
#property (retain) GKInvite *pendingInvite;
#property (retain) NSArray *pendingPlayersToInvite;
#property (assign, readonly) BOOL gameCenterAvailable;
#property (retain) NSMutableDictionary *playersDict;
#property (retain) UIViewController *presentingViewController;
#property (retain) GKMatch *match;
#property (assign) id <GCHelperDelegate> delegate;
- (void)findMatchWithMinPlayers:(int)minPlayers maxPlayers:(int)maxPlayers
viewController:(UIViewController *)viewController
delegate:(id<GCHelperDelegate>)theDelegate;
- (BOOL) reportAchievementIdentifier: (NSString*) identifier percentComplete: (float) percent;
+ (GCHelper *)sharedInstance;
- (void)authenticateLocalUser;
#end
And here is the implementation of the game center helper class
#import "GCHelper.h"
#implementation GCHelper
#synthesize gameCenterAvailable;
#synthesize presentingViewController;
#synthesize match;
#synthesize delegate;
#synthesize playersDict;
#synthesize pendingInvite;
#synthesize pendingPlayersToInvite;
#pragma mark Initialization
static GCHelper *sharedHelper = nil;
+ (GCHelper *) sharedInstance {
if (!sharedHelper) {
sharedHelper = [[GCHelper alloc] init];
}
return sharedHelper;
}
- (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);
}
- (id)init {
if ((self = [super init])) {
gameCenterAvailable = [self isGameCenterAvailable];
if (gameCenterAvailable) {
NSNotificationCenter *nc =
[NSNotificationCenter defaultCenter];
[nc addObserver:self
selector:#selector(authenticationChanged)
name:GKPlayerAuthenticationDidChangeNotificationName
object:nil];
}
}
return self;
}
- (void)authenticationChanged {
if ([GKLocalPlayer localPlayer].isAuthenticated && !userAuthenticated) {
NSLog(#"Authentication changed: player authenticated.");
userAuthenticated = TRUE;
[GKMatchmaker sharedMatchmaker].inviteHandler = ^(GKInvite *acceptedInvite, NSArray *playersToInvite) {
NSLog(#"Received invite");
self.pendingInvite = acceptedInvite;
self.pendingPlayersToInvite = playersToInvite;
[delegate inviteReceived];
};
} else if (![GKLocalPlayer localPlayer].isAuthenticated && userAuthenticated) {
NSLog(#"Authentication changed: player not authenticated");
userAuthenticated = FALSE;
}
}
- (void)lookupPlayers {
NSLog(#"Looking up %d players...", match.playerIDs.count);
[GKPlayer loadPlayersForIdentifiers:match.playerIDs withCompletionHandler:^(NSArray *players, NSError *error) {
if (error != nil) {
NSLog(#"Error retrieving player info: %#", error.localizedDescription);
matchStarted = NO;
[delegate matchEnded];
} else {
// Populate players dict
self.playersDict = [NSMutableDictionary dictionaryWithCapacity:players.count];
for (GKPlayer *player in players) {
NSLog(#"Found player: %#", player.alias);
[playersDict setObject:player forKey:player.playerID];
}
// Notify delegate match can begin
matchStarted = YES;
[delegate matchStarted];
}
}];
}
#pragma mark User functions
- (void)authenticateLocalUser {
if (!gameCenterAvailable) return;
NSLog(#"Authenticating local user...");
if ([GKLocalPlayer localPlayer].authenticated == NO) {
[[GKLocalPlayer localPlayer] authenticateWithCompletionHandler:nil];
} else {
NSLog(#"Already authenticated!");
}
}
- (void)findMatchWithMinPlayers:(int)minPlayers maxPlayers:(int)maxPlayers viewController:(UIViewController *)viewController delegate:(id<GCHelperDelegate>)theDelegate {
if (!gameCenterAvailable) return;
matchStarted = NO;
self.match = nil;
self.presentingViewController = viewController;
delegate = theDelegate;
if (pendingInvite != nil) {
[presentingViewController dismissModalViewControllerAnimated:NO];
GKMatchmakerViewController *mmvc = [[[GKMatchmakerViewController alloc] initWithInvite:pendingInvite] autorelease];
mmvc.matchmakerDelegate = self;
[presentingViewController presentModalViewController:mmvc animated:YES];
self.pendingInvite = nil;
self.pendingPlayersToInvite = nil;
} else {
[presentingViewController dismissModalViewControllerAnimated:NO];
GKMatchRequest *request = [[[GKMatchRequest alloc] init] autorelease];
request.minPlayers = minPlayers;
request.maxPlayers = maxPlayers;
request.playersToInvite = pendingPlayersToInvite;
GKMatchmakerViewController *mmvc = [[[GKMatchmakerViewController alloc] initWithMatchRequest:request] autorelease];
mmvc.matchmakerDelegate = self;
[presentingViewController presentModalViewController:mmvc animated:YES];
self.pendingInvite = nil;
self.pendingPlayersToInvite = nil;
}
}
#pragma mark GKMatchmakerViewControllerDelegate
// The user has cancelled matchmaking
- (void)matchmakerViewControllerWasCancelled:(GKMatchmakerViewController *)viewController {
[presentingViewController dismissModalViewControllerAnimated:YES];
}
// Matchmaking has failed with an error
- (void)matchmakerViewController:(GKMatchmakerViewController *)viewController didFailWithError:(NSError *)error {
[presentingViewController dismissModalViewControllerAnimated:YES];
NSLog(#"Error finding match: %#", error.localizedDescription);
}
// A peer-to-peer match has been found, the game should start
- (void)matchmakerViewController:(GKMatchmakerViewController *)viewController didFindMatch:(GKMatch *)theMatch {
[presentingViewController dismissModalViewControllerAnimated:YES];
self.match = theMatch;
match.delegate = self;
if (!matchStarted && match.expectedPlayerCount == 0) {
NSLog(#"Ready to start match!");
[self lookupPlayers];
}
}
#pragma mark GKMatchDelegate
// The match received data sent from the player.
- (void)match:(GKMatch *)theMatch didReceiveData:(NSData *)data fromPlayer:(NSString *)playerID {
if (match != theMatch) return;
[delegate match:theMatch didReceiveData:data fromPlayer:playerID];
}
// The player state changed (eg. connected or disconnected)
- (void)match:(GKMatch *)theMatch player:(NSString *)playerID didChangeState:(GKPlayerConnectionState)state {
if (match != theMatch) return;
switch (state) {
case GKPlayerStateConnected:
// handle a new player connection.
NSLog(#"Player connected!");
if (!matchStarted && theMatch.expectedPlayerCount == 0) {
NSLog(#"Ready to start match!");
[self lookupPlayers];
}
break;
case GKPlayerStateDisconnected:
// a player just disconnected.
NSLog(#"Player disconnected!");
matchStarted = NO;
[delegate matchEnded];
break;
}
}
// The match was unable to connect with the player due to an error.
- (void)match:(GKMatch *)theMatch connectionWithPlayerFailed:(NSString *)playerID withError:(NSError *)error {
if (match != theMatch) return;
NSLog(#"Failed to connect to player with error: %#", error.localizedDescription);
matchStarted = NO;
[delegate matchEnded];
}
// The match was unable to be established with any players due to an error.
- (void)match:(GKMatch *)theMatch didFailWithError:(NSError *)error {
if (match != theMatch) return;
NSLog(#"Match failed with error: %#", error.localizedDescription);
matchStarted = NO;
[delegate matchEnded];
}
- (void)reportScore:(int64_t)score forCategory:(NSString *)category {
// Only execute if OS supports Game Center & player is logged in
if ([self isGameCenterAvailable] && [GKLocalPlayer localPlayer].authenticated == YES)
{
// Create score object
GKScore *scoreReporter = [[[GKScore alloc] initWithCategory:category] autorelease];
// Set the score value
scoreReporter.value = score;
// Try to send
[scoreReporter reportScoreWithCompletionHandler:^(NSError *error) {
if (error != nil)
{
// Handle reporting error here by adding object to a serializable array, to be sent again later
[unsentScores addObject:scoreReporter];
}
}];
}
}
- (BOOL) reportAchievementIdentifier: (NSString*) identifier percentComplete: (float) percent {
if ([self isGameCenterAvailable] && [GKLocalPlayer localPlayer].authenticated == YES)
{
GKAchievement *achievement = [[[GKAchievement alloc] initWithIdentifier: identifier] autorelease];
if (achievement)
{
achievement.percentComplete = percent;
[achievement reportAchievementWithCompletionHandler:^(NSError *error)
{
if (error != nil)
{
// Retain the achievement object and try again later (not shown).
}
}];
}
return YES;
}
return NO;
}
#end
And Finally this is how I call the game center from my game layer (I tried two different options but none of them worked)
Option 1
[[GCHelper sharedInstance] findMatchWithMinPlayers:2 maxPlayers:2 viewController: [[[UIApplication sharedApplication] keyWindow] rootViewController] delegate: self];
Option 2
AppController *app = (AppController*) [[UIApplication sharedApplication] delegate];
UINavigationController *viewController = [app navController];
[[GCHelper sharedInstance] findMatchWithMinPlayers:2 maxPlayers:2 viewController:viewController delegate:self];
Any help will be appreciated. Thanks in advance...
I've been dealing with multiplayer for a few months now and invitations have been a real issue for me....and like you, I used Ray's tutorial to get me started. I realized today that Ray's code has a bug in it where invites will not work if both clients have the GKMatchmakerView up. You need to dismiss it when you first receive the invite with something along the lines of:
[gcdelegate.viewController dismissViewControllerAnimated:YES
completion:^{
GKMatchmakerViewController *mmvc = [[GKMatchmakerViewController alloc] initWithInvite:pendingInvite];
mmvc.matchmakerDelegate = self;
[gcdelegate.viewController presentModalViewController:mmvc animated:YES];
self.pendingInvite = nil;
self.pendingPlayersToInvite = nil;
boo_invite=true;
}];
Ok, it seems that it again works without any changes in our code or network settings. I opened ticket to Apple support, bug records, etc... and it seems that some of them worked...
Now we understand that it was a bug in Game Center sandbox. As far as I see, sanbox version of game center is not that stable and people in Apple don't give enough attention to this service. There is also no way to check the system status on the internet.
I'm still continuing to discuss with Apple support in order to understand the reason and will share the all conversation here when it's completed.
Happy coding...