System Overview

System Architecture

RakPeer.h provides the base functionality for all of RakNet. On start, RakPeer starts a thread that will process all incoming datagrams and send most outgoing datagrams. A shared memory array of RemoteSystem structures is shared between the user thread and the network thread. Messages are sent between these two threads using a fast single producer single consumer object. Each player has one entry in the array of RemoteSystem structures. Each element in the array has an instance of the ReliabilityLayer class, which handles reliable sends, ordering, and integrates with the message security classes. As user messages arrive, they are stored a single producer single consumer queue. When a user wants a message, by calling Receive(), this will return a pointer to one element of the queue. When a user is done with a message, by calling DeallocatePacket(), this will increment the write pointer of the queue. This is why you have to return messages in the same order as you get them.

Calling Receive() also does any user-thread based processing. One case of this is RakNet's plugin system. The plugin system is just an interface (PluginInterface.h) that has hooks inside RakPeer to perform certain functionality on certain events. The biggest two are Update() and the processing of packets in OnReceive(), both of which happen when the user calls RakPeer::Receive(). This has two advantages. First, you don't have to keep track of which plugin modules you are using. Second, if a plugin module has its own message types, those message types can be processed in the plugin and blocked from being returned from Receive(). Hence, your application can add modular functionality with no code changes beyond adding the plugin itself.

RakNet's messaging system works by treating the first byte of any message as an identifier indicating the format and meaning of the rest of the data in the message. The message id bytes used by RakNet are stored in the file MessageIdentifiers.h. Users that want to extend this system can start their own enumerations starting at the highest enumeration in MessageIdentifiers.h + 1. Users would then check the first byte of messages that are returned to them and call a function to handle the rest of the data in the message. To make this easier, RakNet supports remote function calls. Essentially, remote function calls perform this mapping for you by registering a string with a function pointer. You then call RakPeer::RPC with the name of the function and if the other side has that function registered, it will call the function pointer along with a structure containing data such as encoded parameters and who sent the message. This system has two further extensions. First, it supports return values by blocking in the Receive() call until a message is returned or until a timeout elapses. Second, it supports calling into object member functions, not just static C functions.

The NetworkIDObject class provides the ability for systems to refer to common objects and is used by object member remote function calls. It is a fairly simple system that has the server assign a number to objects as they are created. When a client creates an object, it is responsible for sending this event to the server, getting the number to use when the object is created on the server in turn, and then assigning this number to the object. This way systems can refer to objects on multiple systems which would otherwise be impossible since pointer addresses would probably not be the same. The easiest way to use this system is to derive from it. However, you can also include it as a member variable of your base class and call SetParent(parentClassPointer). The disadvantage of this architecture is that it relies on a single system being present to assign network IDs. This can be addressed by having NetworkIDs be a SystemAddress / NetworkID pair instead of a simple number. However, that takes more bandwidth which would be wasted in server/client topology, which happens to be the most common case. It is currently implemented using a single number.

The SystemAddress structure is what RakNet uses to represent remote systems. It is the binary encoding of the IP address along with the port of that system.

The BitStream class, located in BitStream.h, is natively supported by RakNet. This is both a user class and an internal class. It is primarily used to write single bits to a stream and for automatic endian swapping, which can be enabled by commenting out __BITSTREAM_NATIVE_END in RakNetDefines.h. It is disabled by default for speed, since most users do not need endian swapping.

Next page: Detailed Implementation

See Also

Index Introduction Detailed Implementation Tutorial Compiler Setup