How To Make a Multiplayer iPhone Game Hosted on Your Own Server Part 1

A while back, I wrote a tutorial on How To Make A Simple Multiplayer Game with Game Center. That tutorial showed you how to use Game Center to create a peer-to-peer match. This means that packets of game data were sent directly between the connected devies, and there was no central game server. However, sometimes […] By Ray Wenderlich.

Leave a rating/review
Save for later
Share

Create a simple multiplayer game hosted on your own server with Game Center matchmaking!

Create a simple multiplayer game hosted on your own server with Game Center matchmaking!

A while back, I wrote a tutorial on How To Make A Simple Multiplayer Game with Game Center.

That tutorial showed you how to use Game Center to create a peer-to-peer match. This means that packets of game data were sent directly between the connected devies, and there was no central game server.

However, sometimes you might want to create your own game server, and have your game clients connect to that rather than talking to each other directly. This can have the following advantages:

  • The maximum number of players in a peer-to-peer games is 4, but a hosted game can have up to 16.
  • With a central server, you can add neat features such as centrally storing data, dynamically modifying game state and performing calculations, metrics, and more.
  • With a central server, you have a built-in authority that can make important decisions and keep track of the official game state.

Even though there’s all these great advantages, there’s one big disadvantage – there’s not a heck of a lot of documentation on how to actually do this!

But never fear – a while back this won the weekly tutorial vote in the sidebar, so all you have to do is follow along! :]

Before going through this tutorial, you should do the following tutorials first (or have equivalent knowledge):

It’s also very helpful to have at least a basic familiarity with Python for this tutorial, since we’ll use it to construct the server. If you are completely new to Python, check out the official Python tutorial.

I realize this is a lot of prerequisites (plus this tutorial is super-long!), but making your own game server is not the easiest task in the world! :]

It is however a lot of fun to make your own server, and one of the best learning experiences you can have. So I encourage you all to grab some caffeine and have fun learning how to make your own game server with me!

Getting Started

We’re going to start out with the same starter code that we used in the Game Center peer-to-peer tutorial.

This way, you can focus on adding the code related to network programming rather than having to worry about creating a game too.

Plus, since it’s the same game we used in the Game Center peer-to-peer tutorial, you’ll get to see where we have to diverge to make it a hosted match instead.

So download the starter code and run the project, and you should see something like this:

Simple Cocos2D Racing Game Starter Code

Take a look through the project to refresh your memory how it works. It’s all pretty simple stuff – but just needs some networking code so the kid on the tricycle can have a fighting chance!

Authenticating with Game Center

Even though we’re going to host the game on our own server, we’re still going to authenticate the user with game center.

This keeps us from having to create an account system and authentication system for users on our own server. It’s also nice for users because they don’t have to sign into an account for our app in particular, plus they can easily invite people from their Game Center friends list.

So let’s start by getting the authentication code in. This should be review from the Game Center peer-to-peer tutorial.

First of all, open up Resources\Info.plist and set the Bundle identifier that you made previously for your app. For example, mine was “com.razeware.catrace”:

Setting the Bundle Identifier in your info.plist

Second, add the GameKit framework to your project. To do this, select the CatRace project in the upper left of Groups & Files, select the Build Phases tab, expand the “Link Binary with Libraries” section, and click the “+” button.

Select GameKit.framework, and click Add. Change the type from Required to Optional, and your screen should look like the following:

Adding Game Kit Framework to Xcode 4 Project

Next, let’s add the code to authenticate the local user with Game Center. This will be mostly review, with a bit of new stuff added in too.

Go to File\New\New File, choose iOS\Cocoa Touch\Objective-C class, and click Next. Enter NSObject for Subclass of, click Next, name the new class NetworkController.m, and click Save.

Open up NetworkController.h and replace it with the following:

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

typedef enum {
    NetworkStateNotAvailable,
    NetworkStatePendingAuthentication,
    NetworkStateAuthenticated,    
} NetworkState;

@protocol NetworkControllerDelegate
- (void)stateChanged:(NetworkState)state;
@end

@interface NetworkController : NSObject {
    BOOL _gameCenterAvailable;
    BOOL _userAuthenticated;
    id <NetworkControllerDelegate> _delegate;
    NetworkState _state;
}

@property (assign, readonly) BOOL gameCenterAvailable;
@property (assign, readonly) BOOL userAuthenticated;
@property (assign) id <NetworkControllerDelegate> delegate;
@property (assign, readonly) NetworkState state;

+ (NetworkController *)sharedInstance;
- (void)authenticateLocalUser;

