feedback: plasma box, explicit fonts, and per-position text coalescing
Three endpoint gaps, found building the Descent 3 score overlay - the boxed place|score field the original games drew over the callsign: plasma box <x> <y> <w> <h> draws an outlined box with a blanked interior, the overlay chrome, as one ESC P graphics write. The wire addresses whole bytes horizontally, so the write covers the byte-aligned span containing the box and clears span pixels outside it; documented, with 8-px alignment the advice. Boxes queue FIFO with rows. plasma text gains an explicit font: a third numeric token after the position, with text still following, so `plasma text 2 2 7` still displays "7". Auto-fit picks the font by LENGTH - short text always rendered large, and a "1" that must fit a 12-px box simply could not be sent before. ResolvePosText generalizes the legacy Score-font special case: 0 = auto, nonzero honored. Text coalescing is now per position. The global rule - any queued text superseded every other queued text - meant the documented two-field layout (callsign top, score bottom) could not survive its own send burst: the second field silently ate the first whenever both were queued. A newer text now replaces only a queued text at the same (x,y). 15 new tests; 472 pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -18,12 +18,13 @@ namespace RioJoy.Core.Feedback;
|
||||
/// profile-owned lamps for the same reason.
|
||||
///
|
||||
/// Plasma writes are single-flight over a small bounded queue whose rules
|
||||
/// match what each command means: <c>text</c> coalesces (only the newest
|
||||
/// queued text survives — a flooding score updater shows the latest value),
|
||||
/// match what each command means: <c>text</c> coalesces per position (only the
|
||||
/// newest queued text at the same (x,y) survives — a flooding score updater
|
||||
/// shows the latest value, while other fields on the glass keep theirs),
|
||||
/// <c>clear</c> flushes everything queued before it, and bitmap <c>row</c>s
|
||||
/// are FIFO in arrival order (a frame is many rows; coalescing would tear
|
||||
/// it). The bound caps what a flooding client can queue against the
|
||||
/// 9600-baud display port.
|
||||
/// and <c>box</c>es are FIFO in arrival order (a frame is many rows;
|
||||
/// coalescing would tear it). The bound caps what a flooding client can queue
|
||||
/// against the 9600-baud display port.
|
||||
/// </summary>
|
||||
public sealed class FeedbackRouter
|
||||
{
|
||||
@@ -111,6 +112,7 @@ public sealed class FeedbackRouter
|
||||
case FeedbackCommandKind.PlasmaText:
|
||||
case FeedbackCommandKind.PlasmaClear:
|
||||
case FeedbackCommandKind.PlasmaRow:
|
||||
case FeedbackCommandKind.PlasmaBox:
|
||||
DispatchPlasma(target, command);
|
||||
break;
|
||||
}
|
||||
@@ -172,20 +174,30 @@ public sealed class FeedbackRouter
|
||||
break;
|
||||
|
||||
case FeedbackCommandKind.PlasmaText:
|
||||
// Only the newest text survives (a score updater shows the
|
||||
// latest value); queued rows keep their place.
|
||||
// Only the newest text FOR THE SAME POSITION survives (a
|
||||
// score updater shows the latest value). Texts at other
|
||||
// positions are other fields - the documented multi-field
|
||||
// layout (callsign top, score bottom) sends several in a
|
||||
// burst, and the original global coalescing ate all but
|
||||
// the last of them. Queued rows/boxes keep their place.
|
||||
for (int i = _plasmaQueue.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (_plasmaQueue[i].Kind == FeedbackCommandKind.PlasmaText)
|
||||
if (_plasmaQueue[i].Kind == FeedbackCommandKind.PlasmaText &&
|
||||
_plasmaQueue[i].X == command.X && _plasmaQueue[i].Y == command.Y)
|
||||
{
|
||||
_plasmaQueue.RemoveAt(i);
|
||||
Interlocked.Increment(ref _dropped); // superseded before it ran
|
||||
}
|
||||
}
|
||||
if (_plasmaQueue.Count >= MaxPlasmaQueue)
|
||||
{
|
||||
Interlocked.Increment(ref _dropped);
|
||||
return;
|
||||
}
|
||||
_plasmaQueue.Add(command);
|
||||
break;
|
||||
|
||||
default: // PlasmaRow: strict FIFO — a bitmap frame is many rows
|
||||
default: // PlasmaRow/PlasmaBox: strict FIFO — a bitmap frame is many rows
|
||||
if (_plasmaQueue.Count >= MaxPlasmaQueue)
|
||||
{
|
||||
Interlocked.Increment(ref _dropped); // client outran the display
|
||||
@@ -219,7 +231,10 @@ public sealed class FeedbackRouter
|
||||
{
|
||||
FeedbackCommandKind.PlasmaClear => target.Plasma!.ClearAsync(),
|
||||
FeedbackCommandKind.PlasmaRow => target.Plasma!.RowAsync(command.Y, command.Data!),
|
||||
_ => target.Plasma!.PosTextAsync(command.Text ?? string.Empty, command.X, command.Y),
|
||||
FeedbackCommandKind.PlasmaBox =>
|
||||
target.Plasma!.BoxAsync(command.X, command.Y, command.Width, command.Height),
|
||||
_ => target.Plasma!.PosTextAsync(command.Text ?? string.Empty, command.X, command.Y,
|
||||
0, command.Font),
|
||||
};
|
||||
|
||||
write.ContinueWith(w =>
|
||||
|
||||
Reference in New Issue
Block a user