Client-Server Architecture

The client-server model is the most common networking pattern for games.

Server Setup

MafiaNet::RakPeerInterface* server = MafiaNet::RakPeerInterface::GetInstance();

// Bind to port 60000
MafiaNet::SocketDescriptor sd(60000, 0);

// Start with capacity for 100 clients
MafiaNet::StartupResult result = server->Startup(100, &sd, 1);
if (result != MafiaNet::RAKNET_STARTED) {
    printf("Server failed to start: %d\n", result);
    return -1;
}

// Allow incoming connections
server->SetMaximumIncomingConnections(100);

Client Setup

MafiaNet::RakPeerInterface* client = MafiaNet::RakPeerInterface::GetInstance();

MafiaNet::SocketDescriptor sd;
client->Startup(1, &sd, 1);

// Connect to server
MafiaNet::ConnectionAttemptResult result =
    client->Connect("game.example.com", 60000, nullptr, 0);

if (result != MafiaNet::CONNECTION_ATTEMPT_STARTED) {
    printf("Failed to initiate connection\n");
}

Handling Events

Common network events to handle:

void ProcessPackets(MafiaNet::RakPeerInterface* peer) {
    MafiaNet::Packet* packet;
    for (packet = peer->Receive(); packet;
         peer->DeallocatePacket(packet), packet = peer->Receive()) {

        switch (packet->data[0]) {
            // Connection events
            case ID_NEW_INCOMING_CONNECTION:  // Server: new client connected
                OnClientConnected(packet->systemAddress);
                break;

            case ID_CONNECTION_REQUEST_ACCEPTED:  // Client: connected to server
                OnConnectedToServer();
                break;

            case ID_CONNECTION_ATTEMPT_FAILED:
                OnConnectionFailed();
                break;

            case ID_NO_FREE_INCOMING_CONNECTIONS:
                OnServerFull();
                break;

            // Disconnection events
            case ID_DISCONNECTION_NOTIFICATION:
                OnDisconnected(packet->systemAddress);
                break;

            case ID_CONNECTION_LOST:
                OnConnectionLost(packet->systemAddress);
                break;

            // Custom game messages
            default:
                if (packet->data[0] >= ID_USER_PACKET_ENUM) {
                    ProcessGameMessage(packet);
                }
                break;
        }
    }
}

Broadcasting to All Clients

void BroadcastToAllClients(MafiaNet::RakPeerInterface* server,
                           MafiaNet::BitStream* bs) {
    server->Send(bs, MafiaNet::Priority::High, MafiaNet::Reliability::ReliableOrdered, 0,
                 MafiaNet::UNASSIGNED_SYSTEM_ADDRESS, true);
}

Sending to Specific Client

void SendToClient(MafiaNet::RakPeerInterface* server,
                  MafiaNet::BitStream* bs,
                  MafiaNet::SystemAddress client) {
    server->Send(bs, MafiaNet::Priority::High, MafiaNet::Reliability::ReliableOrdered, 0, client, false);
}

Per-Server Configuration

A server usually has state a client needs before it can do anything useful – game mode, map, content version. The session handshake carries it as part of connecting, so the client has it the moment the connection is reported rather than one round trip later:

// Server, before Startup()
server->SetSessionConfig(configJson.c_str(), (unsigned int)configJson.size());

// Client
case ID_CONNECTION_REQUEST_ACCEPTED: {
    unsigned int length = 0;
    const char *config = client->GetRemoteSessionConfig(packet->guid, &length);
    // Already available -- the packet is not produced until the payload has arrived.
    break;
}

To validate each client before answering (build token, protocol version, ban list), turn on interactive mode and answer per peer. The client’s payload arrives first, so a peer can be refused without the server disclosing anything:

server->SetSessionConfigInteractive(true);

case ID_SESSION_CONFIG_REQUEST:
    if (BuildMatches(packet->data + 1, packet->length - 1))
        server->AcceptSession(packet->guid, configJson.c_str(), (unsigned int)configJson.size());
    else
        server->RejectSession(packet->guid, "build mismatch");
    break;

A refused peer never appears as a connection on either side. See Connecting for the full flow and Session Handshake Security for what the payload can and cannot be trusted to contain.

Connection Timeout

Configure connection timeout behavior:

// Set timeout to 10 seconds
peer->SetTimeoutTime(10000, MafiaNet::UNASSIGNED_SYSTEM_ADDRESS);