Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions docs/adr/0004-capability-matrix.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,18 @@ only because an abstraction can describe it.
assuming an unproven SDK ownership, affinity, or cancellation contract.
- **Deferred**: V1 intentionally provides no operational route through the aggregate Client.

### Runtime evidence

`ClientCapabilityAvailability.Evidence` records six independent gates for every Client capability:
implementation, the consumed package artifact, host observation, live qualification, activation policy, and
activation lifetime. The legacy `State` is only a projection of that evidence: it is `Available` when every gate is
satisfied, `Unavailable` when a gate is known to be missing, and `Unknown` when the remaining evidence is unknown,
faulted, or malformed. A faulted or malformed host observation is deliberately not reported as an unavailable host.

An explicit `EnableUnsafeLuaExecution()` opt-in satisfies the policy gate only. It never establishes that the package
contains an implementation, that the active Cheat Engine host exposes the required globals, or that the operation has
been live-qualified.

| Area | Public surface | Current source status | Boundary before live qualification |
|---|---|---|---|
| Plugin lifecycle and DI | `CheatEngineClientPlugin`, `CheatEnginePluginBuilder`, modules, options | Implemented | Exercise enable, rollback, disable, and repeated epoch activation in a CE host. |
Expand Down
38 changes: 38 additions & 0 deletions libs/CheatEngine.Client.Abstractions/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,44 @@ static CheatEngine.Client.Runtime.ClientCapabilityId.Hotkeys.get -> CheatEngine.
static CheatEngine.Client.Runtime.ClientCapabilityId.RemoteExecution.get -> CheatEngine.Client.Runtime.ClientCapabilityId
static CheatEngine.Client.Runtime.ClientCapabilityId.Speed.get -> CheatEngine.Client.Runtime.ClientCapabilityId
static CheatEngine.Client.Runtime.ClientCapabilityId.Timers.get -> CheatEngine.Client.Runtime.ClientCapabilityId
CheatEngine.Client.Runtime.ClientCapabilityAvailability.ClientCapabilityAvailability(CheatEngine.Client.Runtime.ClientCapabilityId capability, CheatEngine.Client.Runtime.ClientCapabilityEvidence evidence) -> void
CheatEngine.Client.Runtime.ClientCapabilityAvailability.Evidence.get -> CheatEngine.Client.Runtime.ClientCapabilityEvidence
CheatEngine.Client.Runtime.ClientCapabilityEvidence
CheatEngine.Client.Runtime.ClientCapabilityEvidence.ClientCapabilityEvidence() -> void
CheatEngine.Client.Runtime.ClientCapabilityEvidence.ClientCapabilityEvidence(CheatEngine.Client.Runtime.ClientCapabilityEvidenceGate implementation, CheatEngine.Client.Runtime.ClientCapabilityEvidenceGate package, CheatEngine.Client.Runtime.ClientCapabilityEvidenceGate host, CheatEngine.Client.Runtime.ClientCapabilityEvidenceGate liveQualification, CheatEngine.Client.Runtime.ClientCapabilityEvidenceGate policy, CheatEngine.Client.Runtime.ClientCapabilityEvidenceGate lifetime) -> void
CheatEngine.Client.Runtime.ClientCapabilityEvidence.AvailabilityState.get -> CheatEngine.Client.Runtime.ClientCapabilityAvailabilityState
CheatEngine.Client.Runtime.ClientCapabilityEvidence.EffectiveReason.get -> string!
CheatEngine.Client.Runtime.ClientCapabilityEvidence.Host.get -> CheatEngine.Client.Runtime.ClientCapabilityEvidenceGate
CheatEngine.Client.Runtime.ClientCapabilityEvidence.Implementation.get -> CheatEngine.Client.Runtime.ClientCapabilityEvidenceGate
CheatEngine.Client.Runtime.ClientCapabilityEvidence.IsExecutable.get -> bool
CheatEngine.Client.Runtime.ClientCapabilityEvidence.Lifetime.get -> CheatEngine.Client.Runtime.ClientCapabilityEvidenceGate
CheatEngine.Client.Runtime.ClientCapabilityEvidence.LiveQualification.get -> CheatEngine.Client.Runtime.ClientCapabilityEvidenceGate
CheatEngine.Client.Runtime.ClientCapabilityEvidence.Package.get -> CheatEngine.Client.Runtime.ClientCapabilityEvidenceGate
CheatEngine.Client.Runtime.ClientCapabilityEvidence.Policy.get -> CheatEngine.Client.Runtime.ClientCapabilityEvidenceGate
CheatEngine.Client.Runtime.ClientCapabilityEvidence.Equals(CheatEngine.Client.Runtime.ClientCapabilityEvidence other) -> bool
CheatEngine.Client.Runtime.ClientCapabilityEvidenceGate
CheatEngine.Client.Runtime.ClientCapabilityEvidenceGate.ClientCapabilityEvidenceGate() -> void
CheatEngine.Client.Runtime.ClientCapabilityEvidenceGate.ClientCapabilityEvidenceGate(CheatEngine.Client.Runtime.ClientCapabilityEvidenceState state, string! reason) -> void
CheatEngine.Client.Runtime.ClientCapabilityEvidenceGate.IsSatisfied.get -> bool
CheatEngine.Client.Runtime.ClientCapabilityEvidenceGate.Reason.get -> string!
CheatEngine.Client.Runtime.ClientCapabilityEvidenceGate.State.get -> CheatEngine.Client.Runtime.ClientCapabilityEvidenceState
CheatEngine.Client.Runtime.ClientCapabilityEvidenceGate.Equals(CheatEngine.Client.Runtime.ClientCapabilityEvidenceGate other) -> bool
CheatEngine.Client.Runtime.ClientCapabilityEvidenceState
CheatEngine.Client.Runtime.ClientCapabilityEvidenceState.Faulted = 3 -> CheatEngine.Client.Runtime.ClientCapabilityEvidenceState
CheatEngine.Client.Runtime.ClientCapabilityEvidenceState.Malformed = 4 -> CheatEngine.Client.Runtime.ClientCapabilityEvidenceState
CheatEngine.Client.Runtime.ClientCapabilityEvidenceState.Missing = 2 -> CheatEngine.Client.Runtime.ClientCapabilityEvidenceState
CheatEngine.Client.Runtime.ClientCapabilityEvidenceState.Satisfied = 1 -> CheatEngine.Client.Runtime.ClientCapabilityEvidenceState
CheatEngine.Client.Runtime.ClientCapabilityEvidenceState.Unknown = 0 -> CheatEngine.Client.Runtime.ClientCapabilityEvidenceState
override CheatEngine.Client.Runtime.ClientCapabilityEvidence.GetHashCode() -> int
override CheatEngine.Client.Runtime.ClientCapabilityEvidenceGate.GetHashCode() -> int
static CheatEngine.Client.Runtime.ClientCapabilityEvidence.operator !=(CheatEngine.Client.Runtime.ClientCapabilityEvidence left, CheatEngine.Client.Runtime.ClientCapabilityEvidence right) -> bool
static CheatEngine.Client.Runtime.ClientCapabilityEvidence.operator ==(CheatEngine.Client.Runtime.ClientCapabilityEvidence left, CheatEngine.Client.Runtime.ClientCapabilityEvidence right) -> bool
static CheatEngine.Client.Runtime.ClientCapabilityEvidenceGate.operator !=(CheatEngine.Client.Runtime.ClientCapabilityEvidenceGate left, CheatEngine.Client.Runtime.ClientCapabilityEvidenceGate right) -> bool
static CheatEngine.Client.Runtime.ClientCapabilityEvidenceGate.operator ==(CheatEngine.Client.Runtime.ClientCapabilityEvidenceGate left, CheatEngine.Client.Runtime.ClientCapabilityEvidenceGate right) -> bool
~override CheatEngine.Client.Runtime.ClientCapabilityEvidence.Equals(object obj) -> bool
~override CheatEngine.Client.Runtime.ClientCapabilityEvidence.ToString() -> string
~override CheatEngine.Client.Runtime.ClientCapabilityEvidenceGate.Equals(object obj) -> bool
~override CheatEngine.Client.Runtime.ClientCapabilityEvidenceGate.ToString() -> string
CheatEngine.Client.Processes.IProcessClient.AttachForeground(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> CheatEngine.Client.Processes.ProcessSnapshot
CheatEngine.Client.Processes.IProcessClient.Create(CheatEngine.Client.Processes.ProcessStartRequest request, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> CheatEngine.Client.Processes.ProcessSnapshot
CheatEngine.Client.Processes.IProcessClient.GetPauseState(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> CheatEngine.Client.Processes.ProcessPauseSnapshot
Expand Down
5 changes: 5 additions & 0 deletions libs/CheatEngine.Client.Abstractions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@ The contracts describe runtime, process, memory, inspection, AOB scanning, table
and explicitly disposable value-scan sessions. A contract is not an availability promise: callers
must inspect `ICheatEngineRuntime` capability observations or handle `CapabilityUnavailable`.

Each `ClientCapabilityAvailability` also exposes immutable `Evidence`: implementation, consumed package artifact,
host observation, live qualification, policy, and activation lifetime are distinct gates. `Available` requires all six;
missing, faulted, and malformed host observations remain distinguishable instead of being collapsed into a generic
unavailable result.

In particular, the value-scan contract and state model are published, but the Core implementation
does **not** currently create a live `MemScan`/`FoundList` session. The next SDK line now contains a
production owner factory with parent rollback and child-before-parent teardown, but Client
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public static ClientCapabilities Create(ReadOnlySpan<ClientCapabilityAvailabilit
ClientCapabilityAvailability[] copy = entries.ToArray();
for (int index = 0; index < copy.Length; index++)
{
_ = new ClientCapabilityAvailability(copy[index].Capability, copy[index].State, copy[index].Reason);
_ = new ClientCapabilityAvailability(copy[index].Capability, copy[index].Evidence);
for (int previous = 0; previous < index; previous++)
{
if (copy[previous].Capability == copy[index].Capability)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,24 @@ public ClientCapabilityAvailability(
ArgumentException.ThrowIfNullOrWhiteSpace(reason);

Capability = capability;
State = state;
Reason = reason;
Evidence = CreateLegacyEvidence(state, reason);
State = Evidence.AvailabilityState;
Reason = Evidence.EffectiveReason;
}

/// <summary>Creates an availability projection from independently sourced prerequisite evidence.</summary>
public ClientCapabilityAvailability(ClientCapabilityId capability, ClientCapabilityEvidence evidence)
{
if (capability.IsEmpty)
{
throw new ArgumentException("A Client capability identifier is required.", nameof(capability));
}

_ = evidence.EffectiveReason;
Capability = capability;
Evidence = evidence;
State = evidence.AvailabilityState;
Reason = evidence.EffectiveReason;
}

/// <summary>Gets the stable Client-owned capability identifier.</summary>
Expand All @@ -44,6 +60,12 @@ public ClientCapabilityAvailabilityState State
get;
}

/// <summary>Gets the separately observed implementation, package, host, qualification, policy, and lifetime gates.</summary>
public ClientCapabilityEvidence Evidence
{
get;
}

/// <summary>Gets the explicit probe, policy, or gate reason behind the state.</summary>
public string Reason
{
Expand All @@ -55,4 +77,16 @@ public string Reason

/// <summary>Gets whether this capability was explicitly established as available or unavailable.</summary>
public bool IsKnown => State != ClientCapabilityAvailabilityState.Unknown;

private static ClientCapabilityEvidence CreateLegacyEvidence(ClientCapabilityAvailabilityState state, string reason)
{
ClientCapabilityEvidenceState evidenceState = state switch
{
ClientCapabilityAvailabilityState.Available => ClientCapabilityEvidenceState.Satisfied,
ClientCapabilityAvailabilityState.Unavailable => ClientCapabilityEvidenceState.Missing,
_ => ClientCapabilityEvidenceState.Unknown
};
ClientCapabilityEvidenceGate gate = new(evidenceState, reason);
return new ClientCapabilityEvidence(gate, gate, gate, gate, gate, gate);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
namespace CheatEngine.Client.Runtime;

/// <summary>
/// Immutable, independently sourced prerequisites for one Client capability. A satisfied policy never establishes
/// package, host, qualification, or activation-lifetime evidence.
/// </summary>
public readonly record struct ClientCapabilityEvidence
{
/// <summary>Creates validated evidence for one Client capability.</summary>
public ClientCapabilityEvidence(
ClientCapabilityEvidenceGate implementation,
ClientCapabilityEvidenceGate package,
ClientCapabilityEvidenceGate host,
ClientCapabilityEvidenceGate liveQualification,
ClientCapabilityEvidenceGate policy,
ClientCapabilityEvidenceGate lifetime)
{
Validate(implementation, nameof(implementation));
Validate(package, nameof(package));
Validate(host, nameof(host));
Validate(liveQualification, nameof(liveQualification));
Validate(policy, nameof(policy));
Validate(lifetime, nameof(lifetime));

Implementation = implementation;
Package = package;
Host = host;
LiveQualification = liveQualification;
Policy = policy;
Lifetime = lifetime;
}

/// <summary>Gets whether an operational Client adapter is delivered for this capability.</summary>
public ClientCapabilityEvidenceGate Implementation
{
get;
}

/// <summary>Gets whether the required primitive is established in the consumed package artifact.</summary>
public ClientCapabilityEvidenceGate Package
{
get;
}

/// <summary>Gets the independent observation of the required Cheat Engine host primitive.</summary>
public ClientCapabilityEvidenceGate Host
{
get;
}

/// <summary>Gets whether the required host profile has passed its explicit live qualification gate.</summary>
public ClientCapabilityEvidenceGate LiveQualification
{
get;
}

/// <summary>Gets whether Client policy permits this capability for the current activation.</summary>
public ClientCapabilityEvidenceGate Policy
{
get;
}

/// <summary>Gets whether the activation lifetime required by this capability is current.</summary>
public ClientCapabilityEvidenceGate Lifetime
{
get;
}

/// <summary>Gets the legacy availability projection without collapsing the underlying evidence dimensions.</summary>
public ClientCapabilityAvailabilityState AvailabilityState
{
get
{
if (HasState(ClientCapabilityEvidenceState.Missing))
{
return ClientCapabilityAvailabilityState.Unavailable;
}

return AllSatisfied
? ClientCapabilityAvailabilityState.Available
: ClientCapabilityAvailabilityState.Unknown;
}
}

/// <summary>Gets whether every prerequisite has been independently established.</summary>
public bool IsExecutable => AllSatisfied;

/// <summary>Gets the reason for the highest-priority missing, faulted, malformed, or unknown prerequisite.</summary>
public string EffectiveReason => GetEffectiveGate().Reason;

private bool AllSatisfied =>
Implementation.IsSatisfied && Package.IsSatisfied && Host.IsSatisfied && LiveQualification.IsSatisfied &&
Policy.IsSatisfied && Lifetime.IsSatisfied;

private ClientCapabilityEvidenceGate GetEffectiveGate()
{
if (Lifetime.State == ClientCapabilityEvidenceState.Missing)
{
return Lifetime;
}

if (Policy.State == ClientCapabilityEvidenceState.Missing)
{
return Policy;
}

if (Implementation.State == ClientCapabilityEvidenceState.Missing)
{
return Implementation;
}

if (Package.State == ClientCapabilityEvidenceState.Missing)
{
return Package;
}

if (Host.State == ClientCapabilityEvidenceState.Missing)
{
return Host;
}

if (LiveQualification.State == ClientCapabilityEvidenceState.Missing)
{
return LiveQualification;
}

if (Host.State is ClientCapabilityEvidenceState.Faulted or ClientCapabilityEvidenceState.Malformed)
{
return Host;
}

if (Package.State is ClientCapabilityEvidenceState.Faulted or ClientCapabilityEvidenceState.Malformed)
{
return Package;
}

if (LiveQualification.State is ClientCapabilityEvidenceState.Faulted or ClientCapabilityEvidenceState.Malformed)
{
return LiveQualification;
}

if (Implementation.State is ClientCapabilityEvidenceState.Faulted or ClientCapabilityEvidenceState.Malformed)
{
return Implementation;
}

if (Policy.State is ClientCapabilityEvidenceState.Faulted or ClientCapabilityEvidenceState.Malformed)
{
return Policy;
}

if (Lifetime.State is ClientCapabilityEvidenceState.Faulted or ClientCapabilityEvidenceState.Malformed)
{
return Lifetime;
}

if (Host.State == ClientCapabilityEvidenceState.Unknown)
{
return Host;
}

if (Package.State == ClientCapabilityEvidenceState.Unknown)
{
return Package;
}

if (LiveQualification.State == ClientCapabilityEvidenceState.Unknown)
{
return LiveQualification;
}

if (Implementation.State == ClientCapabilityEvidenceState.Unknown)
{
return Implementation;
}

if (Policy.State == ClientCapabilityEvidenceState.Unknown)
{
return Policy;
}

return Lifetime;
}

private bool HasState(ClientCapabilityEvidenceState state)
{
return Implementation.State == state || Package.State == state || Host.State == state ||
LiveQualification.State == state || Policy.State == state || Lifetime.State == state;
}

private static void Validate(ClientCapabilityEvidenceGate gate, string parameterName)
{
if (!Enum.IsDefined(gate.State))
{
throw new ArgumentOutOfRangeException(parameterName, gate.State,
"The Client capability evidence state is not defined.");
}

ArgumentException.ThrowIfNullOrWhiteSpace(gate.Reason);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
namespace CheatEngine.Client.Runtime;

/// <summary>One immutable capability prerequisite together with its explicit observation reason.</summary>
public readonly record struct ClientCapabilityEvidenceGate
{
/// <summary>Creates a validated capability prerequisite observation.</summary>
public ClientCapabilityEvidenceGate(ClientCapabilityEvidenceState state, string reason)
{
if (!Enum.IsDefined(state))
{
throw new ArgumentOutOfRangeException(nameof(state), state,
"The Client capability evidence state is not defined.");
}

ArgumentException.ThrowIfNullOrWhiteSpace(reason);
State = state;
Reason = reason;
}

/// <summary>Gets the independently observed prerequisite state.</summary>
public ClientCapabilityEvidenceState State
{
get;
}

/// <summary>Gets the bounded reason for this prerequisite observation.</summary>
public string Reason
{
get;
}

/// <summary>Gets whether the prerequisite has been established.</summary>
public bool IsSatisfied => State == ClientCapabilityEvidenceState.Satisfied;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
namespace CheatEngine.Client.Runtime;

/// <summary>Describes one independently established prerequisite in a Client capability evidence record.</summary>
public enum ClientCapabilityEvidenceState : byte
{
/// <summary>The Client has not established this prerequisite for the current observation.</summary>
Unknown = 0,

/// <summary>The prerequisite was established for the current observation.</summary>
Satisfied = 1,

/// <summary>The prerequisite is known to be absent or denied.</summary>
Missing = 2,

/// <summary>The prerequisite probe reached the host but failed before it could establish a result.</summary>
Faulted = 3,

/// <summary>The host returned a result that cannot satisfy the documented prerequisite shape.</summary>
Malformed = 4
}
Loading