using System; using System.Threading; namespace game.PlayerDataStorage { internal enum PlayerKVGuardPoint { Compare, ReadLocal, Update, Immediate, Sync, Lifecycle, PrefsSave } internal sealed class PlayerKVLoadSideEffectException : InvalidOperationException { internal PlayerKVLoadSideEffectException() : base("KV controlled load rejected a legacy side effect.") { } } internal sealed class PlayerKVGameLoadGuard : IDisposable { static readonly object gate=new object(); static PlayerKVGameLoadGuard owner; readonly int[] counts=new int[7]; int violations; bool disposed; private PlayerKVGameLoadGuard() { } internal static bool TryEnter(out PlayerKVGameLoadGuard lease) { lock(gate) { lease=null; if(owner!=null) return false; owner=lease=new PlayerKVGameLoadGuard(); return true; } } internal bool IsCurrent { get { lock(gate) return !disposed && ReferenceEquals(owner,this); } } internal int Violations => Volatile.Read(ref violations); internal int Count(PlayerKVGuardPoint point) => Volatile.Read(ref counts[(int)point]); internal static bool Reject(PlayerKVGuardPoint point) { lock(gate) { if(owner==null) return false; Interlocked.Increment(ref owner.counts[(int)point]); Interlocked.Increment(ref owner.violations); return true; } } internal static void RequireLegacyAllowed(PlayerKVGuardPoint point) { if(Reject(point)) throw new PlayerKVLoadSideEffectException(); } public void Dispose() { lock(gate) { if(disposed) return; disposed=true; if(ReferenceEquals(owner,this)) owner=null; } } } }