refactor(D.5.4): retire _liveEntityInfoByGuid; selection resolves from ClientObjectTable

The one weenie table now holds every object's name+type, so the redundant
Name+ItemType dictionary is gone (retail: one weenie_object_table).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-06-18 16:48:13 +02:00
parent 50cee50df1
commit a9d40addac

View file

@ -837,7 +837,6 @@ public sealed class GameWindow : IDisposable
/// keys the render list; this parallel dictionary keys by server guid.
/// </summary>
private readonly Dictionary<uint, AcDream.Core.World.WorldEntity> _entitiesByServerGuid = new();
private readonly Dictionary<uint, LiveEntityInfo> _liveEntityInfoByGuid = new();
/// <summary>
/// Latest <see cref="AcDream.Core.Net.WorldSession.EntitySpawn"/> for each
/// guid. Captured at the end of <see cref="OnLiveEntitySpawnedLocked"/> so
@ -854,9 +853,6 @@ public sealed class GameWindow : IDisposable
// far-range sends fire the wire packet immediately at SendUse/SendPickUp
// time. Cleared before the deferred send fires — single-fire, no retry.
private (uint Guid, bool IsPickup)? _pendingPostArrivalAction;
private readonly record struct LiveEntityInfo(
string? Name,
AcDream.Core.Items.ItemType ItemType);
private static bool IsPlayerGuid(uint guid) => (guid & 0xFF000000u) == 0x50000000u;
private const double ServerControlledVelocityStaleSeconds = 0.60;
private int _liveSpawnReceived; // diagnostics
@ -1302,7 +1298,7 @@ public sealed class GameWindow : IDisposable
// live state from this GameWindow instance every frame:
// - selected guid → _selectedGuid (set by PickAndStoreSelection)
// - entity resolver → position from _entitiesByServerGuid +
// itemType / PWD bits from cached LiveEntityInfo + last spawn
// itemType from ClientObjectTable (Objects) + last spawn
// - camera → _cameraController.Active or (zero) when not
// yet ready, in which case the panel bails on viewport==0.
_targetIndicator = new AcDream.App.UI.TargetIndicatorPanel(
@ -1311,9 +1307,7 @@ public sealed class GameWindow : IDisposable
{
if (!_entitiesByServerGuid.TryGetValue(guid, out var entity))
return null;
uint rawItemType = 0;
if (_liveEntityInfoByGuid.TryGetValue(guid, out var info))
rawItemType = (uint)info.ItemType;
uint rawItemType = (uint)LiveItemType(guid);
uint pwdBits = 0;
uint? useability = null;
if (_lastSpawnByGuid.TryGetValue(guid, out var spawn))
@ -2706,12 +2700,6 @@ public sealed class GameWindow : IDisposable
$"itemType={itemTypeStr} animParts={animPartCount} texChanges={texChangeCount} subPalettes={subPalCount}");
}
_liveEntityInfoByGuid[spawn.Guid] = new LiveEntityInfo(
spawn.Name,
spawn.ItemType is { } rawItemType
? (AcDream.Core.Items.ItemType)rawItemType
: AcDream.Core.Items.ItemType.None);
// Target the statue specifically for full diagnostic dump: Name match
// is cheap and gives us exactly one entity's worth of log regardless
// of arrival order.
@ -3717,7 +3705,6 @@ public sealed class GameWindow : IDisposable
// clear using the same guid the next spawn/update would use.
_remoteDeadReckon.Remove(serverGuid);
_remoteLastMove.Remove(serverGuid);
_liveEntityInfoByGuid.Remove(serverGuid);
_entitiesByServerGuid.Remove(serverGuid);
_lastSpawnByGuid.Remove(serverGuid);
if (_selectedGuid == serverGuid)
@ -3809,8 +3796,7 @@ public sealed class GameWindow : IDisposable
// Per-Door UM dispatch trail; grep [door-cycle] in launch.log to verify door animation.
if (AcDream.Core.Physics.PhysicsDiagnostics.ProbeBuildingEnabled
&& _liveEntityInfoByGuid.TryGetValue(update.Guid, out var doorInfo)
&& IsDoorName(doorInfo.Name))
&& IsDoorName(LiveName(update.Guid)))
{
Console.WriteLine(System.FormattableString.Invariant(
$"[door-cycle] guid=0x{update.Guid:X8} stance=0x{stance:X4} cmd=0x{(command ?? 0u):X4}"));
@ -11590,9 +11576,7 @@ public sealed class GameWindow : IDisposable
// RadarBlipColor are produced for the just-picked entity.
// Helps verify whether a "green NPC" really is flagged as
// Vendor server-side or whether our lookup is wrong.
uint rawItemType = 0;
if (_liveEntityInfoByGuid.TryGetValue(guid, out var info))
rawItemType = (uint)info.ItemType;
uint rawItemType = (uint)LiveItemType(guid);
uint pwdBits = 0;
uint? pickUseability = null;
float? pickUseRadius = null;
@ -11649,8 +11633,7 @@ public sealed class GameWindow : IDisposable
// Retail string at acclient_2013_pseudo_c.txt:1033115
// (data_7e2a70): "The %s cannot be used".
bool isCreature = _liveEntityInfoByGuid.TryGetValue(sel, out var info)
&& (info.ItemType & AcDream.Core.Items.ItemType.Creature) != 0;
bool isCreature = (LiveItemType(sel) & AcDream.Core.Items.ItemType.Creature) != 0;
if (isCreature)
{
@ -11891,8 +11874,7 @@ public sealed class GameWindow : IDisposable
// Mirror InstallSpeculativeTurnToTarget's per-type radius heuristic.
float useRadius = 0.6f;
if (_liveEntityInfoByGuid.TryGetValue(targetGuid, out var info)
&& (info.ItemType & AcDream.Core.Items.ItemType.Creature) != 0)
if ((LiveItemType(targetGuid) & AcDream.Core.Items.ItemType.Creature) != 0)
{
useRadius = 3.0f;
}
@ -11919,8 +11901,7 @@ public sealed class GameWindow : IDisposable
// Per-type use radius — same heuristic as the picker's
// radiusForGuid callback.
float useRadius = 0.6f;
if (_liveEntityInfoByGuid.TryGetValue(targetGuid, out var info)
&& (info.ItemType & AcDream.Core.Items.ItemType.Creature) != 0)
if ((LiveItemType(targetGuid) & AcDream.Core.Items.ItemType.Creature) != 0)
{
useRadius = 3.0f;
}
@ -12001,10 +11982,8 @@ public sealed class GameWindow : IDisposable
return false;
if (!_entitiesByServerGuid.ContainsKey(guid))
return false;
if (!_liveEntityInfoByGuid.TryGetValue(guid, out var info))
return false;
return (info.ItemType & AcDream.Core.Items.ItemType.Creature) != 0;
return (LiveItemType(guid) & AcDream.Core.Items.ItemType.Creature) != 0;
}
@ -12171,8 +12150,7 @@ public sealed class GameWindow : IDisposable
// `ItemUseable = null`; without the fallback the M1 "click NPC"
// flow regresses. The diagnostic line below lets us measure
// how often this branch fires in real play.
if (_liveEntityInfoByGuid.TryGetValue(guid, out var info)
&& (info.ItemType & AcDream.Core.Items.ItemType.Creature) != 0)
if ((LiveItemType(guid) & AcDream.Core.Items.ItemType.Creature) != 0)
{
if (AcDream.Core.Physics.PhysicsDiagnostics.ProbeUseabilityFallbackEnabled)
Console.WriteLine(System.FormattableString.Invariant(
@ -12280,11 +12258,15 @@ public sealed class GameWindow : IDisposable
return (it & SmallItemMask) != 0u;
}
private AcDream.Core.Items.ItemType LiveItemType(uint guid) =>
Objects.Get(guid)?.Type ?? AcDream.Core.Items.ItemType.None;
private string? LiveName(uint guid) => Objects.Get(guid)?.Name;
private string DescribeLiveEntity(uint guid)
{
if (_liveEntityInfoByGuid.TryGetValue(guid, out var info)
&& !string.IsNullOrWhiteSpace(info.Name))
return info.Name!;
var name = LiveName(guid);
if (!string.IsNullOrWhiteSpace(name)) return name!;
return $"0x{guid:X8}";
}