using System.Numerics; using Nexus.Core; using Nexus.Memory; namespace Nexus.Data; /// /// Immutable snapshot of player position for lock-free cross-thread reads. /// Class (reference type) so volatile assignment is atomic. /// public sealed class PlayerPositionData { public readonly float X, Y, Z; public readonly bool HasPosition; private PlayerPositionData() { } public PlayerPositionData(float x, float y, float z) { X = x; Y = y; Z = z; HasPosition = true; } public static readonly PlayerPositionData Empty = new(); } /// /// Immutable snapshot of player vitals for lock-free cross-thread reads. /// Class (reference type) so volatile assignment is atomic. /// public sealed class PlayerVitalsData { public readonly int LifeCurrent, LifeTotal; public readonly int ManaCurrent, ManaTotal; public readonly int EsCurrent, EsTotal; public readonly bool HasVitals; private PlayerVitalsData() { } public PlayerVitalsData(int lifeCur, int lifeMax, int manaCur, int manaMax, int esCur, int esMax) { LifeCurrent = lifeCur; LifeTotal = lifeMax; ManaCurrent = manaCur; ManaTotal = manaMax; EsCurrent = esCur; EsTotal = esMax; HasVitals = true; } public static readonly PlayerVitalsData Empty = new(); } /// /// Immutable wrapper for Matrix4x4 so volatile reference assignment works. /// public sealed class CameraMatrixData { public readonly Matrix4x4 Matrix; public CameraMatrixData(Matrix4x4 matrix) { Matrix = matrix; } } /// /// Centralized memory cache — single source of truth for all game data. /// Hot fields updated at 60Hz (4 RPM calls), cold fields at 10Hz (full snapshot). /// All fields use volatile reference types or atomic primitives for lock-free cross-thread reads. /// public sealed class GameDataCache { // ── Hot data (updated at 60Hz) ── public volatile CameraMatrixData? CameraMatrix; public volatile PlayerPositionData PlayerPosition = PlayerPositionData.Empty; public volatile PlayerVitalsData PlayerVitals = PlayerVitalsData.Empty; public volatile bool IsLoading; public volatile bool IsEscapeOpen; // ── Cold data (updated at 10Hz) ── public volatile IReadOnlyList Entities = []; public volatile IReadOnlyList HostileMonsters = []; public volatile IReadOnlyList NearbyLoot = []; public volatile WalkabilitySnapshot? Terrain; public volatile uint AreaHash; public volatile int AreaLevel; public volatile string? CurrentAreaName; public volatile string? CharacterName; // ── UI tree root pointer (updated at 10Hz) — tree is read on-demand ── public volatile nint GameUiPtr; // ── Quest groups from UI element tree (updated at 10Hz) ── public volatile IReadOnlyList? UiQuestGroups; // ── Quest linked lists from GameUi (updated at 10Hz) ── public volatile IReadOnlyList? QuestLinkedList; // ── Quest states from AreaInstance sub-object (updated at 10Hz) ── public volatile IReadOnlyList? QuestStates; // ── Full GameState (updated at 10Hz) — for systems that need the complete object ── public volatile GameState? LatestState; // ── Timestamps ── private long _hotTickTimestamp; private long _coldTickTimestamp; public long HotTickTimestamp { get => Interlocked.Read(ref _hotTickTimestamp); set => Interlocked.Exchange(ref _hotTickTimestamp, value); } public long ColdTickTimestamp { get => Interlocked.Read(ref _coldTickTimestamp); set => Interlocked.Exchange(ref _coldTickTimestamp, value); } }