Skip to content

Value-type fixes: thread-safe to_string + optional connected_address (#15)#31

Merged
Segfaultd merged 3 commits into
masterfrom
feat/value-type-modern-accessors
Jun 15, 2026
Merged

Value-type fixes: thread-safe to_string + optional connected_address (#15)#31
Segfaultd merged 3 commits into
masterfrom
feat/value-type-modern-accessors

Conversation

@Segfaultd

@Segfaultd Segfaultd commented Jun 15, 2026

Copy link
Copy Markdown
Member

Closes #15.

Summary

Adds modern, additive value-type accessors and moves the legacy non-thread-safe GUID stringifier into them (per maintainer direction, overriding the issue's "additive only" framing).

New — mafianet/guid_util.h

  • std::string to_string(const RakNetGUID&) — allocates, owns its buffer, thread-safe; built on the existing thread-safe ToString(char*, size_t).
  • std::optional<SystemAddress> connected_address(RakPeerInterface&, const RakNetGUID&) — maps the UNASSIGNED_SYSTEM_ADDRESS "none" sentinel to std::nullopt. Thin facade wrapper, not added to RakPeerInterface.
  • Wired into Source/CMakeLists.txt and the mafianet.h umbrella header.

Moved/removed

  • Removed the non-thread-safe static-buffer RakNetGUID::ToString(void) member + impl. Its role now lives in to_string.
  • AddressOrGUID::ToString(bool) (which delegated to the removed member) now self-contains its rotating buffer, preserving its const char* API/behavior.
  • Migrated ~70 GUID call sites across the library and 17 sample files to MafiaNet::to_string(g).c_str(). All SystemAddress/AddressOrGUID ToString calls are untouched. Includes 3 samples excluded from the default build (SteamLobby, RakVoice, ReplicatedLogin), migrated by hand.

Tests — GuidUtilTest (registered, passing)

  • to_string matches the legacy member output and the UNASSIGNED label.
  • Concurrency: 16 threads × 20k iterations on distinct GUIDs — a shared buffer would corrupt; it doesn't.
  • connected_address returns nullopt for the sentinel and an unknown GUID, and mirrors GetSystemAddressFromGuid for the engaged case.

Acceptance criteria

  • to_string(guid) is safe to call concurrently (no shared buffer).
  • optional accessor returns std::nullopt where the legacy API returns the sentinel.

Not included

  • Issue point 3 (std::string_view overloads for char*+len pairs) — explicitly optional and outside the acceptance criteria; deferred.

Verification

  • Full build (library + all samples + dependent extensions): clean, exit 0.
  • ./build/Samples/Tests/Tests GuidUtilTest → pass; SystemAddressAndGuidTest, PeerGuidTest → pass.

Summary by CodeRabbit

  • New Features

    • Added thread-safe GUID string conversion utility (MafiaNet::to_string())
    • Added optional GUID-to-address lookup helper (MafiaNet::connected_address())
  • Refactor

    • Updated samples and test outputs (including JSON GUID printing) to use the new GUID utilities
    • Removed the non-thread-safe RakNetGUID::ToString() API
    • Updated internal GUID formatting used by related string conversions
  • Tests

    • Added GuidUtilTest (including correctness + concurrency validation)
    • Improved DisconnectReasonTest robustness with bounded retries and extended timeouts

…threadsafe GUID ToString (#15)

Adds modern value-type accessors in a new mafianet/guid_util.h:
- std::string to_string(const RakNetGUID&): owns its buffer, thread-safe,
  built on the existing thread-safe ToString(char*, size_t).
- std::optional<SystemAddress> connected_address(RakPeerInterface&,
  const RakNetGUID&): maps the UNASSIGNED_SYSTEM_ADDRESS sentinel to
  std::nullopt (facade wrapper, not in RakPeerInterface).

Removes the non-thread-safe static-buffer RakNetGUID::ToString(void)
member and migrates all ~70 GUID call sites across the library and
samples to MafiaNet::to_string(g).c_str(). AddressOrGUID::ToString(bool)
now self-contains its rotating buffer instead of delegating to the
removed member. SystemAddress/AddressOrGUID ToString calls untouched.

Adds GuidUtilTest covering thread-safety (16 threads) and the
nullopt-on-sentinel contract.
@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

Adds MafiaNet::to_string(const RakNetGUID&) and MafiaNet::connected_address(RakPeerInterface&, const RakNetGUID&) as thread-safe free functions in a new guid_util.h/guid_util.cpp. Removes the non-thread-safe RakNetGUID::ToString(void) const overload. Migrates all sample programs and library internals to the new API, and adds GuidUtilTest covering correctness, concurrency, and optional-sentinel behavior. Also strengthens DisconnectReasonTest with retry logic for reliable notification payload validation.

Changes

GUID Utility API and Migration

Layer / File(s) Summary
guid_util API declaration and CMake wiring
Source/include/mafianet/guid_util.h, Source/include/mafianet/types.h, Source/include/mafianet/mafianet.h, Source/CMakeLists.txt
Declares MafiaNet::to_string and MafiaNet::connected_address, updates RakNetGUID doc comment to drop non-thread-safe ToString() guidance, adds guid_util.h to the umbrella header, and registers the new source and header in the build.
guid_util.cpp implementation and ToString() removal
Source/src/guid_util.cpp, Source/src/RakNetTypes.cpp
Implements to_string via a fixed local buffer, implements connected_address returning nullopt for the sentinel address, removes the non-thread-safe RakNetGUID::ToString(void) const overload, and patches AddressOrGUID::ToString to use its own rotating buffer.
Library source migration (FullyConnectedMesh2)
Source/src/FullyConnectedMesh2.cpp
Includes guid_util.h and replaces two debug-log ToString() calls with to_string().
GuidUtilTest: correctness and concurrency validation
Samples/Tests/GuidUtilTest.h, Samples/Tests/GuidUtilTest.cpp, Samples/Tests/IncludeAllTests.h, Samples/Tests/Tests.cpp
Adds GuidUtilTest validating to_string output against legacy ToString, checking the UNASSIGNED sentinel string, stress-testing concurrent calls for buffer corruption, and verifying connected_address returns nullopt for sentinel/unknown GUIDs and matches GetSystemAddressFromGuid for known GUIDs.
Sample programs migration to MafiaNet::to_string
Samples/ChatExample/..., Samples/ComprehensivePCGame/..., Samples/FCMHost/..., Samples/FCMHostSimultaneous/..., Samples/FCMVerifiedJoinSimultaneous/..., Samples/LANServerDiscovery/..., Samples/NATCompleteClient/..., Samples/OfflineMessagesTest/..., Samples/RakVoice/..., Samples/ReadyEvent/..., Samples/ReplicaManager3/..., Samples/ReplicatedLogin/..., Samples/Router2/..., Samples/SteamLobby/..., Samples/TeamManager/..., Samples/Tests/OfflineMessagesConvertTest.cpp
Adds #include "mafianet/guid_util.h" and replaces all .ToString() call sites with MafiaNet::to_string(...).c_str() across all sample programs. No control-flow changes.

DisconnectReasonTest Reliability with Retry Logic

Layer / File(s) Summary
Retry helpers and per-case validation
Samples/Tests/DisconnectReasonTest.cpp
Introduces per-attempt timeout constants, defines transient error codes distinct from hard failures, adds ResetConnection helper to drain and close peers cleanly, adds TryDisconnectCase to run one connect/close/notification cycle with detailed payload verification, and wraps all three test cases with RunDisconnectCaseWithRetries to retry transient failures up to kMaxAttempts times. Case 3 adds validation of empty-but-non-null reason bitstream.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • MafiaHub/MafiaNet#29: Modifies Source/include/mafianet/mafianet.h, the same umbrella header that this PR further extends by adding mafianet/guid_util.h.
  • MafiaHub/MafiaNet#24: Modifies Samples/Tests/DisconnectReasonTest.cpp to validate disconnect-reason payloads; this PR strengthens the test with retry logic for reliable notification verification.

Poem

🐇 A GUID once returned a shared static string,
"NOT THREADSAFE!" the old comment would sing.
Now to_string owns its own stack,
No race condition to hold us back —
Optional sentinels, threads can safely spring! 🌿

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: introducing a thread-safe to_string utility and an optional connected_address function for RakNetGUID.
Linked Issues check ✅ Passed The PR fully addresses all coding requirements from issue #15: implements thread-safe to_string, adds optional connected_address mapping sentinels to nullopt, and removes the non-thread-safe ToString() method.
Out of Scope Changes check ✅ Passed All changes are tightly scoped to issue #15: introducing new utilities in guid_util.h, migrating ~70 call sites, removing the old ToString(), and adding GuidUtilTest. No unrelated modifications present.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/value-type-modern-accessors

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
Source/include/mafianet/guid_util.h (1)

24-25: ⚡ Quick win

Use project-qualified include paths in this public header.

guid_util.h is part of the public API surface; please include project headers using the mafianet/... form for consistency with the repository policy.

Proposed change
-#include "types.h"
-#include "peerinterface.h"
+#include "mafianet/types.h"
+#include "mafianet/peerinterface.h"

As per coding guidelines, "Include paths in C++ code should use the 'mafianet/...' format for public API headers".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Source/include/mafianet/guid_util.h` around lines 24 - 25, The public header
guid_util.h is using relative include paths for types.h and peerinterface.h
instead of the project-qualified format. Update both include directives to use
the mafianet/ prefix (e.g., change `#include` "types.h" to `#include`
"mafianet/types.h" and `#include` "peerinterface.h" to `#include`
"mafianet/peerinterface.h") to maintain consistency with the repository's public
API header guidelines.

Source: Coding guidelines

Samples/Tests/GuidUtilTest.h (1)

17-27: ⚡ Quick win

Scope GuidUtilTest under MafiaNet instead of exporting a global using namespace from the header.

This header currently injects MafiaNet symbols into every includer and keeps GuidUtilTest in the global namespace. If tests are not intentionally exempt from the namespace rule, wrap the type in namespace MafiaNet { ... } and remove the using-directive.

♻️ Proposed change
-using namespace MafiaNet;
-
 /// Verifies the modern value-type accessors from "mafianet/guid_util.h" (issue
 /// `#15`):
 /// - to_string(guid) returns the same text as the legacy thread-safe
 ///   ToString(char*, size_t) member, handles the UNASSIGNED sentinel, and is
 ///   safe to call concurrently from many threads (no shared static buffer).
 /// - connected_address(peer, guid) maps the UNASSIGNED_SYSTEM_ADDRESS "none"
 ///   sentinel to std::nullopt and otherwise forwards the legacy lookup.
- 
-class GuidUtilTest : public TestInterface
+namespace MafiaNet {
+class GuidUtilTest : public TestInterface
 {
 public:
 	GuidUtilTest(void);
 	~GuidUtilTest(void);
 	int RunTest(DataStructures::List<RakString> params, bool isVerbose, bool noPauses);
 	RakString GetTestName();
 	RakString ErrorCodeToString(int errorCode);
 	void DestroyPeers();

 private:
 	DataStructures::List<RakPeerInterface *> destroyList;
 };
+} // namespace MafiaNet

As per coding guidelines, "Use the MafiaNet namespace (or MNet macro shorthand) exclusively throughout the library for all code".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Samples/Tests/GuidUtilTest.h` around lines 17 - 27, The GuidUtilTest header
currently uses a global `using namespace MafiaNet;` directive which pollutes the
global namespace for all includers. Instead, remove the `using namespace
MafiaNet;` statement and wrap the GuidUtilTest class definition and its
associated documentation comment within an explicit `namespace MafiaNet { ... }`
block. This keeps the symbols scoped properly according to the coding guidelines
that require all code to use the MafiaNet namespace exclusively.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Source/src/FullyConnectedMesh2.cpp`:
- Line 365: The printf statement that logs the popping participant is using dot
notation to access the rakNetGuid member of a pointer element from the
fcm2ParticipantList array. Since fcm2ParticipantList contains pointers, the
member access operator must be changed from the dot operator (.) to the arrow
operator (->) when accessing rakNetGuid on the pointer element at index
fcm2ParticipantList.Size()-1.

---

Nitpick comments:
In `@Samples/Tests/GuidUtilTest.h`:
- Around line 17-27: The GuidUtilTest header currently uses a global `using
namespace MafiaNet;` directive which pollutes the global namespace for all
includers. Instead, remove the `using namespace MafiaNet;` statement and wrap
the GuidUtilTest class definition and its associated documentation comment
within an explicit `namespace MafiaNet { ... }` block. This keeps the symbols
scoped properly according to the coding guidelines that require all code to use
the MafiaNet namespace exclusively.

In `@Source/include/mafianet/guid_util.h`:
- Around line 24-25: The public header guid_util.h is using relative include
paths for types.h and peerinterface.h instead of the project-qualified format.
Update both include directives to use the mafianet/ prefix (e.g., change
`#include` "types.h" to `#include` "mafianet/types.h" and `#include` "peerinterface.h"
to `#include` "mafianet/peerinterface.h") to maintain consistency with the
repository's public API header guidelines.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: fc4c6c40-2813-4f2a-af32-09c1461526e4

📥 Commits

Reviewing files that changed from the base of the PR and between dd14547 and dd7360a.

📒 Files selected for processing (28)
  • Samples/ChatExample/Client/Chat Example Client.cpp
  • Samples/ChatExample/Server/Chat Example Server.cpp
  • Samples/ComprehensivePCGame/ComprehensivePCGame.cpp
  • Samples/FCMHost/FCM2HostTest.cpp
  • Samples/FCMHostSimultaneous/FCM2HostSimultaneousTest.cpp
  • Samples/FCMVerifiedJoinSimultaneous/FCM2VerifiedJoinSimultaneousTest.cpp
  • Samples/LANServerDiscovery/LANServerDiscovery.cpp
  • Samples/NATCompleteClient/main.cpp
  • Samples/OfflineMessagesTest/OfflineMessagesTest.cpp
  • Samples/RakVoice/main.cpp
  • Samples/ReadyEvent/ReadyEventSample.cpp
  • Samples/ReplicaManager3/main.cpp
  • Samples/ReplicatedLogin/ReplicatedLogin.cpp
  • Samples/Router2/Router2Sample.cpp
  • Samples/SteamLobby/main.cpp
  • Samples/TeamManager/TeamManagerTest.cpp
  • Samples/Tests/GuidUtilTest.cpp
  • Samples/Tests/GuidUtilTest.h
  • Samples/Tests/IncludeAllTests.h
  • Samples/Tests/OfflineMessagesConvertTest.cpp
  • Samples/Tests/Tests.cpp
  • Source/CMakeLists.txt
  • Source/include/mafianet/guid_util.h
  • Source/include/mafianet/mafianet.h
  • Source/include/mafianet/types.h
  • Source/src/FullyConnectedMesh2.cpp
  • Source/src/RakNetTypes.cpp
  • Source/src/guid_util.cpp

Comment thread Source/src/FullyConnectedMesh2.cpp Outdated
…h bounded retry

Delivering ID_DISCONNECTION_NOTIFICATION to Receive() is a reliable ACK
round-trip whose completion depends on both peers' internal network
threads being scheduled. On a CPU-starved CI runner running the whole
suite in one process, a single connect->close->wait cycle can
intermittently miss its window, failing Case 1 with 'client never
received ID_DISCONNECTION_NOTIFICATION'.

Wrap each case's connect->close->receive in a bounded retry (3 attempts)
with a ResetConnection helper that silently tears down both sides between
attempts so the fresh reconnect can't race an 'already connected'
rejection. Only the transient 'never received' miss is retried; wrong
payload failures fail hard immediately, so a real regression in the
reason-payload feature is still caught.

Verified the retry/reconnect path recovers via temporary fault injection
(forced first two attempts to miss -> third reconnects and delivers,
content checks still pass), plus 0 failures across 150 in-process runs
and 60 single-process runs under heavy CPU oversubscription.
fcm2ParticipantList is a List<FCM2Participant*>, so the popped-participant
debug printf must access rakNetGuid via -> not . — the dot form does not
compile when DEBUG_FCM2 is defined. The block is excluded from normal
builds, so CI never caught it.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
Samples/Tests/DisconnectReasonTest.cpp (1)

146-163: 💤 Low value

Consider checking Read(gotCode) return value for clearer error classification.

The Read(gotCode) on line 150 doesn't check its return value. While the length check at line 139 ensures payload data exists, checking the return would allow reporting a deserialization failure (deserialErr) rather than a potentially misleading "code did not round-trip" (codeErr) if parsing goes wrong.

♻️ Suggested improvement
-		readBack.Read(gotCode);
-		bool textOk = gotText.Deserialize(&readBack);
+		bool codeOk = readBack.Read(gotCode);
+		bool textOk = codeOk && gotText.Deserialize(&readBack);
 		client->DeallocatePacket(note);
 		ResetConnection(server, client, clientGuid, serverPort);
 
 		// Content mismatches are hard failures: a real regression in the reason
 		// payload would fail these identically on every attempt, so they are never
 		// retried away.
-		if (!textOk)
+		if (!codeOk || !textOk)
 			return deserialErr;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Samples/Tests/DisconnectReasonTest.cpp` around lines 146 - 163, The Read
method call on gotCode does not check its return value, which means if
deserialization fails, the code will incorrectly classify the error as a code
mismatch (codeErr) rather than a deserialization failure (deserialErr). Add a
check for the return value of Read(gotCode) immediately after the call, similar
to how the Deserialize method is checked for gotText, and return deserialErr if
the Read operation fails.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@Samples/Tests/DisconnectReasonTest.cpp`:
- Around line 146-163: The Read method call on gotCode does not check its return
value, which means if deserialization fails, the code will incorrectly classify
the error as a code mismatch (codeErr) rather than a deserialization failure
(deserialErr). Add a check for the return value of Read(gotCode) immediately
after the call, similar to how the Deserialize method is checked for gotText,
and return deserialErr if the Read operation fails.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9329f367-fbbc-439e-9141-efe3a08792f0

📥 Commits

Reviewing files that changed from the base of the PR and between dd7360a and 6653458.

📒 Files selected for processing (2)
  • Samples/Tests/DisconnectReasonTest.cpp
  • Source/src/FullyConnectedMesh2.cpp
✅ Files skipped from review due to trivial changes (1)
  • Source/src/FullyConnectedMesh2.cpp

@Segfaultd Segfaultd merged commit fefaf15 into master Jun 15, 2026
4 checks passed
@Segfaultd Segfaultd deleted the feat/value-type-modern-accessors branch June 15, 2026 12:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[DX L0] Value-type fixes: thread-safe Guid::to_string, optional/string_view

1 participant