using DorkNet.Server.Compat; using Microsoft.AspNetCore.Mvc; namespace DorkNet.Server.Controllers.Auth; /// The boot-time client-version gate the 2020.12 watch hits /// at startup as GET api/versioncheck/v4?v={build}&p={platform}. /// /// Verified against the watch's KMDHPCHFADM.VerifyGameVersion /// (decompiled): the response is parsed into JFKEIPDCCHH which /// exposes a single VersionStatus property of enum type /// PCBCGDHBAEL: /// /// 0 = ValidForPlay — proceed normally /// 1 = ValidForMenu — let user reach menus but block matchmaking /// 2 = UpdateRequired — watch shows its built-in /// "Update Required" dialog and refuses to advance past the boot screen /// /// This is the CLEAN way to reject a wrong-build client at the /// front door — the watch has dedicated UX for it, no custom client /// mod work needed. /// /// The v query param is the raw build identifier the watch /// reads from KMJJGELKNAP.MCFMPCNNEBH — for the December build /// that's the literal string "20201210". We map raw build IDs /// to our internal s via the /// DorkNet:BuildIdToVersionKey config map; any build ID we /// don't recognise OR that maps to a key not in /// gets /// VersionStatus = 2 and the watch refuses to proceed. /// /// This endpoint is EXEMPT from /// VersionDetectionMiddleware's gate (otherwise the gate would /// 426 the rejection request itself before this endpoint could reply /// with the polite UpdateRequired payload). The exemption is set in /// VersionDetectionMiddleware.IsExempt. /// [ApiController] public class VersionCheckController( IConfiguration config, VersionRegistry registry, ILogger logger) : ControllerBase { // Mirrors the PCBCGDHBAEL enum the watch decodes the response into. private const int VersionStatus_ValidForPlay = 0; private const int VersionStatus_ValidForMenu = 1; private const int VersionStatus_UpdateRequired = 2; [HttpGet("/api/versioncheck/v4")] [HttpGet("/api/versioncheck/v3")] [HttpGet("/api/versioncheck/v2")] [HttpGet("/api/versioncheck/v1")] [HttpGet("/api/versioncheck")] [HttpGet("/api/version/v1")] [HttpGet("/api/version")] public IActionResult Check( [FromQuery(Name = "v")] string? buildId, [FromQuery(Name = "p")] string? platform) { // Map raw build ID → ClientVersion key via config. Format: // "DorkNet": { // "BuildIdToVersionKey": { // "20201210": "december_2020_12_18", // "20200310": "march_2020_03_10" // } // } // Mapping ABSENT → unknown build → UpdateRequired. var mapSection = config.GetSection("DorkNet:BuildIdToVersionKey"); string? versionKey = !string.IsNullOrWhiteSpace(buildId) ? mapSection[buildId] : null; bool allowed = versionKey is not null && registry.IsSupported(versionKey); var status = allowed ? VersionStatus_ValidForPlay : VersionStatus_UpdateRequired; logger.LogInformation( "[versioncheck] buildId={Build} platform={Platform} → versionKey={Key} status={Status} ({Label})", buildId, platform, versionKey ?? "(unmapped)", status, status == VersionStatus_ValidForPlay ? "ValidForPlay" : "UpdateRequired"); // Wire shape verified against KMDHPCHFADM_NestedType_JFKEIPDCCHH.txt: // single key "VersionStatus" (PascalCase, case-sensitive — LitJson). return Ok(new { VersionStatus = status }); } }