@end

This class is similar to the GCHelper class from the Game Center peer-to-peer tutorial, but we renamed it to NetworkController because now it’s about more than just Game Center – it will manage communictation with our custom server as well.

Note that we have started creating an enum called NetworkState. This will keep track of the various states that we’ll pass through as we get set up for a match. Right now, we just have three states – Game Center not available, pending Game Center authentication, and authenticated with Game Center.

We also declare a protocol and a delegate so that we can notify a listener (our HelloWorldLayer in this case) when the network state changes. We’ll update the label at the top of the screen so we can easily tell what’s going on.

Next, switch to NetworkController.m and replace it with the following:

#import "NetworkController.h"

@implementation NetworkController
@synthesize gameCenterAvailable = _gameCenterAvailable;
@synthesize userAuthenticated = _userAuthenticated;
@synthesize delegate = _delegate;
@synthesize state = _state;

#pragma mark - Helpers

static NetworkController *sharedController = nil;
+ (NetworkController *) sharedInstance {
    if (!sharedController) {
        sharedController = [[NetworkController alloc] init];
    }
    return sharedController;
}

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

- (void)setState:(NetworkState)state {
    _state = state;
    if (_delegate) {
        [_delegate stateChanged:_state];
    }
}

#pragma mark - Init

- (id)init {
    if ((self = [super init])) {
        [self setState:_state];
        _gameCenterAvailable = [self isGameCenterAvailable];
        if (_gameCenterAvailable) {
            NSNotificationCenter *nc = 
            [NSNotificationCenter defaultCenter];
            [nc addObserver:self 
                   selector:@selector(authenticationChanged) 
                       name:GKPlayerAuthenticationDidChangeNotificationName 
                     object:nil];
        }
    }
    return self;
}

#pragma mark - Authentication

- (void)authenticationChanged {    
    
    if ([GKLocalPlayer localPlayer].isAuthenticated && !_userAuthenticated) {
        NSLog(@"Authentication changed: player authenticated.");
        [self setState:NetworkStateAuthenticated];
        _userAuthenticated = TRUE; 
    } else if (![GKLocalPlayer localPlayer].isAuthenticated && _userAuthenticated) {
        NSLog(@"Authentication changed: player not authenticated");
        _userAuthenticated = FALSE;
        [self setState:NetworkStateNotAvailable];
    }
    
}

- (void)authenticateLocalUser { 
    
    if (!_gameCenterAvailable) return;
    
    NSLog(@"Authenticating local user...");
    if ([GKLocalPlayer localPlayer].authenticated == NO) {     
        [self setState:NetworkStatePendingAuthentication];
        [[GKLocalPlayer localPlayer] authenticateWithCompletionHandler:nil];        
    } else {
        NSLog(@"Already authenticated!");
    }
}

@end

Read over this carefully and make sure you understand everything – it’s mostly review from last time.

The only new bit is the new setState method, that notifies the delegate of the network state change when it occurs (so it can update its label).

OK, now let’s make use of this! Open up AppDelegate.m and make the following changes:

// Add to top of file
#import "NetworkController.h"

// Add at top of applicationDidFinishLaunching
[[NetworkController sharedInstance] authenticateLocalUser];

This starts the authentication process as soon as possible when the app launches.

Next switch to HelloWorldLayer.h and make the following changes:

// Add to top of file
#import "NetworkController.h"

// Modify @interface declaration as to add NetworkControllerDelegate protocol
@interface HelloWorldLayer : CCLayer <NetworkControllerDelegate>

Finally, switch to HelloWorldLayer.m and make the following changes:

// Add at bottom of init
[NetworkController sharedInstance].delegate = self;
[self stateChanged:[NetworkController sharedInstance].state];

// Add new method right above dealloc
- (void)stateChanged:(NetworkState)state {
    switch(state) {
        case NetworkStateNotAvailable:
            debugLabel.string = @"Not Available";
            break;
        case NetworkStatePendingAuthentication:
            debugLabel.string = @"Pending Authentication";
            break;
        case NetworkStateAuthenticated:
            debugLabel.string = @"Authenticated";
            break;        
    }
}

This is just the code that updates the debug label based on the network state.

Compile and run, and you should see yourself get authenticated with Game Center!

User Authenticated with Game Center

If this doesn’t work right for you, it might be that an old version of the app is still cached somewhere. Go to your iOS simulator menu and choose “Reset Content and Settings” and quit the simulator, and hopefully it should work OK for you.

Contributors

Over 300 content creators. Join our team.