Tutorial : RakNet Replica3 on the iPhone

Sorry this took a while getting out, it took a bit longer than expected to finish.

One of the more useful features of RakNet, particularly for games, is the ReplicaManager plugin. This piece of code handles “replication” of objects, ensuring that all clients have the same state as the server. For example, in some of my previous posts I demonstrated the feature by running a physics simulation on a server and viewing the scene on several connecting clients. Replica is very scaleable and allows for fine-grained control over what aspects of an object are transmitted over the network, how reliable the arrival order is, etc. This tutorial will only cover a basic client-side iPhone integration based on the Replica sample provided with RakNet; more details on the specifics of using the system can be found in the RakNet forums and documentation section.

I’m going to assume that you’ve looked at the previous tutorials using RakNet, and have at least compiled an iOS RakNet library. We’ll need to use that to compile the application in this tutorial.

The first step is to create a new “View-based Application” project for the iPhone. You can name it whatever you want, but I’m using RaknetReplicaSample and this name may appear in some screenshots.

The original RakNet sample was contained in a single .cpp file that would run both  a client server, making things a bit confusing to work with. I’ve split the example into several files and added a logger class that will print to a UITextView, which makes things a bit easier on the iOS platform. These files, along with a compiled version of the RakNet sample for OS X, can be downloaded here.

Import these files into your newly created Xcode project and add them to the Classes folder. There are still some hooks to add to the ViewController and AppDelegate files. These are similar to the ones made for the previous tutorial on setting up a RakNet chat client on the iPhone, but there are a few key differences in the ViewController.

We’ll start off with the AppDelegate though. Like last time, we need to setup an NSTimer to run our network loop. Add a new NSTimer to the AppDelegate.h file at the end of the @interface declaration:

NSTimer * mTimer;

And a new method to actually call the network update function:

-(void) networkUpdate: (NSTimer *) pTimer;

The interface should look something like this:

In the corresponding implementation file, <ProjectName>AppDelegate.m, add the following line at the end of the application:didFinishLaunching: method:

mTimer = [NSTimer scheduledTimerWithTimeInterval: 0.1 target:self selector:@selector(networkUpdate:) userInfo:nil repeats: YES];

Then, add an implementation for the networkUpdate method:

-(void) networkUpdate: (NSTimer *) pTimer
{
	[self.viewController tickClient];
}

That’s all that needs to be done in AppDelegate. In theViewController.h, add the following includes to the top of the file, directly after UIKit.h. Please keep them in this order as well:

#include "SampleReplica.h"
#include "SampleConnection.h"
#include "SampleReplicaManager.h"
#include "Logger.h"

Then add the following items to the @interface declaration:

	NetworkIDManager networkIdManager;
	RakPeerInterface *rakPeer;
	ReplicaManager3Sample replicaManager;
	SocketDescriptor sd;
	Packet *packet;

	UITextView* mTextView;

This is similar to the chat tutorial, but now we had a new “ReplicaManager” instance. This class and its super class handle much of the work behind the object replication process.

We’ll also need to add the following lines after the closing brace on the @interface declaration:

-(void)tickClient;

@property (nonatomic,retain) IBOutlet UITextView* mTextView;

In the implementation file for the ViewController, we first need to @synthesize  the UITextView by adding the following line after the @implementation declaration:

@synthesize mTextView;

In the viewDidLoad method, add the following block of code after the call to the super class:

	Logger::getLogger()->setTextView(mTextView);

	sd.socketFamily=AF_INET;
	sd.port=0;

	rakPeer = RakNet::RakPeerInterface::GetInstance();
	rakPeer->Startup(32,&sd,1);
	rakPeer->AttachPlugin(&replicaManager);
	replicaManager.SetNetworkIDManager(&networkIdManager);
	rakPeer->SetMaximumIncomingConnections(32);	

	rakPeer->Connect("192.168.2.17",12345,0,0,0);

Just like the chat client example, the IP address can and should be changed to suit the environment you’re using. The port needs to remain as it is, since the RakNetReplicaSample server is hardcoded to use this port. The port shouldn’t be a problem for anyone.

Next, add the tickClient method. This method is called by our NSTimer once every 10th of a second and updates the network:

