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
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,25 @@ ship.

### Added

- **Optional Lua operations.** Generated operations accept trailing SDK `LuaOptional<T>` inputs of SDK-supported
scalar types and one optional trailing `out` result on a `bool` binding. Their public surface uses the Client-owned
`LuaOptional<T>` and its
`Omitted`, `Nil` and `Of` factories; omitted arguments, absent results, explicit `nil` and present values stay
distinct. Result mappers run only for present optional values. Existing scalar operation signatures stay unchanged.

### Changed

- **Roadmap status.** Historical work items now distinguish shipped implementations, deliberately deferred domains
and outstanding live qualification or hosted performance evidence.

### Security

### Deployment

- **1.1 development line.** Record the published 1.0.0 public API and analyzer rules as shipped, set the MinVer floor
and template defaults to 1.1, and regenerate the project-reference lock files. Package validation compares against
published 1.0.0 packages with strict baseline equality disabled, permitting additive APIs while checking breaks.

## [1.0.0] - 2026-09-25

The first release of CheatEngine.Client: high-level, activation-scoped C# APIs for Cheat Engine 7.7 x64 plugins, built
Expand Down
7 changes: 4 additions & 3 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,16 @@
<PropertyGroup Label="Packaging and versioning">
<!-- MinVer derives one version for every package from the nearest v* tag, so CI must fetch full history and tags. -->
<MinVerTagPrefix>v</MinVerTagPrefix>
<!-- Floor for commits no v* tag covers yet, which build as 1.0.0-alpha.0.N. Raise it after each release to open the next line, and never above the line of the tag being released. -->
<MinVerMinimumMajorMinor>1.0</MinVerMinimumMajorMinor>
<!-- Floor for commits no v* tag covers yet, which build as 1.1.0-alpha.0.N. Raise it after each release to open the next line, and never above the line of the tag being released. -->
<MinVerMinimumMajorMinor>1.1</MinVerMinimumMajorMinor>
<!--
Evaluation-time placeholder equal to the MinVer floor: NuGet records it for project references in lock files
([1.0.0, )), including the Coexistence fixtures that are outside Central Package Management; MinVer computes the
([1.1.0, )), including the Coexistence fixtures that are outside Central Package Management; MinVer computes the
real version at build and pack. CHEATENGINECLIENT9019 refuses any other version source.
-->
<VersionPrefix>$(MinVerMinimumMajorMinor).0</VersionPrefix>
<_CheatEngineClientRootVersionPrefix>$(VersionPrefix)</_CheatEngineClientRootVersionPrefix>
<CheatEngineClientPackageValidationBaselineVersion>1.0.0</CheatEngineClientPackageValidationBaselineVersion>
<!--
The Roslyn version the shipped Lua generator (packed inside CheatEngine.Client.Hosting) is compiled against. A
generator built against Roslyn N does not load in an older compiler (CS9057), so this floor, the
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

