Skip to content

Commit

Permalink
fix: Make RelayInfo zero allocation (#1699)
Browse files Browse the repository at this point in the history
  • Loading branch information
stefanomerotta authored Mar 9, 2024
1 parent cfe9e04 commit 76f132f
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 64 deletions.
62 changes: 22 additions & 40 deletions Projects/Server/Gumps/RelayInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,61 +13,43 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*************************************************************************/

using Server.Text;
using System;

namespace Server.Gumps;

public readonly struct TextRelay
public readonly ref struct RelayInfo
{
public TextRelay(int entryId, string text)
private readonly ReadOnlySpan<byte> _textBlock;
private readonly ReadOnlySpan<ushort> _textIds;
private readonly ReadOnlySpan<Range> _textRanges;

public RelayInfo(
int buttonId,
ReadOnlySpan<int> switches,
ReadOnlySpan<ushort> textIds,
ReadOnlySpan<Range> textRanges,
ReadOnlySpan<byte> textBlock
)
{
EntryId = entryId;
Text = text;
}

public int EntryId { get; }

public string Text { get; }
}

public ref struct RelayInfo
{
public RelayInfo(int buttonID, int[] switches, TextRelay[] textEntries)
{
ButtonID = buttonID;
ButtonID = buttonId;
Switches = switches;
TextEntries = textEntries;
_textIds = textIds;
_textRanges = textRanges;
_textBlock = textBlock;
}

public int ButtonID { get; }

public ReadOnlySpan<int> Switches { get; }

public ReadOnlySpan<TextRelay> TextEntries { get; }

public bool IsSwitched(int switchID)
{
for (var i = 0; i < Switches.Length; ++i)
{
if (Switches[i] == switchID)
{
return true;
}
}

return false;
}
public bool IsSwitched(int switchId) => Switches.Contains(switchId);

public string GetTextEntry(int entryId)
{
for (var i = 0; i < TextEntries.Length; ++i)
{
if (TextEntries[i].EntryId == entryId)
{
return TextEntries[i].Text;
}
}

return default;
int index = _textIds.IndexOf((ushort)entryId);
return index == -1
? default
: TextEncoding.GetString(_textBlock[_textRanges[index]], TextEncoding.Unicode, true);
}
}
81 changes: 57 additions & 24 deletions Projects/UOContent/Network/Packets/IncomingPlayerPackets.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,12 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*************************************************************************/

using System;
using System.Buffers;
using System.Buffers.Binary;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using CommunityToolkit.HighPerformance;
using Server.Diagnostics;
using Server.Engines.Virtues;
Expand Down Expand Up @@ -343,29 +347,37 @@ public static void HelpRequest(NetState state, SpanReader reader)
public static void DisplayGumpResponse(NetState state, SpanReader reader)
{
var serial = (Serial)reader.ReadUInt32();
var typeID = reader.ReadInt32();
var buttonID = reader.ReadInt32();
var typeId = reader.ReadInt32();
var buttonId = reader.ReadInt32();

foreach (var gump in state.Gumps)
Gump gump = null;

foreach (var g in state.Gumps)
{
if (gump.Serial != serial || gump.TypeID != typeID)
if (g.Serial != serial || g.TypeID != typeId)
{
continue;
}

var buttonExists = buttonID == 0; // 0 is always 'close'
gump = g;
break;
}

if (gump != null)
{
var buttonExists = buttonId == 0; // 0 is always 'close'

if (!buttonExists)
{
foreach (var e in gump.Entries)
{
if (e is GumpButton button && button.ButtonID == buttonID)
if (e is GumpButton button && button.ButtonID == buttonId)
{
buttonExists = true;
break;
}

if (e is GumpImageTileButton tileButton && tileButton.ButtonID == buttonID)
if (e is GumpImageTileButton tileButton && tileButton.ButtonID == buttonId)
{
buttonExists = true;
break;
Expand All @@ -376,7 +388,7 @@ public static void DisplayGumpResponse(NetState state, SpanReader reader)
if (!buttonExists)
{
state.LogInfo("Invalid gump response, disconnecting...");
var exception = new InvalidGumpResponseException($"Button {buttonID} doesn't exist");
var exception = new InvalidGumpResponseException($"Button {buttonId} doesn't exist");
exception.SetStackTrace(new StackTrace());
NetState.TraceException(exception);
state.Mobile?.SendMessage("Invalid gump response.");
Expand All @@ -399,15 +411,25 @@ public static void DisplayGumpResponse(NetState state, SpanReader reader)
return;
}

var switches = new int[switchCount];
// Read in all of the integers
ReadOnlySpan<int> switchBlock =
MemoryMarshal.Cast<byte, int>(reader.Buffer.Slice(reader.Position, switchCount * 4));

scoped ReadOnlySpan<int> switches;

for (var i = 0; i < switches.Length; ++i)
// Swap the endianness if necessary
if (BitConverter.IsLittleEndian)
{
Span<int> reversedSwitches = stackalloc int[switchCount];
BinaryPrimitives.ReverseEndianness(switchBlock, reversedSwitches);
switches = reversedSwitches;
}
else
{
switches[i] = reader.ReadInt32();
switches = switchBlock;
}

var textCount = reader.ReadInt32();

if (textCount < 0 || textCount > gump.TextEntries)
{
state.LogInfo("Invalid gump response, disconnecting...");
Expand All @@ -420,12 +442,14 @@ public static void DisplayGumpResponse(NetState state, SpanReader reader)
return;
}

var textEntries = new TextRelay[textCount];
Span<ushort> textIds = stackalloc ushort[textCount];
Span<Range> textFields = stackalloc Range[textCount];

for (var i = 0; i < textEntries.Length; ++i)
var textOffset = reader.Position;
for (var i = 0; i < textCount; i++)
{
int entryID = reader.ReadUInt16();
int textLength = reader.ReadUInt16();
var textId = reader.ReadUInt16();
var textLength = reader.ReadUInt16();

if (textLength > 239)
{
Expand All @@ -439,30 +463,39 @@ public static void DisplayGumpResponse(NetState state, SpanReader reader)
return;
}

var text = reader.ReadBigUniSafe(textLength);
textEntries[i] = new TextRelay(entryID, text);
textIds[i] = textId;
var offset = reader.Position - textOffset;
var length = textLength * 2;
textFields[i] = offset..(offset + length);
reader.Seek(length, SeekOrigin.Current);
}

var textBlock = reader.Buffer.Slice(textOffset, reader.Position - textOffset);

state.RemoveGump(gump);

var prof = GumpProfile.Acquire(gump.GetType());

prof?.Start();

var relayInfo = new RelayInfo(buttonID, switches, textEntries);
var relayInfo = new RelayInfo(
buttonId,
switches,
textIds,
textFields,
textBlock
);
gump.OnResponse(state, relayInfo);

prof?.Finish();

return;
}

if (typeID == 461)
if (typeId == 461)
{
// Virtue gump
var switchCount = reader.Remaining >= 4 ? reader.ReadInt32() : 0;

if (buttonID == 1 && switchCount > 0)
if (buttonId == 1 && switchCount > 0)
{
var beheld = World.FindEntity<PlayerMobile>((Serial)reader.ReadUInt32());

Expand All @@ -477,7 +510,7 @@ public static void DisplayGumpResponse(NetState state, SpanReader reader)

if (beheld != null)
{
VirtueGump.RequestVirtueItem((PlayerMobile)state.Mobile, beheld, buttonID);
VirtueGump.RequestVirtueItem((PlayerMobile)state.Mobile, beheld, buttonId);
}
}
}
Expand Down

0 comments on commit 76f132f

Please sign in to comment.