-(void)tickClient
{
	for (packet = rakPeer->Receive(); packet; rakPeer->DeallocatePacket(packet), packet = rakPeer->Receive())
	{
		switch (packet->data[0])
		{
			case ID_CONNECTION_ATTEMPT_FAILED:
				Logger::getLogger()->appendLine(@"Connection failed\n");
				break;
			case ID_NO_FREE_INCOMING_CONNECTIONS:
				Logger::getLogger()->appendLine(@"Connection failed - No free incoming connections\n");
				break;
			case ID_CONNECTION_REQUEST_ACCEPTED:
				Logger::getLogger()->appendLine(@"Connection request accepeted\n");
				break;
			case ID_DISCONNECTION_NOTIFICATION:
				Logger::getLogger()->appendLine(@"Disconnecting\n");
				break;
			case ID_CONNECTION_LOST:
				Logger::getLogger()->appendLine(@"Connection lost\n");
				break;
			case ID_SND_RECEIPT_LOSS:
			case ID_SND_RECEIPT_ACKED:
			{
				uint32_t msgNumber;
				memcpy(&msgNumber, packet->data+1, 4);

				DataStructures::Multilist replicaListOut;
				replicaManager.GetReplicasCreatedByMe(replicaListOut);
				DataStructures::DefaultIndexType idx;
				for (idx=0; idx < replicaListOut.GetSize(); idx++) 
				{ 					
                                      ((SampleReplica*)replicaListOut[idx])->NotifyReplicaOfMessageDeliveryStatus(packet->guid,msgNumber, packet->data[0]==ID_SND_RECEIPT_ACKED);
				}
			}
				break;
		}
	}
}

Finally, add two lines of clean-up to the viewDidUnload method to disconnect from the network:

	rakPeer->Shutdown(100,0);
	RakNet::RakPeerInterface::DestroyInstance(rakPeer);

Like last time, we also need to tell the compiler that this project will contain some C++ code. This means that all the .m files need to be renamed to .mm, or set to compile as Objective-C++ by right clicking them, going to Get Info, and changing the File Type field to Objective-C++:

That should cover all of the code changes needed. You’ll also need to add a new UITextView to the interface builder and link it to the UITextView created in the ViewController class. This will be used to display output from the server and any connect/disconnect messages.

Like last time, you’ll need to include your RakNet library and add the RakNet source directory to your header search path. At this point, everything should be ready to compile.

To test the app, first start the provided OS X Replica application. Since this application is taken straight from RakNet, it can run as a client, server or peer-to-peer instance. Start it as a server by entering ‘s’ into the console. You can create new objects by pressing ‘c’, and assign them random values with ‘r’. Delete all created objects with the ‘d’ key. All three of these actions will be replicated on the client, and can be verified by checking the output UITextView on the iPhone app.

As always, I’m happy to help sort out any issues that come up with the tutorial. Let me know in the comments if you need anything, or if there are any other specific RakNet samples that you want to see ported to the iPhone.

T

Advertisements

3 responses to “Tutorial : RakNet Replica3 on the iPhone

  • pystone

    Wow! Taylor, you’re so great. I’ve been looking for the tutorial of Raknet for days and found nothing.
    I’ve been working on a ios game and now i need to add the network part.
    I’m now working on the server part of it, which is based on the sample from Raknet, Lobby2Server.
    But I cannot connect to the PostgreSQL and the console always says that “Database connection failed” though I have run the PosegreSQL on Windows XP(yes, I want to run the server on windows because I can’t manage to run it successfully on Linux…). Could you please give me a little help? Thanks a lot.

  • Taylor Petrick

    Sure, I can take a look at it this afternoon. One suggestion though is to check that your router is setup correctly. You’ll need to make sure proper port forwarding is in place based on what you’re using in your application, for example.

  • pystone

    Sorry, but i’ve been working on some other stuff these days so that i noticed your reply just now.
    When i run the Lobby2Server with the Lobby2Client connecting to it, the Lobby2Server says “ERROR: role “postgres” does not exist”. I think i find the key to the problem. I’m now turning to the tutorial of PostgreSQL for help. Thank you very much anyhow.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: