为什么iOS GameCenter多人实时匹配每次创建新线程?

时间:2012-12-12 13:47:17

标签: ios game-center multiplayer

我创建了绝对简单的多人游戏应用程序,它只找到匹配(只有1个对手)并在5秒后终止它:

-(void)matchmakerViewController:(GKMatchmakerViewController *)viewController 
                   didFindMatch:(GKMatch *)match {

    [self.presentingViewController dismissModalViewControllerAnimated:YES];

    self.match = match;
    self.match.delegate = self;

    if (!self.matchStarted && self.match.expectedPlayerCount == 0)
    {
        NSLog(@"Ready to start match!");
        //[self lookupPlayers];

        // My Own Test Code Begin

        if (!self.delegate) NSLog(@"No delegate on match invite.");

        // Notify delegate match can begin
        self.matchStarted = YES;
        [self.delegate matchStarted];

        // My Own Test Code End
    }
}

AppDelegate中的一些方法:

-(void)matchStarted
{
    CCLOG(@"Match started. Delay 5 seconds...");

    [self performSelector:@selector(matchEnded) withObject:nil afterDelay:5];
}

-(void)matchEnded
{
    CCLOG(@"Match ended");

    [[GameCenterMatchHelper sharedInstance].match disconnect];
    [GameCenterMatchHelper sharedInstance].match = nil;
}

第一场比赛一切正常。但是当匹配完成后,还有3个额外的线程(我可以看到它们暂停执行):

  1. 主题com.apple.gamekitservices.gcksession.recvproc
  2. 主题com.apple.gamekitservices.gcksession.sendproc
  3. 线程com.apple.gamekitservices.eventcallback.eventcbproc
  4. 如果我开始两场比赛 - 比赛结束后已经有6(3和3)个线程。 而且它之所以如此糟糕的主要原因是 - 应用程序崩溃,就像所有玩家都已断开连接一样。

    我使用iPod touch 4g和iPad 2进行最新iOS 6测试。这些线程都是在这两个设备上创建的。

    我认为这是因为我为GCHelper和所有委托使用单例类,但我尝试将委托提取到其他一次性使用的类 - 每次出现其他线程时。

    也许smb知道如何解决它?

2 个答案:

答案 0 :(得分:1)

I think this is helpful for you.


#import <Foundation/Foundation.h>
#import <GameKit/Gamekit.h>

@interface GCHelper : NSObject<GKMatchmakerViewControllerDelegate, GKMatchDelegate>
{
    BOOL isUserAuthenticated;

    UIViewController *presentingViewController;
    GKMatch *match;
    BOOL matchStarted;

    GKInvite *pendingInvite;
    NSArray *pendingPlayersToInvite;
    NSMutableDictionary *playersDict;

    NSString *MultiplayerID;
    NSData *MultiData;
    NSString *otherPlayerID;

    char AlertMessageBoxNo;

    BOOL isDataRecieved;
}

//variables

@property (assign, readonly) BOOL gameCenterAvailable;
@property (retain) UIViewController *presentingViewController;
@property (retain) GKMatch *match;
@property (retain) GKInvite *pendingInvite;
@property (retain) NSArray *pendingPlayersToInvite;
@property (retain) NSMutableDictionary *playersDict;

@property (retain) NSString *MultiplayerID;
@property (retain) NSData *MultiData;

-(NSString*)getOtherPlayerId;
-(void)setOtherPlayerId;
//Functions
+ (GCHelper *)sharedInstance;
-(BOOL)isGameCenterAvailable;
-(void)authenticationChanged;
-(void)authenticateLocalUser;

-(void)gameOver:(NSString*)message;

-(void)setDataRecieved:(BOOL)d;
-(BOOL)getDataRecieved;


- (void)findMatchWithMinPlayers:(int)minPlayers maxPlayers:(int)maxPlayers viewController:(UIViewController *)viewController;

@end

/////////


#import "GCHelper.h"
#import "IPadSharebleClass.h"


@implementation GCHelper

@synthesize gameCenterAvailable;
@synthesize presentingViewController;
@synthesize match;
@synthesize pendingInvite;
@synthesize pendingPlayersToInvite;
@synthesize playersDict;
@synthesize MultiData;
@synthesize MultiplayerID;

static GCHelper *sharedHelper = nil;

+(GCHelper *) sharedInstance
{
    if (!sharedHelper)
    {
        sharedHelper = [[GCHelper alloc] init];
    }
    return sharedHelper;
}


- (BOOL)isGameCenterAvailable
{
    Class gcClass = (NSClassFromString(@"GKLocalPlayer"));
    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];
        }
        else
        {
            UIAlertView* alert=[[UIAlertView alloc]initWithTitle:@"Game Center Alert" message:@"Game Center Not Available" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
            [alert show];
            [alert release];
        }
    }
    return self;
}

-(void)authenticationChanged
{
    if ([GKLocalPlayer localPlayer].isAuthenticated && !isUserAuthenticated)
    {
        NSLog(@"Authentication changed: player authenticated.");
        isUserAuthenticated = TRUE;

        [GKMatchmaker sharedMatchmaker].inviteHandler = ^(GKInvite *acceptedInvite, NSArray *playersToInvite)
        {
            NSLog(@"Received invite");
            self.pendingInvite = acceptedInvite;
            self.pendingPlayersToInvite = playersToInvite;
            IPadCallAnyWhereF.inviteReceived();
        };

    }
    else if (![GKLocalPlayer localPlayer].isAuthenticated && isUserAuthenticated)
    {
        NSLog(@"Authentication changed: player not authenticated");
        isUserAuthenticated = FALSE;
    }


}

