refactor(battle-node): remove ScriptedBotParticipant and dev-affordance wiring

Deletes the scripted opponent and every entry point that created a
BattleType.Scripted session (the ?scripted=1 query opt-in, the
SoloDefaultsToScripted toggle, the resolver short-circuit, the WS handler case,
the bridge validation arm). Real two-client PvP and the Bot matchmaking-timeout
fallback are untouched. ResolveAsync drops its scriptedOptIn parameter.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
gamer147
2026-06-03 20:15:48 -04:00
parent 8085119439
commit f21ab7a38c
21 changed files with 49 additions and 395 deletions

View File

@@ -27,20 +27,13 @@ public class ArenaTwoPickBattleController : SVSimController
[HttpPost("do_matching")]
public async Task<IActionResult> DoMatching(
[FromBody] DoMatchingRequest req,
[FromQuery(Name = "scripted")] string? scripted = null,
CancellationToken ct = default)
{
if (!TryGetViewerId(out var vid)) return Unauthorized();
// Accept "1" or "true" (case-insensitive) as per-request opt-in for the Scripted
// path. ASP.NET's default bool binder rejects "1", so parse permissively here.
// BattleNodeOptions.SoloDefaultsToScripted is the process-wide equivalent and is
// applied inside the resolver.
var scriptedOptIn = scripted is not null
&& (scripted == "1" || string.Equals(scripted, "true", StringComparison.OrdinalIgnoreCase));
try
{
var ctx = await _matchContextBuilder.BuildForTwoPickAsync(vid);
var r = await _resolver.ResolveAsync("arena_two_pick_battle", new BattlePlayer(vid, ctx), scriptedOptIn, ct);
var r = await _resolver.ResolveAsync("arena_two_pick_battle", new BattlePlayer(vid, ctx), ct);
return Ok(new DoMatchingResponseDto
{
MatchingState = r.MatchingState,

View File

@@ -132,10 +132,7 @@ public sealed class RankBattleController : ControllerBase
return Ok(new DoMatchingResponseDto { MatchingState = 3001, NodeServerUrl = "" });
}
// Rank battle has no ?scripted=1 query opt-in (no live capture has shown such a
// param on the rank URLs). The process-wide BattleNodeOptions.SoloDefaultsToScripted
// toggle is the only scripted entry point and is honored inside the resolver.
var r = await _resolver.ResolveAsync(mode, new BattlePlayer(vid, ctx), scriptedOptIn: false, ct);
var r = await _resolver.ResolveAsync(mode, new BattlePlayer(vid, ctx), ct);
return Ok(new DoMatchingResponseDto
{

View File

@@ -10,10 +10,7 @@ namespace SVSim.EmulatedEntrypoint.Matching;
/// regardless of which URL family carried the request:
/// </para>
/// <list type="number">
/// <item>Honor the dev-affordance scripted opt-in (route flag and/or
/// <see cref="BattleNodeOptions.SoloDefaultsToScripted"/>) — bypass pair-up,
/// register a Scripted match, return immediately.</item>
/// <item>Otherwise consult <see cref="IMatchingPairUpService"/> and translate the
/// <item>Consult <see cref="IMatchingPairUpService"/> and translate the
/// resulting <see cref="PairUpResult"/> into a wire matching_state per the
/// 3002 / 3004 / 3007 / 3011 vocabulary.</item>
/// </list>
@@ -33,15 +30,9 @@ public interface IMatchingResolver
/// <c>"rotation_rank_battle"</c>, <c>"unlimited_rank_battle"</c>).
/// </param>
/// <param name="player">Caller's <see cref="BattlePlayer"/> (viewer-id + built MatchContext).</param>
/// <param name="scriptedOptIn">
/// Per-request opt-in from a controller-specific signal (e.g. TK2's <c>?scripted=1</c>
/// query param). OR'd with <see cref="BattleNodeOptions.SoloDefaultsToScripted"/>;
/// either being true short-circuits to a Scripted match.
/// </param>
Task<MatchingResolution> ResolveAsync(
string mode,
BattlePlayer player,
bool scriptedOptIn,
CancellationToken ct);
}

View File

@@ -1,5 +1,4 @@
using SVSim.BattleNode.Bridge;
using SVSim.BattleNode.Sessions;
namespace SVSim.EmulatedEntrypoint.Matching;
@@ -8,7 +7,6 @@ public sealed class MatchingResolver : IMatchingResolver
{
private readonly IMatchingBridge _bridge;
private readonly IMatchingPairUpService _pairUp;
private readonly BattleNodeOptions _options;
public MatchingResolver(
IMatchingBridge bridge,
@@ -17,25 +15,13 @@ public sealed class MatchingResolver : IMatchingResolver
{
_bridge = bridge;
_pairUp = pairUp;
_options = options;
}
public Task<MatchingResolution> ResolveAsync(
string mode,
BattlePlayer player,
bool scriptedOptIn,
CancellationToken ct)
{
// Dev-affordance short-circuit. Either a per-request flag (e.g. ?scripted=1) or the
// process-wide BattleNodeOptions.SoloDefaultsToScripted toggle puts us here.
// Registers a Scripted match (server-side scripted opponent in BattleSession) and
// returns matching_state=3004 SUCCEEDED so the client opens the WS and proceeds.
if (scriptedOptIn || _options.SoloDefaultsToScripted)
{
var m = _bridge.RegisterBattle(player, p2: null, BattleType.Scripted);
return Task.FromResult(new MatchingResolution(3004, m.BattleId, m.NodeServerUrl));
}
return ResolveViaPairUpAsync(mode, player, ct);
}

View File

@@ -125,7 +125,7 @@ public class Program
// BestHTTP's SocketManager parses this as the Socket.IO v2 endpoint URL.
opt.NodeServerUrl = "localhost:5148/socket.io/";
// Any field in BattleNodeOptions can be overridden via the "BattleNode" section
// in appsettings*.json — see appsettings.Development.json for SoloDefaultsToScripted.
// in appsettings*.json — see appsettings.Development.json for DiagnosticLogging.
builder.Configuration.GetSection("BattleNode").Bind(opt);
});
// In-process FCFS pair-up for TK2 PvP /do_matching, plus rank-battle's AI-fallback
@@ -138,9 +138,8 @@ public class Program
new ModePolicy("unlimited_rank_battle", PolicyKind.PvpFirstThenAiFallback),
}));
builder.Services.AddSingleton<IMatchingPairUpService, InProcessPairUp>();
// Single resolver shared by every /do_matching family controller. Owns the scripted-
// flag short-circuit + the pair-up → matching_state mapping. Singleton: stateless,
// all deps are singletons too.
// Single resolver shared by every /do_matching family controller. Owns the
// pair-up → matching_state mapping. Singleton: stateless, all deps are singletons too.
builder.Services.AddSingleton<IMatchingResolver, MatchingResolver>();
// Phase 3: bot roster used by RankBattleController.AiStart to compose oppo_info.
// Transient because BotRoster depends on the transient IGlobalsRepository.

View File

@@ -9,7 +9,6 @@
"BypassSteamTicket": true
},
"BattleNode": {
"SoloDefaultsToScripted": false,
"DiagnosticLogging": true
}
}