From 3a4dee0bf131b14329451237b93150cdc94bce1e Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Sun, 19 Jul 2026 22:03:17 -0700 Subject: [PATCH] =?UTF-8?q?fix(advanced-search):=20E=20=E2=80=94=20descend?= =?UTF-8?q?ing=20sort=20last=20page=20renders=20its=20items?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The paging loop in AdvancedSearchGump.Build assumed a full MaxEntries page: on a partial last page in descending mode, the first iteration's index already overflowed SearchResults.Length, and the break early-out killed the whole loop, rendering zero rows. Add a VisibleCount(total, displayFrom, maxEntries) helper and bound the loop to it instead of MaxEntries, generalizing the descending offset formula (MaxEntries - 1 - i -> visibleCount - 1 - i) so it stays valid for partial pages while being identical to the old formula on full pages. --- .../AdvancedSearchPagingTests.cs | 17 +++++++++++++++ .../Advanced Search/AdvancedSearchGump.cs | 21 ++++++++++++------- 2 files changed, 30 insertions(+), 8 deletions(-) create mode 100644 Projects/UOContent.Tests/Tests/Engines/AdvancedSearch/AdvancedSearchPagingTests.cs diff --git a/Projects/UOContent.Tests/Tests/Engines/AdvancedSearch/AdvancedSearchPagingTests.cs b/Projects/UOContent.Tests/Tests/Engines/AdvancedSearch/AdvancedSearchPagingTests.cs new file mode 100644 index 000000000..5166124e5 --- /dev/null +++ b/Projects/UOContent.Tests/Tests/Engines/AdvancedSearch/AdvancedSearchPagingTests.cs @@ -0,0 +1,17 @@ +using Server.Engines.AdvancedSearch; +using Xunit; + +namespace UOContent.Tests; + +public class AdvancedSearchPagingTests +{ + [Theory] + [InlineData(20, 0, 18, 18)] // full first page + [InlineData(20, 18, 18, 2)] // partial last page -> 2 visible (bug rendered 0 in descending) + [InlineData(5, 0, 18, 5)] + [InlineData(0, 0, 18, 0)] + public void VisibleCount_IsCorrect(int total, int from, int max, int expected) + { + Assert.Equal(expected, AdvancedSearchGump.VisibleCount(total, from, max)); + } +} diff --git a/Projects/UOContent/Engines/Advanced Search/AdvancedSearchGump.cs b/Projects/UOContent/Engines/Advanced Search/AdvancedSearchGump.cs index 8d4222aaf..9045adf08 100644 --- a/Projects/UOContent/Engines/Advanced Search/AdvancedSearchGump.cs +++ b/Projects/UOContent/Engines/Advanced Search/AdvancedSearchGump.cs @@ -130,6 +130,12 @@ public class AdvancedSearchGump : Gump public AdvancedSearchGump() : base(50, 50) => Build(); + // Number of result entries that actually fit on the current page, given how many + // remain after DisplayFrom. Prevents the paging loop from reading/rendering past + // the end of SearchResults on a partial last page. + internal static int VisibleCount(int total, int displayFrom, int maxEntries) => + Math.Clamp(total - displayFrom, 0, maxEntries); + private void Build() { const int height = 500; @@ -325,15 +331,14 @@ public class AdvancedSearchGump : Gump var allDisplayedSelected = true; - for (var i = 0; i < MaxEntries; i++) - { - var offset = SortDescending ? MaxEntries - 1 - i : i; - var index = offset + DisplayFrom; + // Bounded to the entries that actually exist on this page so a partial last + // page (fewer than MaxEntries remaining) still renders in descending mode. + var visibleCount = VisibleCount(SearchResults.Length, DisplayFrom, MaxEntries); - if (index >= SearchResults.Length) - { - break; - } + for (var i = 0; i < visibleCount; i++) + { + var offset = SortDescending ? visibleCount - 1 - i : i; + var index = offset + DisplayFrom; var entry = SearchResults[index];