The Game Center turn-based matchmaking interface allows a user to create a match with a number of players and fill some of those places with invites to friends and leave the others for auto-matching.
I am creating our own in-game match creation interface which works fine, so far, except when it comes to creating a match with both invited and auto-match players.
GKMatchmaker has the addPlayersToMatch method where I believe you can add auto-match players once the match exists, but GKTurnBasedMatch has no equivalent method.
The following is the code I am using, which works just fine. If anyone knows how to add a number of auto-match players it would be much appreciated!
- (GKMatchRequest *) buildMatchRequestWithFriends: (NSArray *) friendsList NumberOfPlayers: (NSInteger) numberOfPlayers
{
NSLog(#"TurnByTurnHelper.buildMatchRequestWithFriends");
GKMatchRequest *request = [[GKMatchRequest alloc] init];
NSArray *playersToInvite = [NSArray arrayWithArray:friendsList];
request.playersToInvite = playersToInvite;
request.defaultNumberOfPlayers = numberOfPlayers + 1;
return request;
}
- (void) requestMatchWithFriends:(NSArray *) friendsList NumberOfPlayers: (NSInteger) numberOfPlayers{
if (!_delegate)
{
NSLog(#"Error: Expected but did not find delegate");
return;
}
GKMatchRequest *request = [self buildMatchRequestWithFriends:friendsList NumberOfPlayers: numberOfPlayers];
[GKTurnBasedMatch findMatchForRequest: request withCompletionHandler:^(GKTurnBasedMatch *match, NSError *error)
{
if (match){
NSLog(#"findMatchForRequest: Success!");
// Add match to matches
} else {
NSLog(#"error: %#", error);
}
}];
}
Well, with a bit of searching around and testing I have discovered that the Apple
Developer documentation is incomplete and/or misleading. The following statement is made in the playersToInvite property section of GKMatchRequest:
"If the value of the property is non-nil, when you use the request to create a match, Game Center invites those players to the match. No automatching is done and the GKMatchRequest maxPlayers and minPlayers properties are ignored."
This is true for GKMatchmaker but NOT for GKTurnBasedMatch! For GKTurnBasedMatch, if you provide an array of playerIds in the playersToInvite property, the maxPlayers and minPlayers properties are NOT ignored and Game Center fills up the extra spaces with Random players.
The following code does the trick:
- (GKMatchRequest *) buildMatchRequestWithFriends: (NSArray *) friendsList NumberOfPlayers: (NSInteger) numberOfPlayers
{
GKMatchRequest *request = [[GKMatchRequest alloc] init];
NSArray *playersToInvite = [NSArray arrayWithArray:friendsList];
if([friendsList count]<=0){
request.minPlayers = 2;
request.maxPlayers = numberOfPlayers;
request.playersToInvite = nil;
} else {
request.minPlayers = 2;
request.maxPlayers = numberOfPlayers;
request.playersToInvite = playersToInvite;
}
return request;
}
And the moral of that story is: sometimes it's better not to RTFM!
Related
Here's my code for retrieving list of YouTube videos from a particular channel:
GTLServiceYouTube *service;
self.vidInfos = [[NSMutableArray alloc] init];
service = [[GTLServiceYouTube alloc] init];
service.APIKey = #"my-api-key";
GTLQueryYouTube *query1 = [GTLQueryYouTube queryForPlaylistItemsListWithPart:#"id,snippet"];
query1.playlistId = #"the-playlist-id-from-utube-channel";
query1.maxResults = 50;
[service executeQuery:query1 completionHandler:^(GTLServiceTicket *ticket, id object, NSError *error) {
if (!error) {
GTLYouTubePlaylistItemListResponse *playlistItems = object;
for (GTLYouTubePlaylistItem *playlistItem in playlistItems) {
[self.vidInfos addObject:playlistItem];
}
[self.tableView reloadData];
}else {
NSLog(#"%#", error);
}
}];
And, in cellForRowAtIndexPath method:
GTLYouTubePlaylistItem *itemToDisplay = [self.vidInfos objectAtIndex:indexPath.row];
cell.textLabel.text = itemToDisplay.snippet.title;
The query accepts max 50 as the maximum result limit. But I need to display the entire list, which is about 250 videos.
How do i do it? I read about using pageTokens, but I couldn't find any sample or code on how to use pageTokens, where to get them and where to pass them?
A GTLYouTubeListResponse, that you receive after making a query, have an NSString property that is called nextPageToken.
This property indicates the 'address' of the 'next page', in case you have several 'pages' of search results,
(Meaning, the amount of search results is higher than the amount you've set in the maxResults property, which, as you've said, have a 50 results limit)
So, using your question as an example of 250 results total, you have 5 search results 'pages' of 50 search results on each 'page'.
GTLYouTubeQuery have a corresponding pageToken property, which 'tells' the query what 'page' of the results you want to receive.
Could be another ways to achieve that, but this was just at the top of my head
and I think this is pretty simple and straightforward way of achieving this,
Anyway, the example below uses your code to demonstrate how this property can be used.
IMPORTANT NOTE !!!
In the code below, I'm 'looping' through ALL of the search results,
This is just for illustration purposes,
In your app, you would probably also want to create your own custom limit,
So if the user searches for a general keyword, that have TONS of results, you won't try to fetch them all which, among other disadvantages, will probably be more than he will ever read, make an unnecessary network and memory usage, and will 'hog' your google developer points (or whatever it is called, when it 'costs' you to make google API calls)
So, if we'll use your original code-
// First, make GTLServiceYouTube a property, so you could access it thru out your code
#interface YourViewControllerName ()
#property (nonatomic, strong) GTLServiceYouTube *service;
#end
// don't forget to still initialise it in your viewDidLoad
self.vidInfos = [[NSMutableArray alloc] init];
service = [[GTLServiceYouTube alloc] init];
service.APIKey = #"my-api-key";
GTLQueryYouTube *query1 = [GTLQueryYouTube queryForPlaylistItemsListWithPart:#"id,snippet"];
query1.playlistId = #"the-playlist-id-from-utube-channel";
query1.maxResults = 50;
// After you've created the query, we will pass it as a parameter to our method
// that we will create below
[self makeYoutubeSearchWithQuery:query1];
// Here we create a new method that will actually make the query itself,
// We do that, so it'll be simple to call a new search query, from within
// our query's completion block
-(void)makeYoutubeSearchWithQuery:(GTLQueryYouTube *)query {
[service executeQuery:query completionHandler:^(GTLServiceTicket *ticket, id object, NSError *error) {
if (!error) {
GTLYouTubePlaylistItemListResponse *playlistItems = object;
for (GTLYouTubePlaylistItem *playlistItem in playlistItems) {
[self.vidInfos addObject:playlistItem];
}
[self.tableView reloadData];
}else {
NSLog(#"%#", error);
}
// Here we will check if our response, has a value in the nextPageToken
// property, meaning there are more search results 'pages'.
// If it is not nil, we will just set our query's pageToken property
// to be our response's nextPageToken, and will call this method
// again, but this time pass the 'modified' query, so we'll make a new
// search, with the same parameters as before, but that will ask the 'next'
// page of results for our search
// It is advised to add some sort of your own limit to the below if statement,
// such as '&& [self.vidInfos count] < 250'
if(playlistItems.nextPageToken) {
query.pageToken = playlistItems.nextPageToken;
[self makeYoutubeSearchWithQuery:query];
} else {
NSLog(#"No more pages");
}
}];
}
Good luck mate.
My english is little short. I attach Google Play Games to iPhone5. Everything is fine except leaderboard rank. When I attempt to get rank from leaderboard, It always return zero.
Below is my code. What is problem?
- (void)viewDidLoad {
[super viewDidLoad];
[GPGManager sharedInstance].statusDelegate = self;
}
- (IBAction)signInWasClicked:(id)sender {
[[GPGManager sharedInstance] signInWithClientID:CLIENT_ID silently:NO];
}
- (IBAction)submitScore: (UIButton *)sender {
GPGScore* myScore = [[GPGScore alloc] initWithLeaderboardId:LEADERBOARD_ID];
[myScore submitScoreWithCompletionHandler:^(GPGScoreReport *report, NSError *error){
if (error) {
// error
NSLog(#"ERROR");
} else {
// Success. Score retern fine. score return right value but rank is not.
NSLog(#"%#, %lld", report.highScoreForLocalPlayerAllTime.formattedScore,
report.highScoreForLocalPlayerAllTime.rank);
}
}];
}
In Google developer's "Leaderboard in iOS" section, there is no mention about leaderboard rank. But in GPGScoreReport object, there is GPGScore object and in GPGScore object, score and rank value are on it.
Help me please.
GPGLeaderboard *myLeaderboard = [GPGLeaderboard leaderboardWithId:LEADERBOARD_ID];
myLeaderboard.timeScope = GPGLeaderboardTimeScopeAllTime;
GPGLocalPlayerScore* localScore = [myLeaderboard localPlayerScore];
GPGLocalPlayerRank* localRank = [localScore publicRank];
NSLog(#"Current rank : %#", localRank.formattedRank);
I didn't try this code, but according to the class references, it should work fine. Let me know whether it works or not.
Also, put that code "after" submitting your score.
I am developing a board game. Using Game Center for multiplayer, but currently stuck at how to send or receive invitations of GKTurnBasedMatch. I am creating match programmatically using:
GKMatchRequest *request = [[GKMatchRequest alloc] init];
request.defaultNumberOfPlayers = 2;
[GKTurnBasedMatch findMatchForRequest:request
withCompletionHandler:
^(GKTurnBasedMatch *match, NSError *error) {
if(error) {
NSLog(#"%#", error.localizedDescription);
return;
}
[self.delegate startGameForMatch:match];
}];
The GKTurnBasedMatch instance in parameter of above block, has only local player with other player as nil and I need to display details of opponent in the game.
NSMutableArray *participantIds = [[NSMutableArray alloc] init];
NSLog(#"%#", match.participants);
for (GKTurnBasedParticipant *participant in match.participants) {
if(participant.playerID) [participantIds addObject:participant.playerID];
}
[GKPlayer loadPlayersForIdentifiers:participantIds
withCompletionHandler:^(NSArray *players, NSError *error) {
NSMutableString *string = [[NSMutableString alloc] init];
for (GKPlayer *player in players) {
[string appendFormat:#"---- Alias: %# DisplayName: %#", player.alias, player.displayName];
}
NSLog(#"%#", string);
}];
Am I missing something or Game Center works like this?
I read that participants of the match wont get invitations until that GKTurnBasedParticipant is GKTurnBasedMatch.currentParticipant but I need to display the details of opponent when game started.
Thanks for the help. Point me in correct direction.
This happens when you create a match against a random opponent. Try creating a second test user in iTunesConnect and signing into that user on a second device.
Then, send that second test player an invite to be your friend. This will allow you to more easily test your game with multiplayer features without having to wait for a random match to be found.
After the friend request is accepted, try creating a new game once more. Now, invite your 'Friend' to your game and start your turn. You will now notice that the (null) variables will - for the most part - be filled in. Something like this should now appear in your log-
GKTurnBasedParticipant 0xb9432e0 - playerID:G:123456789 status:Invited matchOutcome:None lastTurnDate:(null) timeoutDate:(null)
- (void) reportScore: (int64_t) score forLeaderboardID: (NSString*) identifier
{
GKScore *scoreReporter = [[GKScore alloc] initWithLeaderboardIdentifier: identifier];
scoreReporter.value = score;
scoreReporter.context = 0;
NSArray *scores = #[scoreReporter];
[GKLeaderboard reportScores:scores withCompletionHandler:^(NSError *error) {
//Do something interesting here.
}];
}
https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/GameKit_Guide/LeaderBoards/LeaderBoards.html
On the above site, I used the above code (and the title was Reporting a score to Game Center (iOS 7)) but on the GKLeaderboard reportScores... line, I get an error saying that there is no such method. How do i fix this without using GKScore's deprecated reportScoreWithCompletionHandlerMethod?
So apple's thing had a typo. GKLeaderboard was supposed to be GKScore in the reportScores line.
I want to implement ranking by using Game Center.
So , I implement like this .
-(void)authenticateLocalPlayer
{
GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer];
localPlayer.authenticateHandler = ^(UIViewController *vc,NSError *err){
[self setLastError:err];
if ([CCDirector sharedDirector].isPaused){
[[CCDirector sharedDirector] resume];
}
if (localPlayer.authenticated){
_gameCenterFeaturesEnabled = YES;
// get localplayer's score.
GKLeaderboard *board = [[GKLeaderboard alloc] init];
// make a query
board.timeScope = GKLeaderboardTimeScopeAllTime;
// I want to get all player's score.
board.playerScope = GKLeaderboardTimeScopeToday;
// set my game category.
board.category = #"com.nobinobiru.shooting";
// I want to show top 3 score data.
board.range = NSMakeRange(1, 3);
[board loadScoresWithCompletionHandler:^(NSArray *scores, NSError *error) {
NSString *s = [NSString stringWithFormat:#"%lld",board.localPlayerScore.value];
[ud setObject:[NSString stringWithFormat:#"%#",s] forKey:#"bestScore"];
if (scores){
// I want to 3 items but it returned only 1 item.
NSLog(#"score is %#",scores);
}
}];
}else if (vc){
[[CCDirector sharedDirector] pause];
[self presentViewController:vc];
}
};
}
Then, I create 3 sandbox's user account , and I test it.
But it always only show current user's best score.
I want to show 3 sandbox's data.
I don't know why it happened like that.
My code works well in not sandbox environment?
Do you have any idea?
Thanks in advance.
All.
It works perfectly after 6 hours...
I think that the reason why it happened is my created game center account was not reflect immediately.