Warn when sending empty gumps

This commit is contained in:
Guflly 2026-08-07 21:13:17 -07:00
parent 23dc6649a0
commit 995f33b4b6
9 changed files with 104 additions and 0 deletions

View file

@ -0,0 +1,34 @@
using Server.Gumps;
namespace Server.Tests.Gumps;
public sealed class EmptyLegacyTestGump : Gump
{
public EmptyLegacyTestGump() : base(0, 0)
{
}
}
public sealed class EmptyDynamicTestGump : DynamicGump
{
public EmptyDynamicTestGump() : base(0, 0)
{
}
protected override void BuildLayout(ref DynamicGumpBuilder builder)
{
builder.AddPage();
}
}
public sealed class EmptyStaticTestGump : StaticGump<EmptyStaticTestGump>
{
public EmptyStaticTestGump() : base(0, 0)
{
}
protected override void BuildLayout(ref StaticGumpBuilder builder)
{
builder.SetNoClose();
}
}

View file

@ -73,6 +73,30 @@ public class TestLayoutGumps
AssertThat.Equal(writer.Span, packet);
}
[Fact]
public void TestEmptyGumpsHaveNoVisualElements()
{
Assert.False(Compile(new EmptyLegacyTestGump()).HasVisualElements);
Assert.False(Compile(new EmptyDynamicTestGump()).HasVisualElements);
Assert.False(Compile(new EmptyStaticTestGump()).HasVisualElements);
}
[Fact]
public void TestVisibleGumpsHaveVisualElements()
{
Assert.True(Compile(new LegacyTestGump("Test")).HasVisualElements);
Assert.True(Compile(new DynamicTestGump("Test")).HasVisualElements);
Assert.True(Compile(new StaticTestGump()).HasVisualElements);
}
private static BaseGump Compile(BaseGump gump)
{
var buffer = GC.AllocateUninitializedArray<byte>(512);
var writer = new SpanWriter(buffer);
gump.Compile(ref writer);
return gump;
}
private static void InternalTestStaticGump<T>(ReadOnlySpan<byte> expectedLayout, StaticGump<T> staticGump, string[] strings)
where T : StaticGump<T>
{

View file

@ -13,6 +13,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*************************************************************************/
using Server.Logging;
using Server.Network;
using System;
using System.Buffers;
@ -23,10 +24,14 @@ namespace Server.Gumps;
public abstract class BaseGump
{
private static readonly byte[] _packetBuffer = GC.AllocateUninitializedArray<byte>(0x10000);
#if DEBUG
private static readonly ILogger _logger = LogFactory.GetLogger(typeof(BaseGump));
#endif
private static Serial nextSerial = (Serial)1;
public int TypeID { get; protected set; }
public Serial Serial { get; protected set; }
internal bool HasVisualElements { get; set; }
public abstract int Switches { get; }
public abstract int TextEntries { get; }
@ -56,6 +61,13 @@ public abstract class BaseGump
var writer = new SpanWriter(_packetBuffer);
Compile(ref writer);
#if DEBUG
if (!HasVisualElements)
{
_logger.Warning("Sending empty gump {GumpType}", GetType().FullName);
}
#endif
ns.Send(writer.Span);
writer.Dispose();

View file

@ -47,6 +47,7 @@ public abstract class DynamicGump : BaseGump
BuildLayout(ref gumpBuilder);
gumpBuilder.FinalizeLayout();
HasVisualElements = gumpBuilder.HasVisualElements;
_switches = gumpBuilder.Switches;
_textEntries = gumpBuilder.TextEntries;

View file

@ -36,6 +36,7 @@ public ref struct DynamicGumpBuilder
public int Switches => _gumpBuilder._switches;
public int TextEntries => _gumpBuilder._textEntries;
internal bool HasVisualElements => _gumpBuilder._visualElements > 0;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public DynamicGumpBuilder()

View file

@ -30,6 +30,7 @@ public ref struct GumpLayoutBuilder
internal GumpFlags _flags;
internal int _switches;
internal int _textEntries;
internal int _visualElements;
internal Span<byte> LayoutData => _layoutBuffer.AsSpan(0, _bytesWritten);
@ -95,6 +96,7 @@ public ref struct GumpLayoutBuilder
public void AddBackground(int x, int y, int width, int height, int gumpId)
{
_visualElements++;
GrowIfNeeded(9 + 9 + 45);
WriteStart("resizepic"u8);
WriteValue(x);
@ -109,6 +111,7 @@ public ref struct GumpLayoutBuilder
int x, int y, int normalId, int pressedId, int buttonId, GumpButtonType type = GumpButtonType.Reply, int param = 0
)
{
_visualElements++;
GrowIfNeeded(11 + 6 + 54 + 2);
WriteStart("button"u8);
WriteValue(x);
@ -123,6 +126,7 @@ public ref struct GumpLayoutBuilder
public void AddCheckbox(int x, int y, int inactiveId, int activeId, bool selected, int switchId)
{
_visualElements++;
GrowIfNeeded(10 + 8 + 45 + 2);
WriteStart("checkbox"u8);
WriteValue(x);
@ -154,6 +158,7 @@ public ref struct GumpLayoutBuilder
public int AddHtmlPlaceholder(int x, int y, int width, int height,
bool background = false, bool scrollbar = false)
{
_visualElements++;
GrowIfNeeded(11 + 8 + 36 + 10);
WriteStart("htmlgump"u8);
WriteValue(x);
@ -172,6 +177,7 @@ public ref struct GumpLayoutBuilder
public void AddHtml(int x, int y, int width, int height, int text,
bool background = false, bool scrollbar = false)
{
_visualElements++;
GrowIfNeeded(11 + 8 + 45 + 4);
WriteStart("htmlgump"u8);
WriteValue(x);
@ -188,6 +194,7 @@ public ref struct GumpLayoutBuilder
int x, int y, int width, int height, int number, bool background = false, bool scrollbar = false
)
{
_visualElements++;
GrowIfNeeded(11 + 11 + 45 + 4);
WriteStart("xmfhtmlgump"u8);
WriteValue(x);
@ -204,6 +211,7 @@ public ref struct GumpLayoutBuilder
int x, int y, int width, int height, int number, int color, bool background = false, bool scrollbar = false
)
{
_visualElements++;
GrowIfNeeded(12 + 16 + 45 + 5 + 4);
WriteStart("xmfhtmlgumpcolor"u8);
WriteValue(x);
@ -220,6 +228,7 @@ public ref struct GumpLayoutBuilder
public void AddHtmlLocalized(int x, int y, int width, int height, int number, ReadOnlySpan<char> args, int color,
bool background = false, bool scrollbar = false)
{
_visualElements++;
GrowIfNeeded(12 + 10 + 45 + 5 + 4 + (args.Length > 0 ? 3 + args.Length : 0));
WriteStart("xmfhtmltok"u8);
WriteValue(x);
@ -254,6 +263,7 @@ public ref struct GumpLayoutBuilder
public void AddImage(int x, int y, int gumpId, int hue = 0, ReadOnlySpan<char> cls = default)
{
_visualElements++;
GrowIfNeeded(7 + 7 + 36 + (hue != 0 ? 14 : 0) + (cls.Length > 0 ? 7 + cls.Length : 0));
WriteStart("gumppic"u8);
WriteValue(x);
@ -294,6 +304,7 @@ public ref struct GumpLayoutBuilder
public void AddImageTiledButton(int x, int y, int normalId, int pressedId, int buttonId, GumpButtonType type, int param,
int itemId, int hue, int width, int height, int localizedTooltip = -1)
{
_visualElements++;
GrowIfNeeded(15 + 13 + 90 + 2);
WriteStart("buttontileart"u8);
WriteValue(x);
@ -319,6 +330,7 @@ public ref struct GumpLayoutBuilder
public void AddImageTiled(int x, int y, int width, int height, int gumpId)
{
_visualElements++;
GrowIfNeeded(9 + 12 + 45);
WriteStart("gumppictiled"u8);
WriteValue(x);
@ -331,6 +343,7 @@ public ref struct GumpLayoutBuilder
public void AddItem(int x, int y, int itemId, int hue = 0)
{
_visualElements++;
GrowIfNeeded(7 + 36 + (hue != 0 ? 20 : 7));
WriteStart(hue == 0 ? "tilepic"u8 : "tilepichue"u8);
WriteValue(x);
@ -355,6 +368,7 @@ public ref struct GumpLayoutBuilder
public int AddLabelPlaceholder(int x, int y, int hue)
{
_visualElements++;
GrowIfNeeded(8 + 4 + 27 + 6);
WriteStart("text"u8);
WriteValue(x);
@ -368,6 +382,7 @@ public ref struct GumpLayoutBuilder
public void AddLabel(int x, int y, int hue, int text)
{
_visualElements++;
GrowIfNeeded(8 + 4 + 36 + 6);
WriteStart("text"u8);
WriteValue(x);
@ -379,6 +394,7 @@ public ref struct GumpLayoutBuilder
public int AddLabelCroppedPlaceholder(int x, int y, int width, int height, int hue)
{
_visualElements++;
GrowIfNeeded(10 + 11 + 45 + 6);
WriteStart("croppedtext"u8);
WriteValue(x);
@ -394,6 +410,7 @@ public ref struct GumpLayoutBuilder
public void AddLabelCropped(int x, int y, int width, int height, int hue, int text)
{
_visualElements++;
GrowIfNeeded(10 + 11 + 54 + 6);
WriteStart("croppedtext"u8);
WriteValue(x);
@ -430,6 +447,7 @@ public ref struct GumpLayoutBuilder
public void AddRadio(int x, int y, int inactiveId, int activeId, bool selected, int switchId)
{
_visualElements++;
GrowIfNeeded(10 + 5 + 45 + 2);
WriteStart("radio"u8);
WriteValue(x);
@ -445,6 +463,7 @@ public ref struct GumpLayoutBuilder
public void AddSpriteImage(int x, int y, int gumpId, int width, int height, int sx, int sy)
{
_visualElements++;
GrowIfNeeded(11 + 8 + 63);
WriteStart("picinpic"u8);
WriteValue(x);
@ -461,6 +480,7 @@ public ref struct GumpLayoutBuilder
int x, int y, int width, int height, int hue, int entryId
)
{
_visualElements++;
GrowIfNeeded(11 + 9 + 54 + 6);
WriteStart("textentry"u8);
WriteValue(x);
@ -481,6 +501,7 @@ public ref struct GumpLayoutBuilder
int x, int y, int width, int height, int hue, int entryId, int initialText
)
{
_visualElements++;
GrowIfNeeded(11 + 9 + 63 + 6);
WriteStart("textentry"u8);
WriteValue(x);
@ -499,6 +520,7 @@ public ref struct GumpLayoutBuilder
int x, int y, int width, int height, int hue, int entryId, int size = 0
)
{
_visualElements++;
GrowIfNeeded(12 + 16 + 63 + 6);
WriteStart("textentrylimited"u8);
WriteValue(x);
@ -520,6 +542,7 @@ public ref struct GumpLayoutBuilder
int x, int y, int width, int height, int hue, int entryId, int initialText, int size = 0
)
{
_visualElements++;
GrowIfNeeded(12 + 16 + 72);
WriteStart("textentrylimited"u8);
WriteValue(x);

View file

@ -249,6 +249,7 @@ public class Gump : BaseGump
{
_textEntries = 0;
_switches = 0;
HasVisualElements = false;
var layoutWriter = new SpanWriter(_layoutBuffer);
@ -274,6 +275,7 @@ public class Gump : BaseGump
foreach (var entry in Entries)
{
HasVisualElements |= IsVisualEntry(entry);
entry.AppendTo(ref layoutWriter, _stringsList, ref _textEntries, ref _switches);
}
@ -311,6 +313,9 @@ public class Gump : BaseGump
}
}
private static bool IsVisualEntry(GumpEntry entry) =>
entry is not (GumpAlphaRegion or GumpECHandleInput or GumpGroup or GumpItemProperty or GumpMasterGump or GumpPage or GumpTooltip);
protected void Reset()
{
_switches = 0;

View file

@ -30,6 +30,7 @@ public abstract class StaticGump<TSelf> : BaseGump where TSelf : StaticGump<TSel
private static byte[] _compressedStringsData;
private static bool _hasDynamicStrings;
private static bool _hasVisualElements;
private static int _staticStringsCount;
private static byte[] _staticStrings;
@ -72,6 +73,7 @@ public abstract class StaticGump<TSelf> : BaseGump where TSelf : StaticGump<TSel
if (Cached && _compressedLayoutData != null)
{
HasVisualElements = _hasVisualElements;
writer.Write(_compressedLayoutData);
if (_compressedStringsData != null)
@ -116,6 +118,7 @@ public abstract class StaticGump<TSelf> : BaseGump where TSelf : StaticGump<TSel
BuildLayout(ref gumpBuilder);
gumpBuilder.FinalizeLayout();
HasVisualElements = _hasVisualElements = gumpBuilder.HasVisualElements;
_switches = gumpBuilder.Switches;
_textEntries = gumpBuilder.TextEntries;
_staticStringsCount = gumpBuilder._stringsCount;

View file

@ -43,6 +43,7 @@ public ref struct StaticGumpBuilder
public int Switches => _gumpBuilder._switches;
public int TextEntries => _gumpBuilder._textEntries;
internal bool HasVisualElements => _gumpBuilder._visualElements > 0;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public StaticGumpBuilder()