Connecting¶
After starting MafiaNet, you can connect to remote systems.
Initiating a Connection¶
ConnectionAttemptResult Connect(
const char *host,
unsigned short remotePort,
const char *passwordData,
int passwordDataLength,
PublicKey *publicKey = 0,
unsigned connectionSocketIndex = 0,
unsigned sendConnectionAttemptCount = 6,
unsigned timeBetweenSendConnectionAttemptsMS = 1000,
TimeMS timeoutTime = 0
);
Basic usage:
MafiaNet::ConnectionAttemptResult result = peer->Connect(
"192.168.1.100", // IP address or hostname
60000, // Port
nullptr, // No password
0 // Password length
);
if (result != MafiaNet::CONNECTION_ATTEMPT_STARTED) {
printf("Failed to initiate connection: %d\n", result);
}
Connection Parameters¶
host¶
IP address (e.g., “192.168.1.100”) or hostname (e.g., “game.example.com”).
remotePort¶
The port the remote system is listening on (set in their Startup() call).
passwordData / passwordDataLength¶
Optional password for authentication:
const char* password = "secretpassword";
peer->Connect("192.168.1.100", 60000, password, strlen(password));
The remote system must call SetIncomingPassword() with the same password.
sendConnectionAttemptCount / timeBetweenSendConnectionAttemptsMS¶
Control retry behavior. Default: 6 attempts, 1 second apart.
timeoutTime¶
How long to wait for connection before giving up (milliseconds). 0 = use default.
Handling Connection Results¶
Check for connection packets in your receive loop:
MafiaNet::Packet* packet;
for (packet = peer->Receive(); packet;
peer->DeallocatePacket(packet), packet = peer->Receive()) {
switch (packet->data[0]) {
case ID_CONNECTION_REQUEST_ACCEPTED:
printf("Connected to %s\n",
packet->systemAddress.ToString());
break;
case ID_CONNECTION_ATTEMPT_FAILED:
printf("Connection attempt failed\n");
break;
case ID_NO_FREE_INCOMING_CONNECTIONS:
printf("Server is full\n");
break;
case ID_INVALID_PASSWORD:
printf("Wrong password\n");
break;
case ID_ALREADY_CONNECTED:
printf("Already connected to this system\n");
break;
case ID_CONNECTION_BANNED:
printf("Banned from server\n");
break;
}
}
Accepting Connections (Server)¶
To accept incoming connections:
// Must be ≤ maxConnections from Startup()
peer->SetMaximumIncomingConnections(100);
Optionally set a password:
const char* password = "secretpassword";
peer->SetIncomingPassword(password, strlen(password));
Handle new connections:
case ID_NEW_INCOMING_CONNECTION:
printf("New client connected: %s\n",
packet->systemAddress.ToString());
break;
Disconnecting¶
Graceful disconnect:
peer->CloseConnection(systemAddress, true); // true = send notification
The remote system receives ID_DISCONNECTION_NOTIFICATION.
Disconnect with a reason¶
A graceful disconnect can carry an optional payload so the remote peer learns
why it was dropped (for example a kick/ban reason). Pass a BitStream as
the final reasonData argument to CloseConnection; its bytes are appended
right after the ID_DISCONNECTION_NOTIFICATION message ID:
enum KickReason : uint8_t { KR_BANNED, KR_KICKED, KR_SERVER_FULL };
MafiaNet::BitStream reason;
reason.Write((uint8_t)KR_KICKED); // an enum code...
MafiaNet::RakString("Cheating in match #4821") // ...plus an optional custom string
.Serialize(&reason);
peer->CloseConnection(systemAddress, true, 0, MafiaNet::Priority::Low, &reason);
The receiver reads the reason from the notification packet exactly like any
other message body — packet->data + 1 for packet->length - 1 bytes:
case ID_DISCONNECTION_NOTIFICATION:
if (packet->length > 1) {
MafiaNet::BitStream in(packet->data + 1, packet->length - 1, false);
uint8_t code;
MafiaNet::RakString text;
in.Read(code);
in.Deserialize(&text);
// show "Kicked: <text>", "You were banned.", etc.
}
break;
Note
Only graceful disconnects carry a reason. Locally-synthesized
notifications — ID_CONNECTION_LOST and the timeout/dead-connection path
that also surfaces as ID_DISCONNECTION_NOTIFICATION — have no remote
sender and stay payload-less, so always tolerate a zero-length body
(packet->length == 1). Appending bytes after the ID is
wire-backward-compatible: peers that only inspect packet->data[0] are
unaffected, and you can send a single enum byte if you want minimal overhead.
Forceful disconnect (no notification):
peer->CloseConnection(systemAddress, false);
The remote system will eventually get ID_CONNECTION_LOST.
Connection Events Summary¶
Message ID |
Description |
|---|---|
|
Successfully connected (client) |
|
New client connected (server) |
|
Could not connect |
|
Server is full |
|
Wrong password |
|
Clean disconnect |
|
Connection timed out |
|
IP is banned |
Checking Connection State¶
MafiaNet::ConnectionState state = peer->GetConnectionState(systemAddress);
switch (state) {
case MafiaNet::IS_CONNECTED:
// Fully connected
break;
case MafiaNet::IS_CONNECTING:
// Connection in progress
break;
case MafiaNet::IS_DISCONNECTING:
// Disconnecting
break;
case MafiaNet::IS_NOT_CONNECTED:
// Not connected
break;
}
The Session Handshake¶
After the transport connection is established but before either side reports it, MafiaNet
exchanges an opaque application payload in both directions. The connecting peer sends its payload in
ID_SESSION_CONFIG_REQUEST; the accepting peer answers with ID_SESSION_CONFIG.
Neither ID_CONNECTION_REQUEST_ACCEPTED nor ID_NEW_INCOMING_CONNECTION is produced until that
exchange completes. Those packets therefore mean “the remote peer’s session payload is in hand” –
an application cannot observe a connection without its session data, and there is no window in which
it must remember to check.
The payload is opaque to MafiaNet: encode it however the application likes, up to
MAXIMUM_SESSION_CONFIG_SIZE bytes.
Warning
A payload received from a remote peer is untrusted, unauthenticated input – it arrives before any application-level authentication has run. Parse it as defensively as any other packet from an unknown source, and never put secrets in a payload sent from static mode. See Session Handshake Security.
Static payloads¶
The common case needs no application code beyond staging the bytes:
// Server: published to every client that connects
server->SetSessionConfig(configJson.c_str(), (unsigned int)configJson.size());
// Client: sent up with the connection request
client->SetSessionConfig(buildToken.c_str(), (unsigned int)buildToken.size());
Read the remote payload as soon as the connection surfaces:
case ID_CONNECTION_REQUEST_ACCEPTED: {
unsigned int length = 0;
const char *config = client->GetRemoteSessionConfig(packet->guid, &length);
// config is already available here -- that is the point of the handshake
break;
}
Per-client decisions¶
A server that needs to inspect each client before answering turns on interactive mode. The client’s payload arrives first, so the server can refuse without ever having disclosed its own:
server->SetSessionConfigInteractive(true);
case ID_SESSION_CONFIG_REQUEST:
if (BuildTokenMatches(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;
While the decision is outstanding the connection stays unreported on both sides.
RejectSession() produces ID_CONNECTION_ATTEMPT_FAILED on the client with the reason string at
packet->data + 1, and no connection is ever reported anywhere.
A peer that is never answered is timed out like any other incomplete connection attempt, using the
timeout set by MafiaNet::RakPeerInterface::SetTimeoutTime(), or the one set by
MafiaNet::RakPeerInterface::SetSessionTimeout() when there is one. During the exchange
GetConnectionState() reports IS_CONNECTING. If it goes away before it is answered, the server
receives ID_SESSION_CONFIG_ABANDONED for it.
Holding peers while deciding¶
A decision that takes time – a database lookup, a queue – should not cost a player slot. With a pending pool, peers still in the handshake are counted against a budget of their own instead of against the incoming limit, which then bounds connected peers only:
server->SetSessionConfigInteractive(true);
server->SetMaximumPendingSessions(32, 4); // 32 may wait, at most 4 from one IP
server->SetSessionTimeout(45000); // how long an answer may take
server->Startup(maxPlayers + 32, &sd, 1); // room for both
server->SetMaximumIncomingConnections(maxPlayers);
// While deciding, tell the peer what is happening; this also restarts the timeout on both ends.
server->SendSessionStatus(guid, "You are 3rd in the queue", 24);
A handshake may start while the server is full, which is what makes a queue possible. An
AcceptSession() that would exceed the incoming limit refuses the peer instead, so check
NumberOfConnections() before accepting and refuse with a reason of your own. The client should
call SetSessionTimeout() too, generously: whichever end gives up first ends the attempt.
Admission and Queues walks through a complete queue built on this.
Note
The session handshake changes the wire protocol, so RAKNET_PROTOCOL_VERSION was raised to 7.
Peers built against an older MafiaNet are rejected during the offline connection phase with
ID_INCOMPATIBLE_PROTOCOL_VERSION.
See Also¶
Startup - Starting MafiaNet
Admission and Queues - Admission checks and queues before a connection exists
Debugging Disconnects - Troubleshooting connection issues