65 lines
3.1 KiB
C#
65 lines
3.1 KiB
C#
using System.Collections.Frozen;
|
|
using System.Diagnostics;
|
|
|
|
namespace ICRebuild.Core;
|
|
|
|
public sealed record GameFrame(bool Ready, nint LocalAddress, bool IsPvP, bool IsSlowed, DungeonSnapshot Dungeon,nint ActionManagerAddress = 0,nint TargetAddress = 0,uint TargetEntityId = 0,
|
|
nint LocalStatusManagerAddress = 0, uint LocalEntityId = 0, bool IsReplay = false)
|
|
{
|
|
public static readonly GameFrame Empty = new(false,0,false,false,DungeonSnapshot.Empty);
|
|
}
|
|
|
|
// Producers sample thread-affine services only on Framework.Update. Native workers read values only.
|
|
public sealed class FrameStore(Func<long>? clock = null, long? maximumAge = null)
|
|
{
|
|
private sealed record Published(GameFrame Frame,long Time);
|
|
private readonly Func<long> now = clock ?? Stopwatch.GetTimestamp;
|
|
private readonly long ttl = maximumAge ?? Stopwatch.Frequency / 2;
|
|
private Published? current;
|
|
public void Publish(GameFrame frame) => Volatile.Write(ref current,new(frame,now()));
|
|
public void Clear() => Volatile.Write(ref current,null);
|
|
public GameFrame Read()
|
|
{
|
|
var value=Volatile.Read(ref current);
|
|
if(value==null) return GameFrame.Empty;
|
|
var age=now()-value.Time;
|
|
return age<0 || age>ttl ? GameFrame.Empty : value.Frame;
|
|
}
|
|
}
|
|
|
|
public sealed class SettingsSnapshot
|
|
{
|
|
private readonly float[] values=new float[44];
|
|
public FrozenSet<uint> Timelines { get; }
|
|
public bool BlockListed { get; }
|
|
public DashFilterMode DashMode { get; }
|
|
public float KnockSeconds { get; }
|
|
public FrozenSet<uint> BlockedStatuses { get; }
|
|
public SettingsSnapshot(IReadOnlyDictionary<int,float> source,IEnumerable<uint> timelines,bool blockListed,float knockSeconds,DashFilterMode dashMode = DashFilterMode.BlockListed,
|
|
IEnumerable<uint>? blockedStatuses = null)
|
|
{
|
|
foreach(var f in Features.All) values[f.Id]=Policies.FiniteClamp(source.GetValueOrDefault(f.Id,f.Default),f.Min,f.Max,f.Default);
|
|
Timelines=timelines.Where(x=>x>0).ToFrozenSet();
|
|
BlockListed=blockListed; KnockSeconds=Policies.FiniteClamp(knockSeconds,0,5);
|
|
DashMode=dashMode;
|
|
BlockedStatuses=(blockedStatuses ?? []).Where(StatusCatalog.AuditedBlockCandidates.Contains).ToFrozenSet();
|
|
}
|
|
public float Value(int id) => (uint)id<(uint)values.Length ? values[id] : 0;
|
|
}
|
|
|
|
public static class AccelerationPolicy
|
|
{
|
|
// Audited in 2026.09.15 and 2026.10.06 executables: native routine consumes and updates +0x44.
|
|
// The returned state belongs to the game and MUST NOT be restored to the value before the call.
|
|
public const string AuditedVersion="2026.10.06.0000.0000";
|
|
public const string BaselineVersion="2026.09.15.0000.0000";
|
|
public static bool IsAuditedVersion(string? version) => version is AuditedVersion or BaselineVersion;
|
|
public static bool ShouldPrime(GameFrame frame,nint owner,bool moving,float current) =>
|
|
frame.Ready && !frame.IsSlowed && frame.LocalAddress!=0 && owner==frame.LocalAddress && moving && float.IsFinite(current);
|
|
public static void Run(ref float state,bool prime,Action original)
|
|
{
|
|
if(prime) state=100;
|
|
original();
|
|
}
|
|
}
|