feat(arena-tk2): SoloDefaultsToScripted config flag for dev convenience

Adds BattleNodeOptions.SoloDefaultsToScripted (default false). When true,
the TK2 do_matching controller treats every solo poll as if ?scripted=1
were passed and returns a Scripted 3004 match immediately — useful for
the live client (which can't append query params) to drive the scripted
bot without needing a second player.

Toggle via "BattleNode:SoloDefaultsToScripted" in appsettings*.json
(Program.cs now binds the BattleNode section over the AddBattleNode
defaults). Turn off to test real PvP with two clients.

Trade-off documented on the option: while on, two simultaneous pollers
each get their own Scripted match instead of pairing, so PvP is
effectively disabled until the flag is flipped.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
gamer147
2026-06-01 23:48:14 -04:00
parent 8112b3f81f
commit 0095bdf0cf
5 changed files with 58 additions and 3 deletions

View File

@@ -2,6 +2,7 @@ using System.Net;
using System.Net.Http.Json;
using System.Text.Json;
using Microsoft.Extensions.DependencyInjection;
using SVSim.BattleNode.Bridge;
using SVSim.Database;
using SVSim.Database.Models;
using SVSim.UnitTests.Infrastructure;
@@ -136,6 +137,36 @@ public class ArenaTwoPickBattleControllerTests
"Owner and joiner must see the same node_server_url.");
}
[Test]
public async Task DoMatching_SoloDefaultsToScripted_flag_makes_solo_poll_return_3004_without_query_param()
{
using var factory = new SVSimTestFactory();
// BattleNodeOptions is a singleton in DI; flipping it before the request takes
// effect immediately for this factory. Real deployments toggle it via the
// "BattleNode:SoloDefaultsToScripted" key in appsettings*.json.
factory.Services.GetRequiredService<BattleNodeOptions>().SoloDefaultsToScripted = true;
var vid = await factory.SeedViewerAsync();
await SeedCompleteTwoPickRunAsync(factory, vid);
using var client = factory.CreateAuthenticatedClient(vid);
var req = new {
deck_no = 1L, need_init = 1, log = 1, excluded_field_id_list = new long[] { }, use_stage_select = 1, is_default_skin = 0,
viewer_id = "0", steam_id = 0, steam_session_ticket = "",
};
// No ?scripted=1 — the flag alone should drive the Scripted branch.
var resp = await client.PostAsync("/arena_two_pick_battle/do_matching", JsonContent.Create(req));
Assert.That(resp.StatusCode, Is.EqualTo(HttpStatusCode.OK));
using var doc = JsonDocument.Parse(await resp.Content.ReadAsStringAsync());
var root = doc.RootElement;
Assert.That(root.GetProperty("matching_state").GetInt32(), Is.EqualTo(3004),
"SoloDefaultsToScripted=true should bypass pair-up and return a Scripted 3004 SUCCEEDED.");
Assert.That(root.GetProperty("battle_id").GetString(), Is.Not.Null.And.Not.Empty);
Assert.That(root.GetProperty("node_server_url").GetString(), Does.Contain("/socket.io/"));
}
[Test]
public async Task DoMatching_NoActiveRun_Returns400WithErrorCode()
{