Repository navigation
Conversation
* Implement frozen data handling in GameMessage Added synchronization for frozen data in GameMessage class. * Refactor NetworkBundle for message queue and size Changed messages queue to readonly and updated CurrentSize to long to prevent overflow. * Refactor PacketFragment class for consistency Make Length virtual so the server-side fragment can calculate its length without owning a separate byte[] * Change parameter type from Span to ReadOnlySpan * Refactor ServerPacketFragment to use ReadOnlyMemory * Refactor MessageFragment to use ReadOnlyMemory for data Refactor MessageFragment to use ReadOnlyMemory<byte> for data handling instead of MemoryStream. This change improves memory efficiency by avoiding per-fragment byte array allocations. * Remove FrozenDataLength property from GameMessage * Change CurrentSize type to int and update logic * Clarify comments for fragment size constants Updated comments for MaxFragmentSize and MaxFragmentDataSize constants to clarify their purpose. * Remove payload validation in ServerPacketFragment Removed validation checks for payload length in ServerPacketFragment constructor. * Improve comments on MemoryStream initialization Clarify MemoryStream initial capacity behavior in comments. * Fix formatting in Enqueue method * Add unit tests for ServerPacketFragment class * Add unit tests for MessageFragment functionality * Add test for concurrent fragmentation with frozen data Added a test for concurrent fragmentation of game messages using frozen data. * Change DataTestMethod to TestMethod for TailSize test
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
While testing on the Asheron4Fun live server, we encountered repeated outbound network failures during heavy multibox activity. The issue presented as
System.ArgumentExceptionfailures originating fromMessageFragment.CreateServerFragment()duringSession.TickOutbound(), along with related fragment-state messages indicating invalid positions and incorrect remaining-data calculations.Problem
Investigation identified that multiple client sessions could process the same
GameMessagewhile sharing its underlyingMemoryStream.In simple terms, the old code allowed several sessions to read from the same message while also sharing one movable stream position. One session could move that position while another session was still reading, causing the second session to read from the wrong location. Under enough concurrency, fragment boundaries could become inconsistent and eventually result in out-of-range reads and the exceptions observed in the logs.
Fix
The outbound message is now serialized and frozen once into a byte buffer when it enters the network queue. Each session then creates its fragments from fixed, read-only slices of that frozen payload instead of moving a shared
MemoryStreamcursor.This removes the shared mutable stream position entirely, so one session can no longer interfere with another session's fragment reads.
The change also corrects an edge case in
TailSizewhere messages whose payload length was an exact multiple of the 448-byte fragment size could incorrectly report the final fragment as header-only.This change does not alter the packet format, fragment size, message serialization format, or checksum algorithm. It only changes how the already-serialized outbound message data is accessed during fragmentation.
The implementation also removes the temporary per-fragment payload copy by allowing
ServerPacketFragmentto operate directly on the read-only message slice. The primary purpose of this change is correctness and thread safety, with reduced fragment-allocation churn as a secondary benefit.Regression tests were added for both the concurrency scenario and the exact-multiple fragment-size case.
Validation
The targeted network tests pass 4/4 with 0 failures.
The patched build was deployed to the Asheron4Fun live server on October 6, 2026 at 15:59:12 and remained under live production traffic through the end of the attached logs on October 12, 2026 at 10:38:42.
During that period, the patched server processed exactly 134,834 new network sessions, including same-IP multibox activity reaching at least 13 concurrent sessions, with no recurrence of:
MessageFragment.CreateServerFragment()exceptionsSession.TickOutbound()fragmentation failuresSystem.ArgumentExceptionbounds errorsThere is no data remainingMore data to send then data remaining!Passed index computes to invalid position sizeThe changes in this PR are currently live on Asheron4Fun. Anyone who would like to test the implementation is welcome to log in as a client and exercise the server under normal or multibox usage.
The attached server-log archive contains approximately 5 days and 18 hours of post-patch live-server activity. For reviewers wishing to compare behavior before and after deployment,
ACE_Log.txt.4contains both sides of the transition:ACE_Log.txt.4, the originalSystem.ArgumentException/MessageFragment.CreateServerFragment()failure is recorded repeatedly.ACE_Log.txt.3,.2,.1, and the currentACE_Log.txt, show the post-patch soak period with no recurrence of the original fragmentation failure.The remaining rotated logs in the ZIP are retained as additional historical context.
ACE_Log.txt.zip