The first half of surge day 1. One question, "may this session emit Pro output?", with one method on the interface so nothing downstream knows which rail proved it. SteamEntitlement reads the lifetime SKU the way Valve intends: SteamAPI.Init() then SteamApps.BIsDlcInstalled(dlcAppId). Four different failure paths all resolve to the free tier rather than to Pro or to a crash — no DLC id configured, no Steam client, steam_api64.dll missing (a throw on first touch), and simply not owned. A copied folder run without Steam is the common case, not an exceptional one. NotConfigured() is the shipping default until Steamworks creates the DLC app; it answers false without ever touching the native library, so today's builds behave exactly as they did before this commit. HasProOutput is now IsPremium || DeveloperMode || rail, with an internal EntitlementOverride test seam (internal, because a public setter here is a licence bypass). Four tests: no id reads false, no client reads false without throwing, the fake reports what a test sets, and the view model takes its answer from the rail. Steamworks.NET 2024.8.0 added; the native DLL still comes from scripts/steam-sdk.sh, so the csproj change is three lines and no formatting churn.
69 lines
2.7 KiB
C#
69 lines
2.7 KiB
C#
using ytLive.Helpers;
|
|
using ytLive.Services.Entitlement;
|
|
|
|
namespace ytLive.ViewModels;
|
|
|
|
/// <summary>
|
|
/// The entitlement surface: whether this session may emit output above the free budget.
|
|
///
|
|
/// <para>Deliberately a separate partial from <c>License.cs</c>: that file owns *how* a
|
|
/// licence is validated (Polar today), this one owns *what the answer means* for output.
|
|
/// Steam's DLC check and Polar's lease both collapse into <see cref="IsPremium"/>; nothing
|
|
/// downstream of <see cref="HasProOutput"/> cares which one proved it.</para>
|
|
/// </summary>
|
|
public partial class MainViewModel
|
|
{
|
|
/// <summary>
|
|
/// The single input to <see cref="Models.OutputProfile.Resolve"/>.
|
|
///
|
|
/// <para><b>Fail closed:</b> an unresolved or unreadable licence is simply false, so a
|
|
/// build with no entitlement emits the free profile. There is no "unknown" branch to get
|
|
/// wrong.</para>
|
|
///
|
|
/// <para><b>Developer mode never ships:</b> <see cref="DeveloperMode"/> is only ever
|
|
/// settable inside a development build (see <see cref="DevBuild"/>), so this expression
|
|
/// reduces to <c>IsPremium</c> in a Release binary.</para>
|
|
/// </summary>
|
|
public bool HasProOutput => IsPremium || DeveloperMode || _entitlement.IsPro;
|
|
|
|
/// <summary>
|
|
/// The entitlement rail for this session. Lifetime = Steam DLC; until the DLC app id exists it
|
|
/// is <see cref="SteamEntitlement.NotConfigured"/>, which reads false (fail closed). Tests
|
|
/// inject a fake through <see cref="EntitlementOverride"/>.
|
|
/// </summary>
|
|
private IEntitlement _entitlement = SteamEntitlement.NotConfigured();
|
|
|
|
/// <summary>Test seam: swap the rail without a Steam client. Internal — the test assembly is
|
|
/// the only caller, and a public setter here would be a licence bypass surface.</summary>
|
|
internal IEntitlement EntitlementOverride
|
|
{
|
|
get => _entitlement;
|
|
set
|
|
{
|
|
_entitlement = value;
|
|
OnPropertyChanged(nameof(HasProOutput));
|
|
}
|
|
}
|
|
|
|
private bool _developerMode;
|
|
|
|
/// <summary>
|
|
/// Development-only override: ignore the output budget and allow every shipped tier.
|
|
///
|
|
/// <para>There is no UI to reach this in a Release build — the panel that hosts the
|
|
/// checkbox is excluded from the Release compile — and the setter refuses the value
|
|
/// unless <see cref="DevBuild.IsDevelopment"/> is true, so even a hand-edited settings
|
|
/// file cannot unlock it.</para>
|
|
/// </summary>
|
|
public bool DeveloperMode
|
|
{
|
|
get => _developerMode;
|
|
set
|
|
{
|
|
if (!DevBuild.IsDevelopment) return;
|
|
if (!SetProperty(ref _developerMode, value)) return;
|
|
OnPropertyChanged(nameof(HasProOutput));
|
|
}
|
|
}
|
|
}
|