fix: correctness bugs flagged by CodeRabbit

Four pre-existing upstream defects fixed in v1.0.0:

- Util/GlobalParametersCache.cs GetValue captures Cache into a local
  before the bounds check, so the check and the indexed read operate
  on the same array reference even when Refresh reassigns Cache from
  the main thread between the two operations
- Util/IconUtil.cs binary search bounds: hi initialized to
  entries.Length-1 (was Length), and reset on redirect-restart;
  added entries.Length==0 short-circuit to prevent indexing into
  empty arrays
- Sheets.cs WorldsOnDatacenter compared Region.RowId, which groups
  by region instead of datacenter — now compares DataCenter.RowId
  directly so the result actually reflects same-DC worlds
- Message.cs back-reference loop iterates the processed Sender/Content
  properties rather than the raw constructor parameters, so chunks
  added or replaced by CheckMessageContent also get Message set
This commit is contained in:
2026-05-03 22:03:47 +02:00
parent 3f2e56be67
commit a651b3b9ad
4 changed files with 20 additions and 7 deletions
+4 -1
View File
@@ -49,7 +49,10 @@ public partial class Message
ExtraChatChannel = extraChatChannel; ExtraChatChannel = extraChatChannel;
Hash = GenerateHash(); Hash = GenerateHash();
foreach (var chunk in sender.Concat(content)) // Iterate the processed Content list (returned by CheckMessageContent)
// rather than the raw constructor parameter — chunks added or replaced
// by CheckMessageContent must also have their back-reference set.
foreach (var chunk in Sender.Concat(Content))
chunk.Message = this; chunk.Message = this;
} }
+2 -2
View File
@@ -37,7 +37,7 @@ public static class Sheets
public static IEnumerable<World> WorldsOnDatacenter(IPlayerCharacter character) public static IEnumerable<World> WorldsOnDatacenter(IPlayerCharacter character)
{ {
var dcRow = character.HomeWorld.Value.DataCenter.Value.Region.RowId; var dcRow = character.HomeWorld.Value.DataCenter.RowId;
return WorldSheet.Where(world => world.IsPublic && world.DataCenter.Value.Region.RowId == dcRow); return WorldSheet.Where(world => world.IsPublic && world.DataCenter.RowId == dcRow);
} }
} }
+6 -2
View File
@@ -10,10 +10,14 @@ public static class GlobalParametersCache
public static int GetValue(int index) public static int GetValue(int index)
{ {
if (index < 0 || index >= Cache.Length) // Capture the array reference once so the bounds check and the
// indexed read operate on the same instance, even if Refresh
// reassigns Cache between the two operations.
var cache = Cache;
if (index < 0 || index >= cache.Length)
return 0; return 0;
return Cache[index]; return cache[index];
} }
/// <summary> /// <summary>
+8 -2
View File
@@ -59,8 +59,14 @@ public readonly unsafe ref struct GfdFileView
return false; return false;
} }
if (entries.Length == 0)
{
entry = default;
return false;
}
var lo = 0; var lo = 0;
var hi = entries.Length; var hi = entries.Length - 1;
while (lo <= hi) while (lo <= hi)
{ {
var i = lo + ((hi - lo) >> 1); var i = lo + ((hi - lo) >> 1);
@@ -70,7 +76,7 @@ public readonly unsafe ref struct GfdFileView
{ {
iconId = entries[i].Redirect; iconId = entries[i].Redirect;
lo = 0; lo = 0;
hi = entries.Length; hi = entries.Length - 1;
continue; continue;
} }