[![CI](https://img.shields.io/github/actions/workflow/status/CheatEngineNet/CheatEngine.Client/main-ci.yml?branch=main&style=flat-square&logo=githubactions&logoColor=white&labelColor=24292f)](https://github.com/CheatEngineNet/CheatEngine.Client/actions/workflows/main-ci.yml)
[![OpenSSF Scorecard](https://img.shields.io/ossf-scorecard/github.com/CheatEngineNet/CheatEngine.Client?style=flat-square&label=OpenSSF%20Scorecard&labelColor=24292f)](https://scorecard.dev/viewer/?uri=github.com/CheatEngineNet/CheatEngine.Client)
[![NuGet](https://img.shields.io/nuget/vpre/CheatEngine.Client?style=flat-square&logo=nuget&logoColor=white&labelColor=24292f&color=004880)](https://www.nuget.org/packages/CheatEngine.Client)
[![NuGet](https://img.shields.io/nuget/v/CheatEngine.Client?style=flat-square&logo=nuget&logoColor=white&labelColor=24292f&color=004880)](https://www.nuget.org/packages/CheatEngine.Client)
[![.NET 10](https://img.shields.io/badge/.NET-10.0-512BD4?style=flat-square&logo=dotnet&logoColor=white&labelColor=24292f)](https://dotnet.microsoft.com/download/dotnet/10.0)
[![Windows x64](https://img.shields.io/badge/platform-Windows%20x64-0078D4?style=flat-square&labelColor=24292f)](#requirements)

Expand Down
20 changes: 9 additions & 11 deletions RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,9 @@ otherwise.
- move the rows of `AnalyzerReleases.Unshipped.md` into a new `## Release X.Y.Z` section of
`AnalyzerReleases.Shipped.md`.

Experimental entries keep their `[CECLIENT500x]` prefix in `PublicAPI.Shipped.txt`. For 1.0.0, every Shipped file
starts empty and receives the whole 1.0.0 surface. Make both moves in one commit, the last API commit of the release
pull request: `PublicApiFileTests` reads the `## Release X.Y.Z` section as the promotion, keeps every Shipped file
empty and forbids `*REMOVED*` entries until the first one exists, and requires each one to name a dated
Experimental entries keep their `[CECLIENT500x]` prefix in `PublicAPI.Shipped.txt`. The 1.0.0 surface is already
recorded as shipped. Make both moves in one commit, the last API commit of the release pull request:
`PublicApiFileTests` reads the `## Release X.Y.Z` sections as promotions and requires each one to name a dated
`CHANGELOG.md` release.

Then confirm the move compiles clean (RS0016/RS0017/RS0025, RS2000–RS2008):
Expand Down Expand Up @@ -331,13 +330,12 @@ GitHub release. Then open the next line in one pull request of its own, never in

1. Set `CheatEngineClientPackageValidationBaselineVersion` in [Directory.Build.props](Directory.Build.props) to the
released version (`1.0.0` after the first release), and update the comment of the baseline hook in
`eng/Shipping.props`, which says that nothing is published yet. Package validation then compares every package with
the published package of that version and fails the pack on a breaking change. Two consequences are decided in that
pull request:
- `EnableStrictModeForBaselineValidation` is on (`eng/Shipping.props`), which makes the comparison an equality
check: an API that a 1.1 change adds fails the pack too. Either keep strict mode and record each reviewed addition
in the project's `CompatibilitySuppressions.xml`, or turn strict mode off for the baseline, so that only breaking
changes fail.
`eng/Shipping.props` to identify the published baseline. Package validation then compares every package with
the published package of that version and fails the pack on a breaking change. The 1.1 development line uses
`1.0.0` as its baseline and disables `EnableStrictModeForBaselineValidation`, allowing additive APIs while retaining
breaking-change checks. Two consequences must be reviewed when changing that policy:
- Turning `EnableStrictModeForBaselineValidation` on makes the comparison an equality check: an API that a minor
release adds fails the pack too. Every reviewed addition then needs a project's `CompatibilitySuppressions.xml`.
- The 1.x policy of the [README](README.md#versioning-and-compatibility) allows changes that package validation
reports even without strict mode: a member added to a call-only interface (`CP0006`), and the removal or change
of an experimental API (`CP0001`, `CP0002`). Each one carries a reviewed suppression, generated with
Expand Down
21 changes: 20 additions & 1 deletion ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ Each experimental id leaves experimental once every scenario its capability requ

### Lua operations

Generated Lua operations take scalar inputs and return one result in 1.0 (`CECLUA1103`). CheatEngine.SDK 2.0.0's `LuaOptional<T>` lets a binding omit a trailing argument (`LUA_TNONE`, not an explicit `nil`) and read the number of results Lua actually returned; the Client adopts it once it has a contract for an omitted input and an absent result.
Generated Lua operations take scalar inputs and return one result in 1.0 (`CECLUA1103`). The 1.1 implementation adds the Client-owned `LuaOptional<T>` contract over CheatEngine.SDK 2.0.0's optional values: omitted arguments and absent results stay distinct from explicit `nil` and present values. Optional inputs form a trailing group; optional results use the SDK's `bool` binding with one trailing `out LuaOptional<T>` parameter. Ordinary scalar returns remain supported. SDK Lua values remain inside the generated adapter.

### Benchmarks

Expand All @@ -45,6 +45,25 @@ The facade-versus-direct-SDK comparison (audit item A24-26) is deferred: `tests/

These milestones and epics are the pre-1.0 plan. Their ids stay stable for the issues that reference them; the [CHANGELOG](CHANGELOG.md) says what 1.0.0 shipped.

### Current execution status

Source status as of October 7, 2026. "Shipped" below describes the 1.0.0 implementation; it does not assert live-host qualification. The live runner exists, but no committed run populates `HostQualificationEvidence.Recorded` yet.

| Work items | Status | Evidence or remaining work |
|---|---|---|
| CLI-001–003 | Shipped | Governance files, the runtime capability evidence model and the [1.0.0 changelog](CHANGELOG.md#100---2026-09-25). |
| CLI-004–006 | Shipped | Dispatcher failure mapping, invocation-scoped codec contexts and activation rollback; focused tests in `CheatEngine.Client.Core.Tests` and `CheatEngine.Client.Hosting.Tests`. |
| CLI-007–009 | Shipped, with documented SDK limitations | Typed SDK operations, failure provenance and target identity are adopted. The three SDK requests above remain outstanding. |
| CLI-010–012 | Shipped; live composition evidence pending | SDK Lua registration leases, recursive public boundaries and activation-local DI exist. Multi-plugin host behavior still needs the live run. |
| CLI-013–015 | Shipped | Bounded AOB results, memory batch effect outcomes and typed table/symbol workflows have focused managed tests. |
| CLI-016–017 | Implemented, experimental | Value-scan sessions, allocation leases and Auto Assembler patch leases exist; their scenarios above must pass before experimental ids are lifted. |
| CLI-018–019 | Deferred; removed from 1.0 scope | Debugger/subscription adapters and public event streams need owned SDK primitives. The [1.0 removals](CHANGELOG.md#removed) deliberately remove the earlier placeholder contracts; CLI-019's historical prerequisite row is not a claim that an event source is ready. |
| CLI-020–021 | Shipped | Fluent terminal contracts and stale-client/local-process semantics have implementation and focused tests. |
| CLI-022–023 | Shipped; host deployment evidence pending | Package-consumer, template and deployment-closure checks exist. Actual Cheat Engine loading and coexistence remain a separate gate. |
| CLI-024 | Partly implemented; host evidence pending | The qualification runner and fake-port benchmarks exist. Committed live receipts, hosted Client-versus-SDK measurements and published performance budgets remain outstanding. |

The current development line is 1.1. Optional Lua operations are implemented for this line, and package validation uses the published 1.0.0 baseline. Neither change is a published 1.1 release. Qualification and hosted benchmarks remain separate tasks; no receipt or performance result is inferred from a build, package check or fake-port benchmark.

| Phase | Outcome | Exit evidence |
|---|---|---|
| CLI-M0 | Governance and capability truth | Public status separates implementation, artifact, host qualification and policy. |
Expand Down
7 changes: 3 additions & 4 deletions eng/Shipping.props
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,10 @@
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<EnablePackageValidation>true</EnablePackageValidation>
<EnableStrictModeForBaselineValidation>true</EnableStrictModeForBaselineValidation>
<EnableStrictModeForBaselineValidation>false</EnableStrictModeForBaselineValidation>
<!--
Baseline hook: nothing is published yet, so there is no version to compare with. After the first release, set
CheatEngineClientPackageValidationBaselineVersion in Directory.Build.props to that version; package validation then
also reports breaking changes against the published packages (RELEASING.md).
Baseline hook: compare against the published 1.0.0 packages. Strict mode is disabled so additive 1.x APIs remain
valid while package validation continues to report breaking changes (RELEASING.md).
https://learn.microsoft.com/dotnet/core/project-sdk/msbuild-props#packagevalidationbaselineversion
-->
<PackageValidationBaselineVersion Condition="'$(CheatEngineClientPackageValidationBaselineVersion)' != ''">$(CheatEngineClientPackageValidationBaselineVersion)</PackageValidationBaselineVersion>
Expand Down
122 changes: 122 additions & 0 deletions libs/CheatEngine.Client.Abstractions/Lua/LuaOptional.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
using System.Diagnostics.CodeAnalysis;

namespace CheatEngine.Client.Lua;

/// <summary>Creates Client-owned optional Lua values without exposing the SDK marshalling type.</summary>
public static class LuaOptional
{
/// <summary>Creates an omitted value, which passes no Lua argument or represents no Lua result.</summary>
/// <typeparam name="T">The non-null copied value type.</typeparam>
/// <returns>An omitted optional value.</returns>
public static LuaOptional<T> Omitted<T>()
where T : notnull
{
return default;
}

/// <summary>Creates an explicit Lua <c>nil</c> value.</summary>
/// <typeparam name="T">The non-null copied value type.</typeparam>
/// <returns>An optional value representing explicit Lua <c>nil</c>.</returns>
public static LuaOptional<T> Nil<T>()
where T : notnull
{
return new LuaOptional<T>(LuaOptionalState.Nil, default!);
}

/// <summary>Creates a present Lua value.</summary>
/// <typeparam name="T">The non-null copied value type.</typeparam>
/// <param name="value">The present non-null value.</param>
/// <returns>An optional value containing <paramref name="value" />.</returns>
/// <exception cref="ArgumentNullException"><paramref name="value" /> is <see langword="null" />.</exception>
public static LuaOptional<T> Of<T>(T value)
where T : notnull
{
ArgumentNullException.ThrowIfNull(value);
return new LuaOptional<T>(LuaOptionalState.Value, value);
}
}

/// <summary>One optional Lua value: omitted, explicit <c>nil</c>, or a present non-null value.</summary>
/// <typeparam name="T">The copied Client value type.</typeparam>
public readonly struct LuaOptional<T> : IEquatable<LuaOptional<T>>
where T : notnull
{
private readonly LuaOptionalState _state;
private readonly T _value;

internal LuaOptional(LuaOptionalState state, T value)
{
_state = state;
_value = value;
}

/// <summary>Gets whether no value was passed or returned.</summary>
public bool IsOmitted => _state == LuaOptionalState.Omitted;

/// <summary>Gets whether Lua explicitly passed or returned <c>nil</c>.</summary>
public bool IsNil => _state == LuaOptionalState.Nil;

/// <summary>Gets whether a non-null value was passed or returned.</summary>
public bool HasValue => _state == LuaOptionalState.Value;

/// <summary>Gets the present value, or the safe default when the value is omitted or <c>nil</c>.</summary>
/// <remarks>Use <see cref="HasValue" /> or <see cref="TryGetValue" /> to distinguish an absent value from a present default.</remarks>
public T? Value => HasValue ? _value : default;

/// <summary>Gets the value when it is present.</summary>
/// <param name="value">The present value, or the safe default when omitted or <c>nil</c>.</param>
/// <returns><see langword="true" /> when a present value was copied to <paramref name="value" />.</returns>
public bool TryGetValue([NotNullWhen(true)] out T? value)
{
value = HasValue ? _value : default!;
return HasValue;
}

/// <summary>Compares this optional value's state and present value with another optional value.</summary>
/// <param name="other">The optional value to compare.</param>
/// <returns><see langword="true" /> when the states and any present values are equal.</returns>
public bool Equals(LuaOptional<T> other)
{
return _state == other._state && (!HasValue || EqualityComparer<T>.Default.Equals(_value, other._value));
}

/// <summary>Compares this optional value with an object of the same optional type.</summary>
/// <param name="obj">The object to compare.</param>
/// <returns><see langword="true" /> when the object has the same state and any present value.</returns>
public override bool Equals(object? obj)
{
return obj is LuaOptional<T> other && Equals(other);
}

/// <summary>Computes a hash of this optional value's state and any present value.</summary>
/// <returns>The hash code consistent with optional-value equality.</returns>
public override int GetHashCode()
{
return HasValue ? HashCode.Combine((int) _state, _value) : (int) _state;
}

/// <summary>Compares the state and, for present values, the value.</summary>
/// <param name="left">The first optional value.</param>
/// <param name="right">The second optional value.</param>
/// <returns><see langword="true" /> when both optional values are equal.</returns>
public static bool operator ==(LuaOptional<T> left, LuaOptional<T> right)
{
return left.Equals(right);
}

/// <summary>Compares the state and, for present values, the value.</summary>
/// <param name="left">The first optional value.</param>
/// <param name="right">The second optional value.</param>
/// <returns><see langword="true" /> when the optional values differ.</returns>
public static bool operator !=(LuaOptional<T> left, LuaOptional<T> right)
{
return !left.Equals(right);
}
}

internal enum LuaOptionalState : byte
{
Omitted,
Nil,
Value
}
Loading
Loading