fix #420: seed face-segment media states so character select stops crashing the client
Every launcher-started play session on 2026-08-19 died a few seconds after
login. The user's own session evidence shows it three times in a row:
started -> connected -> characterList -> exited code 1 "crashed", with
client.err.log carrying
System.ArgumentNullException: Value cannot be null. (Parameter 'key')
at System.Collections.Generic.Dictionary`2.FindValue(TKey key)
at AcDream.App.UI.UiButton.OnDraw(UiRenderContext ctx)
UiButton allocated its per-face-segment media-state array as `new string[n]`,
leaving every element null, while the single-face sibling _faceMediaState was
correctly seeded to "" (DirectState). NextMediaState returns `current`
unchanged on three of its four arms — including retail's own "committed state
authored with an empty media array keeps the previous media playing" rule — so
on a multi-segment button whose committed state carries no media the null
survived the first SyncMediaStates and reached
ElementInfo.StateMedia.TryGetValue(null), throwing mid-paint and taking the
process down.
Seed the array with "" at construction. That is what the constructor's
existing comment already claimed the media machine did ("the media machine
begins on the element's BASE media"); only the segment array was left out.
Verified by reverting the one-line fix: the new regression test throws
ArgumentNullException from UiButton.ActiveFile, the same frame as the live
crash. AcDream.App.Tests UiButton filter: 41 passed, 3 skipped.
Found while investigating Campaign LU item 4 ("launching the selected
character doesn't work") — this is why nothing worked. Also lands the Campaign
LU plan doc, whose recon section records the mechanisms the remaining slices
build on.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
a3b0455f59
commit
a34e8f2a17
4 changed files with 344 additions and 0 deletions
|
|
@ -745,6 +745,35 @@ public class UiButtonTests
|
|||
private static UiButton CreateButton(ElementInfo info)
|
||||
=> new(info, NoTex) { Width = info.Width, Height = info.Height };
|
||||
|
||||
/// <summary>
|
||||
/// #420 regression. A multi-segment face whose committed state authors no
|
||||
/// media used to leave that segment's media-state name NULL (the array
|
||||
/// started as <c>new string[n]</c> and NextMediaState returns the previous
|
||||
/// value unchanged on that arm), and the null then reached
|
||||
/// <c>ElementInfo.StateMedia.TryGetValue</c>, throwing
|
||||
/// ArgumentNullException from inside OnDraw. Live symptom: the client
|
||||
/// crashed on the character-select screen on every launch.
|
||||
///
|
||||
/// <para>The assertion is secondary — the point is that drawing COMPLETES.
|
||||
/// Before the fix this test throws instead of failing.</para>
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void MultiSegmentFace_CommittedStateWithoutMedia_DrawsInsteadOfThrowing()
|
||||
{
|
||||
var info = new ElementInfo { Type = 1, Width = 32, Height = 16 };
|
||||
var button = new UiButton(
|
||||
info,
|
||||
static file => (file, 8, 8),
|
||||
mediaInfo: null,
|
||||
faceSegments: [new ElementInfo { Type = 1, Width = 16, Height = 16 }])
|
||||
{
|
||||
Width = info.Width,
|
||||
Height = info.Height,
|
||||
};
|
||||
|
||||
Assert.Equal(0u, DrawnFaceFile(button));
|
||||
}
|
||||
|
||||
// ── #416 media-rule draw harness ─────────────────────────────────────
|
||||
|
||||
private sealed class NullGpuFrameSource
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue