fix: Make RelayInfo zero allocation (#1699)

This commit is contained in:
Stefano Merotta 2024-03-09 07:58:32 +01:00 committed by GitHub
parent cfe9e04c78
commit 76f132fd49
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 79 additions and 64 deletions

View file

@ -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);
}
}

View file

@ -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;
@ -343,29 +347,37 @@ public static class IncomingPlayerPackets
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;
@ -376,7 +388,7 @@ public static class IncomingPlayerPackets
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.");
@ -399,15 +411,25 @@ public static class IncomingPlayerPackets
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));
for (var i = 0; i < switches.Length; ++i)
scoped ReadOnlySpan<int> switches;
// Swap the endianness if necessary
if (BitConverter.IsLittleEndian)
{
switches[i] = reader.ReadInt32();
Span<int> reversedSwitches = stackalloc int[switchCount];
BinaryPrimitives.ReverseEndianness(switchBlock, reversedSwitches);
switches = reversedSwitches;
}
else
{
switches = switchBlock;
}
var textCount = reader.ReadInt32();
if (textCount < 0 || textCount > gump.TextEntries)
{
state.LogInfo("Invalid gump response, disconnecting...");
@ -420,12 +442,14 @@ public static class IncomingPlayerPackets
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)
{
@ -439,30 +463,39 @@ public static class IncomingPlayerPackets
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());
@ -477,7 +510,7 @@ public static class IncomingPlayerPackets
if (beheld != null)
{
VirtueGump.RequestVirtueItem((PlayerMobile)state.Mobile, beheld, buttonID);
VirtueGump.RequestVirtueItem((PlayerMobile)state.Mobile, beheld, buttonId);
}
}
}