using AcDream.Core.Items; namespace AcDream.Core.Net; /// /// Wires WorldSession GameMessage-level object events into the client object /// table: CreateObject (0xF745) = canonical merge-upsert, DeleteObject (0xF747) /// = evict, PublicUpdatePropertyInt (0x02CE) UiEffects = live icon re-composite. /// Keeps object ingestion in Core.Net (pure data, no GL) and off GameWindow. /// Retail: ACCObjectMaint::CreateObject / DeleteObject (the weenie_object_table side). /// public static class ObjectTableWiring { /// /// Subscribe to the three object-lifecycle events /// on . Call this BEFORE the render handler subscribes /// to EntitySpawned so the table is populated before the render path runs. /// public static void Wire(WorldSession session, ClientObjectTable table) { ArgumentNullException.ThrowIfNull(session); ArgumentNullException.ThrowIfNull(table); session.EntitySpawned += s => table.Ingest(ToWeenieData(s)); session.EntityDeleted += d => table.Remove(d.Guid); session.ObjectIntPropertyUpdated += u => { if (u.Property == ClientObjectTable.UiEffectsPropertyId) table.UpdateIntProperty(u.Guid, u.Property, u.Value); }; } /// Translate the wire spawn into the table's merge patch. public static WeenieData ToWeenieData(WorldSession.EntitySpawn s) => new( Guid: s.Guid, Name: s.Name, Type: s.ItemType is { } it ? (ItemType)it : (ItemType?)null, WeenieClassId: s.WeenieClassId, IconId: s.IconId, IconOverlayId: s.IconOverlayId, IconUnderlayId: s.IconUnderlayId, Effects: s.UiEffects, Value: s.Value, StackSize: s.StackSize, StackSizeMax: s.StackSizeMax, Burden: s.Burden, ContainerId: s.ContainerId, WielderId: s.WielderId, ValidLocations: s.ValidLocations, CurrentWieldedLocation: s.CurrentWieldedLocation, Priority: s.Priority, ItemsCapacity: s.ItemsCapacity, ContainersCapacity: s.ContainersCapacity, Structure: s.Structure, MaxStructure: s.MaxStructure, Workmanship: s.Workmanship); }