From a1cff9536572c227a99cb9738eaee6e311f81418 Mon Sep 17 00:00:00 2001 From: Rowan Date: Fri, 7 Aug 2026 13:59:05 -0700 Subject: [PATCH] Re-arm SocketAddress.Size before every ReceiveFrom: the net8.0 leg is whole again The receive path now survives empty polls on .NET 8. Socket.ReceiveFrom requires the reusable SocketAddress sized to the family maximum before each call, and .NET 8 overwrites Size with the received address length BEFORE checking the recvfrom result - so the first EWOULDBLOCK poll (the steady state of a non-blocking socket) zeroes it and the next call throws ArgumentOutOfRangeException "SocketAddress is too small" out of NetcodeSocket.ReceivePacket. .NET 9 moved that assignment below the error check, which is why only the LTS leg failed. Local runs cannot see this: the net8.0 TFM rolls forward to the .NET 10 runtime here, so both legs (46 tests each) pass locally and CI's real .NET 8 is the proof that counts. --- src/Netcode.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Netcode.cs b/src/Netcode.cs index 91ed0d3..8ced7d7 100644 --- a/src/Netcode.cs +++ b/src/Netcode.cs @@ -1896,6 +1896,14 @@ public void SendPacket(in Address to, ReadOnlySpan packetData) /// Non-blocking receive. Returns 0 when no packet is available. public int ReceivePacket(ref Address from, Span packetData) { + // ReceiveFrom requires Size >= the family maximum before every call, and + // .NET 8 overwrites Size BEFORE checking the recvfrom result — so an empty + // poll (EWOULDBLOCK, the common case on a non-blocking socket) zeroes it + // and the next call throws ArgumentOutOfRangeException ("SocketAddress is + // too small"). .NET 9 moved that assignment after the error check. Re-arm + // the size each call; it is a field store, nothing allocates. + _receiveAddress.Size = SocketAddress.GetMaximumAddressSize(_socket.AddressFamily); + int result; try {