- (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
{
    if (!gameCenterAvailable) return;

    matchStarted = NO;
    self.match = nil;
    self.presentingViewController = viewController;
    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
- (void)matchmakerViewControllerWasCancelled:(GKMatchmakerViewController *)viewController
{
    [presentingViewController dismissModalViewControllerAnimated:YES];

    UIAlertView* alert=[[UIAlertView alloc]initWithTitle:@"Game Center Alert" message:@"Game Cancel By you" delegate:self cancelButtonTitle:@"Try Again" otherButtonTitles:@"Main Menu", nil];
    [alert show];
    [alert release];
    AlertMessageBoxNo='E';
}

- (void)matchmakerViewController:(GKMatchmakerViewController *)viewController didFailWithError:(NSError *)error
{
    [presentingViewController dismissModalViewControllerAnimated:YES];
    NSLog(@"Error finding match: %@", error.localizedDescription);
    UIAlertView* alert=[[UIAlertView alloc]initWithTitle:@"Game Center Alert" message:@"Connection Time out" delegate:self cancelButtonTitle:@"Try Again" otherButtonTitles:@"Main Menu", nil];
    [alert show];
    [alert release];
    AlertMessageBoxNo='A';
}


- (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];
    }
}

- (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;
             //IPadCallAnyWhereF.matchEnded();
             UIAlertView* alert=[[UIAlertView alloc]initWithTitle:@"Game Center Alert" message:@"Error retrieving player info" delegate:self cancelButtonTitle:@"Try Again" otherButtonTitles:@"Main Menu", nil];
             [alert show];
             [alert release];
             AlertMessageBoxNo='F';
         }
         else
         {
             self.playersDict = [NSMutableDictionary dictionaryWithCapacity:players.count];
             for (GKPlayer *player in players)
             {
                 NSLog(@"Found player: %@", player.alias);
                 [playersDict setObject:player forKey:player.playerID];
             }
             NSLog(@"Total Number of Players : %d",players.count);
             matchStarted = YES;
             IPadCallAnyWhereF.matchStarted();
         }
     }];

}

#pragma mark GKMatchDelegate
- (void)match:(GKMatch *)theMatch didReceiveData:(NSData *)data fromPlayer:(NSString *)playerID
{
    if (match != theMatch) return;

    MultiData=data;
    MultiplayerID=playerID;
    if(otherPlayerID==nil)
    {
        otherPlayerID=[playerID retain];
    }
    IPadCallAnyWhereF.match();
}

-(void)setDataRecieved:(BOOL)d
{
    isDataRecieved=d;
}
-(BOOL)getDataRecieved
{
    return isDataRecieved;
}


-(NSString*)getOtherPlayerId
{
    return otherPlayerID;
}

-(void)setOtherPlayerId
{
    otherPlayerID=nil;
}

- (void)match:(GKMatch *)theMatch player:(NSString *)playerID didChangeState:(GKPlayerConnectionState)state
{
    if (match != theMatch) return;
    switch (state)
    {
        case GKPlayerStateConnected:
            NSLog(@"New Player connected!");
            if (!matchStarted && theMatch.expectedPlayerCount == 0)
            {
                NSLog(@"&&&&&&&&&& Ready to start match in the match!");
                [self lookupPlayers];
            }
            break;
        case GKPlayerStateDisconnected:
            NSLog(@"--------Player disconnected!--------");
            matchStarted = NO;
            UIAlertView* alert=[[UIAlertView alloc]initWithTitle:@"Game Center Alert" message:@"Player Disconnected" delegate:self cancelButtonTitle:@"Try Again" otherButtonTitles:@"Main Menu", nil];
            [alert show];
            [alert release];
            AlertMessageBoxNo='B';
            //IPadCallAnyWhereF.matchDisconnect();
            break;
    }
}

- (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;
    //IPadCallAnyWhereF.matchEnded();
    UIAlertView* alert=[[UIAlertView alloc]initWithTitle:@"Game Center Alert" message:@"Failed to connect to player" delegate:self cancelButtonTitle:@"Try Again" otherButtonTitles:@"Main Menu", nil];
    [alert show];
    [alert release];
    AlertMessageBoxNo='C';

}

- (void)match:(GKMatch *)theMatch didFailWithError:(NSError *)error
{
    if (match != theMatch) return;

    NSLog(@"Match failed with error: %@", error.localizedDescription);
    matchStarted = NO;
    //IPadCallAnyWhereF.matchEnded();
    UIAlertView* alert=[[UIAlertView alloc]initWithTitle:@"Game Center Alert" message:@"Match failed" delegate:self cancelButtonTitle:@"Try Again" otherButtonTitles:@"Main Menu", nil];
    [alert show];
    [alert release];
    AlertMessageBoxNo='D';

}

-(void)gameOver:(NSString*)message
{
    UIAlertView* alert=[[UIAlertView alloc]initWithTitle:@"Game Center Alert" message:message delegate:self cancelButtonTitle:@"Try Again" otherButtonTitles:@"Main Menu", nil];
    [alert show];
    [alert release];
    AlertMessageBoxNo='G';
}

-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
    NSString *title = [alertView buttonTitleAtIndex:buttonIndex];

    if([title isEqualToString:@"Try Again"])
    {
        IPadCallAnyWhereF.matchDisconnect();
    }
    else if([title isEqualToString:@"Main Menu"])
    {
        IPadCallAnyWhereF.gotoMainMenu();
    }

}



@end

答案 1 :(得分:0)

正确的答案是XCode没有显示真实的线程图片。它显示了很多不同的线程,但如果我试图在profiler中看到它们,那么我理解没有其他线程。所以我应该建议,线程没问题,但调试过程有问题。作为一个主题启动者,我认为它是封闭的。 谢谢大家。