feat: Adds a memory mirrored ring buffer for networking. (#1533)

## Breaking Changes

Incoming packet registration signature has changed to:
```cs
delegate* void OnReceiveCallback(NetState state, SpanReader reader, int packetLength);

IncomingPackets.Register(int packetID, int length, bool ingame, OnReceiveCallback onReceive);
```

For example, an incoming packet handler signature would now look like this:
```cs
public static void SomeIncomingPacket(NetState state, SpanReader reader, int packetLength)
{
    // Parse the data
}
```

## Summary

Updates the network Pipe class to use a mirrored memory technique. This technique involves mapping the same physical memory to two contiguous virtual memory spaces so the byte buffer appears duplicated. This allows writing to a double-sized array to wrap around without the need for the `CircularBuffer` classes.

In practice this allows us to use `Span<byte>` as if the buffer was a regular array.


### Bug Fixes

- [X] Fixes bad fixed length string parsing
This commit is contained in:
Kamron Batman 2023-10-09 00:57:53 -07:00 committed by GitHub
parent 629a5008c3
commit 10a69bf754
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
83 changed files with 958 additions and 2432 deletions

View file

@ -13,6 +13,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*************************************************************************/
using System.Buffers;
using System.Diagnostics;
using Microsoft.Toolkit.HighPerformance;
using Server.Diagnostics;
@ -54,18 +55,18 @@ public static class IncomingPlayerPackets
IncomingPackets.RegisterEncoded(0x32, true, &QuestGumpRequest);
}
public static void DeathStatusResponse(NetState state, CircularBufferReader reader, int packetLength)
public static void DeathStatusResponse(NetState state, SpanReader reader, int packetLength)
{
// Ignored
}
public static void RequestScrollWindow(NetState state, CircularBufferReader reader, int packetLength)
public static void RequestScrollWindow(NetState state, SpanReader reader, int packetLength)
{
int lastTip = reader.ReadInt16();
int type = reader.ReadByte();
}
public static void AttackReq(NetState state, CircularBufferReader reader, int packetLength)
public static void AttackReq(NetState state, SpanReader reader, int packetLength)
{
var from = state.Mobile;
@ -82,7 +83,7 @@ public static class IncomingPlayerPackets
}
}
public static void HuePickerResponse(NetState state, CircularBufferReader reader, int packetLength)
public static void HuePickerResponse(NetState state, SpanReader reader, int packetLength)
{
var serial = reader.ReadUInt32();
_ = reader.ReadInt16(); // Item ID
@ -99,7 +100,7 @@ public static class IncomingPlayerPackets
}
}
public static void SystemInfo(NetState state, CircularBufferReader reader, int packetLength)
public static void SystemInfo(NetState state, SpanReader reader, int packetLength)
{
int v1 = reader.ReadByte();
int v2 = reader.ReadUInt16();
@ -115,7 +116,7 @@ public static class IncomingPlayerPackets
var v8 = reader.ReadInt32();
}
public static void TextCommand(NetState state, CircularBufferReader reader, int packetLength)
public static void TextCommand(NetState state, SpanReader reader, int packetLength)
{
var from = state.Mobile;
@ -210,7 +211,7 @@ public static class IncomingPlayerPackets
}
}
public static void AsciiPromptResponse(NetState state, CircularBufferReader reader, int packetLength)
public static void AsciiPromptResponse(NetState state, SpanReader reader, int packetLength)
{
var from = state.Mobile;
@ -246,7 +247,7 @@ public static class IncomingPlayerPackets
}
}
public static void UnicodePromptResponse(NetState state, CircularBufferReader reader, int packetLength)
public static void UnicodePromptResponse(NetState state, SpanReader reader, int packetLength)
{
var from = state.Mobile;
@ -283,7 +284,7 @@ public static class IncomingPlayerPackets
}
}
public static void MenuResponse(NetState state, CircularBufferReader reader, int packetLength)
public static void MenuResponse(NetState state, SpanReader reader, int packetLength)
{
var serial = reader.ReadUInt32();
int menuID = reader.ReadInt16(); // unused in our implementation
@ -313,33 +314,33 @@ public static class IncomingPlayerPackets
}
}
public static void Disconnect(NetState state, CircularBufferReader reader, int packetLength)
public static void Disconnect(NetState state, SpanReader reader, int packetLength)
{
var minusOne = reader.ReadInt32();
}
public static void ConfigurationFile(NetState state, CircularBufferReader reader, int packetLength)
public static void ConfigurationFile(NetState state, SpanReader reader, int packetLength)
{
}
public static void LogoutReq(NetState state, CircularBufferReader reader, int packetLength)
public static void LogoutReq(NetState state, SpanReader reader, int packetLength)
{
state.SendLogoutAck();
}
public static void ChangeSkillLock(NetState state, CircularBufferReader reader, int packetLength)
public static void ChangeSkillLock(NetState state, SpanReader reader, int packetLength)
{
var s = state.Mobile.Skills[reader.ReadInt16()];
s?.SetLockNoRelay((SkillLock)reader.ReadByte());
}
public static void HelpRequest(NetState state, CircularBufferReader reader, int packetLength)
public static void HelpRequest(NetState state, SpanReader reader, int packetLength)
{
EventSink.InvokeHelpRequest(state.Mobile);
}
public static void DisplayGumpResponse(NetState state, CircularBufferReader reader, int packetLength)
public static void DisplayGumpResponse(NetState state, SpanReader reader, int packetLength)
{
var serial = (Serial)reader.ReadUInt32();
var typeID = reader.ReadInt32();
@ -481,13 +482,13 @@ public static class IncomingPlayerPackets
}
}
public static void SetWarMode(NetState state, CircularBufferReader reader, int packetLength)
public static void SetWarMode(NetState state, SpanReader reader, int packetLength)
{
state.Mobile?.DelayChangeWarmode(reader.ReadBoolean());
}
// TODO: Throttle/make this more safe
public static void Resynchronize(NetState state, CircularBufferReader reader, int packetLength)
public static void Resynchronize(NetState state, SpanReader reader, int packetLength)
{
var from = state.Mobile;
@ -504,17 +505,17 @@ public static class IncomingPlayerPackets
state.Sequence = 0;
}
public static void PingReq(NetState state, CircularBufferReader reader, int packetLength)
public static void PingReq(NetState state, SpanReader reader, int packetLength)
{
state.SendPingAck(reader.ReadByte());
}
public static void SetUpdateRange(NetState state, CircularBufferReader reader, int packetLength)
public static void SetUpdateRange(NetState state, SpanReader reader, int packetLength)
{
state.SendChangeUpdateRange(18);
}
public static void MobileQuery(NetState state, CircularBufferReader reader, int packetLength)
public static void MobileQuery(NetState state, SpanReader reader, int packetLength)
{
var from = state.Mobile;
if (from == null)
@ -545,13 +546,13 @@ public static class IncomingPlayerPackets
}
default:
{
reader.Trace(state);
state.Trace(reader.Buffer);
break;
}
}
}
public static void CrashReport(NetState state, CircularBufferReader reader, int packetLength)
public static void CrashReport(NetState state, SpanReader reader, int packetLength)
{
var clientMaj = reader.ReadByte();
var clientMin = reader.ReadByte();
@ -595,7 +596,7 @@ public static class IncomingPlayerPackets
EventSink.InvokeQuestGumpRequest(state.Mobile);
}
public static unsafe void EncodedCommand(NetState state, CircularBufferReader reader, int packetLength)
public static unsafe void EncodedCommand(NetState state, SpanReader reader, int packetLength)
{
var e = World.FindEntity((Serial)reader.ReadUInt32());
int packetId = reader.ReadUInt16();
@ -612,7 +613,7 @@ public static class IncomingPlayerPackets
if (ph == null)
{
reader.Trace(state);
state.Trace(reader.Buffer);
return;
}