Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 7 additions & 31 deletions Samples/ChatExample/Client/Chat Example Client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "mafianet/MessageIdentifiers.h"

#include "mafianet/peerinterface.h"
#include "mafianet/PeerHandle.h"
#include "mafianet/statistics.h"
#include "mafianet/types.h"
#include "mafianet/BitStream.h"
Expand All @@ -47,24 +48,19 @@
#include "mafianet/SecureHandshake.h" // Include header for secure handshake
#endif

// We copy this from Multiplayer.cpp to keep things all in one file for this example
unsigned char GetPacketIdentifier(MafiaNet::Packet *p);

int main(void)
{
MafiaNet::RakNetStatistics *rss;
// Pointers to the interfaces of our server and client.
// Note we can easily have both in the same program
MafiaNet::RakPeerInterface *client= MafiaNet::RakPeerInterface::GetInstance();
MafiaNet::Peer peer;
MafiaNet::RakPeerInterface *client = peer.get();
// client->InitializeSecurity(0,0,0,0);
//MafiaNet::PacketLogger packetLogger;
//client->AttachPlugin(&packetLogger);


// Holds packets
MafiaNet::Packet* p;

// GetPacketIdentifier returns this
// Holds the packet identifier
unsigned char packetIdentifier;

// Just so we can remember where the packet came from
Expand Down Expand Up @@ -274,10 +270,10 @@ int main(void)

// Get a packet from either the server or the client

for (p=client->Receive(); p; client->DeallocatePacket(p), p=client->Receive())
for (MafiaNet::PacketPtr p = peer.receive(); p; p = peer.receive())
{
// We got a packet, get the identifier with our handy function
packetIdentifier = GetPacketIdentifier(p);
// We got a packet, get the identifier (ID_TIMESTAMP-aware)
packetIdentifier = p.id();

// Check if this is a network message packet
switch (packetIdentifier)
Expand Down Expand Up @@ -344,25 +340,5 @@ int main(void)
// Be nice and let the server know we quit.
client->Shutdown(300);

// We're done with the network
MafiaNet::RakPeerInterface::DestroyInstance(client);

return 0;
}

// Copied from Multiplayer.cpp
// If the first byte is ID_TIMESTAMP, then we want the 5th byte
// Otherwise we want the 1st byte
unsigned char GetPacketIdentifier(MafiaNet::Packet *p)
{
if (p==0)
return 255;

if ((unsigned char)p->data[0] == ID_TIMESTAMP)
{
RakAssert(p->length > sizeof(MafiaNet::MessageID) + sizeof(MafiaNet::Time));
return (unsigned char) p->data[sizeof(MafiaNet::MessageID) + sizeof(MafiaNet::Time)];
}
else
return (unsigned char) p->data[0];
}
1 change: 1 addition & 0 deletions Samples/Tests/IncludeAllTests.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,5 @@
#include "DisconnectReasonTest.h"
#include "PeerGuidTest.h"
#include "PointGridSectorizerTest.h"
#include "PeerHandleTest.h"

230 changes: 230 additions & 0 deletions Samples/Tests/PeerHandleTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
/*
* Copyright (c) 2026, MafiaHub
*
* This source code is licensed under the MIT-style license found in the
* license.txt file in the root directory of this source tree.
*/

#include "PeerHandleTest.h"

#include <utility> // std::move

// A distinct port so a full-suite run does not collide with other tests.
static const unsigned short PEERHANDLE_TEST_PORT = 61015;

// A user message id carried behind an ID_TIMESTAMP prefix, used to exercise the
// timestamp-aware branch of PacketPtr::id().
static const unsigned char PEERHANDLE_TS_ID = (unsigned char) (ID_USER_PACKET_ENUM + 7);

int PeerHandleTest::RunTest(DataStructures::List<RakString> params, bool isVerbose, bool noPauses)
{
(void) params; (void) noPauses;

// --- 1. Peer: movable, non-copyable, moved-from is empty ---
{
Peer a;
if (!a || a.get() == nullptr)
{
if (isVerbose) printf("default-constructed Peer is not valid\n");
return 1;
}
RakPeerInterface *raw = a.get();

Peer b(std::move(a));
if (a || a.get() != nullptr)
{
if (isVerbose) printf("moved-from Peer (ctor) is not empty\n");
return 2;
}
if (!b || b.get() != raw)
{
if (isVerbose) printf("move ctor did not transfer the instance\n");
return 3;
}

Peer c;
c = std::move(b);
if (b || b.get() != nullptr)
{
if (isVerbose) printf("moved-from Peer (assign) is not empty\n");
return 4;
}
if (!c || c.get() != raw)
{
if (isVerbose) printf("move assign did not transfer the instance\n");
return 5;
}
// a and b destruct empty (no-op); c destructs the single live instance.
}

// --- 2. PacketPtr: null/empty path and empty receive() ---
{
Peer p;
PacketPtr nullPkt(p.get(), nullptr);
if (nullPkt)
{
if (isVerbose) printf("null PacketPtr is unexpectedly truthy\n");
return 10;
}
if (nullPkt.id() != 255)
{
if (isVerbose) printf("null PacketPtr id() is not 255\n");
return 11;
}

// A peer that was never started has nothing queued.
PacketPtr none = p.receive();
if (none)
{
if (isVerbose) printf("receive() on an un-started peer is not empty\n");
return 12;
}
}

// --- 3. Real packets over loopback: plain and timestamp id() branches ---
Peer server;
Peer client;

SocketDescriptor serverSd(PEERHANDLE_TEST_PORT, 0);
if (server->Startup(1, &serverSd, 1) != RAKNET_STARTED)
{
if (isVerbose) printf("server Startup failed (port %u in use?)\n", PEERHANDLE_TEST_PORT);
return 20;
}
server->SetMaximumIncomingConnections(1);

SocketDescriptor clientSd;
if (client->Startup(1, &clientSd, 1) != RAKNET_STARTED)
{
if (isVerbose) printf("client Startup failed\n");
return 21;
}

SystemAddress serverAddress;
serverAddress.SetBinaryAddress("127.0.0.1");
serverAddress.SetPortHostOrder(PEERHANDLE_TEST_PORT);

if (client->Connect("127.0.0.1", PEERHANDLE_TEST_PORT, 0, 0) != CONNECTION_ATTEMPT_STARTED)
{
if (isVerbose) printf("Connect did not start\n");
return 22;
}

// Wait for ID_CONNECTION_REQUEST_ACCEPTED on the client. This identifier is
// NOT timestamp-prefixed, so it exercises id()'s plain (first-byte) branch.
bool connected = false;
bool plainBranchOk = false;
TimeMS start = GetTimeMS();
while (GetTimeMS() - start < 10000 && !connected)
{
for (PacketPtr pkt = server.receive(); pkt; pkt = server.receive())
{
// Drain the server side (ID_NEW_INCOMING_CONNECTION, etc.).
}
for (PacketPtr pkt = client.receive(); pkt; pkt = client.receive())
{
if (pkt.id() == ID_CONNECTION_REQUEST_ACCEPTED)
{
if (pkt->data[0] == ID_TIMESTAMP || pkt.id() != (unsigned char) pkt->data[0])
{
if (isVerbose) printf("plain-branch id() did not match data[0]\n");
return 23;
}
plainBranchOk = true;
connected = true;
}
}
RakSleep(30);
}
if (!connected)
{
if (isVerbose) printf("client did not connect within 10s\n");
return 24;
}
if (!plainBranchOk)
return 25;

// Send a timestamp-prefixed user message and verify id() reads the byte that
// follows the MessageID + Time prefix on the server side.
BitStream bs;
bs.Write((MessageID) ID_TIMESTAMP);
bs.Write(GetTime());
bs.Write((MessageID) PEERHANDLE_TS_ID);
client->Send(&bs, Priority::High, Reliability::ReliableOrdered, 0, serverAddress, false);

bool timestampBranchOk = false;
start = GetTimeMS();
while (GetTimeMS() - start < 5000 && !timestampBranchOk)
{
for (PacketPtr pkt = server.receive(); pkt; pkt = server.receive())
{
if (pkt->data[0] == ID_TIMESTAMP)
{
if (pkt.id() != PEERHANDLE_TS_ID)
{
if (isVerbose) printf("timestamp-branch id() returned the wrong byte\n");
return 30;
}
timestampBranchOk = true;
}
}
for (PacketPtr pkt = client.receive(); pkt; pkt = client.receive())
{
// Drain the client side.
}
RakSleep(30);
}
if (!timestampBranchOk)
{
if (isVerbose) printf("server did not receive the timestamp message within 5s\n");
return 31;
}

// server and client are RAII Peers: they destruct (DestroyInstance) here,
// including on every early return above — that is the point of the wrappers.
return 0;
}

PeerHandleTest::PeerHandleTest(void)
{
}

PeerHandleTest::~PeerHandleTest(void)
{
}

RakString PeerHandleTest::GetTestName(void)
{
return "PeerHandleTest";
}

RakString PeerHandleTest::ErrorCodeToString(int errorCode)
{
switch (errorCode)
{
case 0: return "No error";
case 1: return "Default-constructed Peer invalid";
case 2: return "Moved-from Peer (ctor) not empty";
case 3: return "Move ctor did not transfer instance";
case 4: return "Moved-from Peer (assign) not empty";
case 5: return "Move assign did not transfer instance";
case 10: return "Null PacketPtr truthy";
case 11: return "Null PacketPtr id() != 255";
case 12: return "receive() on un-started peer not empty";
case 20: return "Server Startup failed";
case 21: return "Client Startup failed";
case 22: return "Connect did not start";
case 23: return "Plain-branch id() mismatch";
case 24: return "Client did not connect";
case 25: return "Did not observe ID_CONNECTION_REQUEST_ACCEPTED";
case 30: return "Timestamp-branch id() mismatch";
case 31: return "Server did not receive timestamp message";
default: return "Undefined error";
}
}

void PeerHandleTest::DestroyPeers(void)
{
// Peers in this test are RAII MafiaNet::Peer locals; they clean themselves
// up at scope exit, so there is nothing to do here.
}
41 changes: 41 additions & 0 deletions Samples/Tests/PeerHandleTest.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright (c) 2026, MafiaHub
*
* This source code is licensed under the MIT-style license found in the
* license.txt file in the root directory of this source tree.
*/

#pragma once


#include "TestInterface.h"

#include "mafianet/string.h"
#include "mafianet/DS_List.h"
#include "mafianet/peerinterface.h"
#include "mafianet/PeerHandle.h"
#include "mafianet/MessageIdentifiers.h"
#include "mafianet/BitStream.h"
#include "mafianet/sleep.h"
#include "mafianet/time.h"
#include "mafianet/GetTime.h"

using namespace MafiaNet;

/// Verifies the RAII handles Peer / PacketPtr (issue #16):
/// - Peer is movable and non-copyable; a moved-from handle is empty (no
/// double-free at scope exit).
/// - PacketPtr::id() returns 255 for a null/empty packet, the first byte for a
/// plain packet, and the post-timestamp byte for an ID_TIMESTAMP packet.
/// - Peer::receive() yields an empty PacketPtr when nothing is queued, and the
/// move-assignment in a receive loop frees each real packet without leaking.
class PeerHandleTest : public TestInterface
{
public:
PeerHandleTest(void);
~PeerHandleTest(void);
int RunTest(DataStructures::List<RakString> params, bool isVerbose, bool noPauses);
RakString GetTestName();
RakString ErrorCodeToString(int errorCode);
void DestroyPeers();
};
1 change: 1 addition & 0 deletions Samples/Tests/Tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ int main(int argc, char *argv[])
testList.Push(new DisconnectReasonTest(),_FILE_AND_LINE_);
testList.Push(new PeerGuidTest(),_FILE_AND_LINE_);
testList.Push(new PointGridSectorizerTest(),_FILE_AND_LINE_);
testList.Push(new PeerHandleTest(),_FILE_AND_LINE_);

testListSize=testList.Size();

Expand Down
2 changes: 2 additions & 0 deletions Source/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ set(MAFIANET_SOURCES
src/PacketizedTCP.cpp
src/PacketLogger.cpp
src/PacketOutputWindowLogger.cpp
src/PeerHandle.cpp
src/PluginInterface2.cpp
src/PointGridSectorizer.cpp
src/PS4Includes.cpp
Expand Down Expand Up @@ -217,6 +218,7 @@ set(MAFIANET_HEADERS
include/mafianet/PacketOutputWindowLogger.h
include/mafianet/PacketPool.h
include/mafianet/PacketPriority.h
include/mafianet/PeerHandle.h
include/mafianet/peer.h
include/mafianet/peerinterface.h
include/mafianet/PluginInterface2.h
Expand Down
Loading
Loading