Gitea #48: arm the artifact tripwires BY DEFAULT + surface detections through the matchlog
The user's ask: if the translation-table fix did not quite catch it, will the
next playtest LOG the artifact? Before this commit, no -- BT_PLANE_AUDIT was
opt-in via env var, and playtest machines set none.
Changes (all engine L4VB16):
* BT_PLANE_AUDIT is now DEFAULT-ON, =0 opts out. Armed cost is a couple of
integer compares per DRAW CALL (not per pixel); confirmed zero log noise
on a clean default-env run post-fix.
* A SECOND trap class: out-of-bounds draw STARTS, checked always-on in
buildDestPointer (the funnel every primitive builds its pointer through).
Release builds compile Verify() to nothing, so a wild start previously
computed a silent rogue pointer into (or past) the shared buffer. Now
logged ([plane] OOB) and CLAMPED in-bounds.
* First detection of each class writes a GLITCH matchlog record
(kind=plane / kind=oob with position+color+mask), so the evidence arrives
with the round logs the operator already collects -- unattended.
* Both existing plane-leak trap levels (display primitives + the port-level
pixmap table scan) flipped to the same default-on gate.
Honest coverage note (in gauges-hud.md): an in-buffer EXTENT overrun from a
valid start (a blit walking past the right/bottom edge into later rows of the
SAME plane) is not trapped -- per-pixel walk checks are too hot. The plane
trap still catches any such overrun whose color leaks planes, which is the
class the night-3 artifacts belonged to.
Verified: full clean rebuild -- 40 pre-existing LNK2019 only (the
engine->game BTMatchLog linkage resolved, /FORCE-trap checked); default-env
probe run: 0 [plane] lines, game unaffected.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
6bb03aed0b
commit
1d31af61d7
@@ -419,7 +419,7 @@ default-ON (`'0'` disables).
|
||||
| `BT_GAUGE_SKIP_LOG` | `[gskip] <primitive>` per unregistered gauge widget the parse skips |
|
||||
| `BT_VALVE_LOG` | condenser valve flow distribution |
|
||||
| `BT_BAYTEST=<frame>` | #46 rig: send message 1 (crit-induced cook-off arm) to the first AmmoBin at the given sim frame — the 10 s fuse then runs live (`scratchpad/baytest.py` / `baypurge.py`) |
|
||||
| `BT_PLANE_AUDIT` | #48 forensics: log every gauge draw whose color carries bits outside its port's plane mask (the cross-display leak trap; `[plane] LEAK/PORT` lines) |
|
||||
| `BT_PLANE_AUDIT` | #48 forensics -- **DEFAULT-ON** (`=0` opts out): logs any gauge draw whose color carries bits outside its port's plane mask (`[plane] LEAK/PORT`) AND any out-of-bounds draw start (`[plane] OOB`, clamped). First detection of each also writes a `GLITCH` **matchlog** record, so playtest evidence arrives with the round logs even unattended |
|
||||
| `BT_LOOP_AUDIT` | `[loop-audit]` per-sound `sample-flag × source-render-type -> AL_LOOPING` (Gitea #51; rig `scratchpad/loopaudit.py`) |
|
||||
| `BT_LOOP_LEGACY=1` | restore the OLD always-loop rule (`AL_LOOPING = sample != ForceStatic`) for a field A/B — see the loop-flag note in [[wintesla-port]] |
|
||||
| `BT_AUDIO_DUMP` | once-a-second `[playing]` dump of every playing AL source + gain/pitch/**loop** — catches a stuck looping source red-handed |
|
||||
|
||||
@@ -664,6 +664,16 @@ index-mod-2^bits colour IN-PLANE, can never leak). The 1995 binary ships the
|
||||
SAME 64-entry fill and relied on 6-bit art discipline -- garbage is not a
|
||||
preservable behaviour, so the cycle-fill is a guarded PORT deviation.
|
||||
Post-fix: **0 leaks over 60s** on the same probe; sim3 3-pod regression clean.
|
||||
**The tripwires are DEFAULT-ON in every build** (BT_PLANE_AUDIT=0 opts out): the
|
||||
plane-leak trap, plus an out-of-bounds draw-start trap in `buildDestPointer`
|
||||
(release `Verify` is a no-op, so a wild start previously wrote silently -- now
|
||||
logged `[plane] OOB` and clamped). First detection of either class writes a
|
||||
`GLITCH` matchlog record, so if the artifact has a third cause we have not
|
||||
seen, the evidence lands in the round logs the operator already collects --
|
||||
no env setup needed on playtest machines. (Coverage note: an in-buffer
|
||||
EXTENT overrun from a valid start -- a blit walking past the right/bottom edge
|
||||
into later rows of the same plane -- is NOT trapped; per-pixel walk checks are
|
||||
too hot. The plane trap still catches it whenever the color leaks planes.)
|
||||
SECOND real defect fixed en route: sessions configure the CAMERA seat first
|
||||
(`cameraInit` -- which builds the MISSION-REVIEW context: `configure(0, sec,
|
||||
0, 0x00FF, native, rgb, mrpal.pcc)`, a DirectColor context whose table holds
|
||||
|
||||
@@ -1768,6 +1768,43 @@ void
|
||||
Verify(screenY >= 0);
|
||||
Verify(screenY < height);
|
||||
|
||||
// #48 tripwire (DEFAULT-ON; BT_PLANE_AUDIT=0 opts out): release builds
|
||||
// compile Verify() to NOTHING, so an out-of-bounds draw start silently
|
||||
// computes a wild pointer into (or past) the shared buffer -- the OTHER
|
||||
// artifact class this funnel can catch, since every primitive builds its
|
||||
// start pointer here. Log + clamp; first hit also lands in the matchlog.
|
||||
{
|
||||
static int s_oobOn = -1;
|
||||
if (s_oobOn < 0)
|
||||
{
|
||||
const char *e = getenv("BT_PLANE_AUDIT");
|
||||
s_oobOn = (e != NULL && *e == '0') ? 0 : 1;
|
||||
}
|
||||
if (s_oobOn &&
|
||||
(screenX < 0 || screenX >= width || screenY < 0 || screenY >= height))
|
||||
{
|
||||
static int s_oobCount = 0;
|
||||
++s_oobCount;
|
||||
if (s_oobCount == 1)
|
||||
{
|
||||
extern int BTMatchLogActive();
|
||||
extern void BTMatchLog(const char *tag, const char *format, ...);
|
||||
if (BTMatchLogActive())
|
||||
BTMatchLog("GLITCH", "kind=oob x=%d y=%d w=%d h=%d",
|
||||
screenX, screenY, width, height);
|
||||
}
|
||||
if (s_oobCount <= 100 || (s_oobCount % 500) == 0)
|
||||
DEBUG_STREAM << "[plane] OOB #" << s_oobCount
|
||||
<< " draw start at(" << screenX << "," << screenY
|
||||
<< ") outside " << width << "x" << height
|
||||
<< " -- clamped" << "\n" << std::flush;
|
||||
if (screenX < 0) screenX = 0;
|
||||
if (screenX >= width) screenX = width - 1;
|
||||
if (screenY < 0) screenY = 0;
|
||||
if (screenY >= height) screenY = height - 1;
|
||||
}
|
||||
}
|
||||
|
||||
*pixel_pointer = pixelBuffer.Data.MapPointer +
|
||||
screenX +
|
||||
(screenY * width);
|
||||
@@ -1861,8 +1898,15 @@ void
|
||||
static void BTPlaneAudit(const char *prim, int color, int bitmask,
|
||||
Enumeration operation, int x, int y)
|
||||
{
|
||||
// DEFAULT-ON (#48 tripwire): playtest machines set no env vars, so the
|
||||
// trap must arm itself. BT_PLANE_AUDIT=0 opts out. Cost when armed is a
|
||||
// couple of integer ops per DRAW CALL (not per pixel).
|
||||
static int s_on = -1;
|
||||
if (s_on < 0) s_on = (getenv("BT_PLANE_AUDIT") != NULL) ? 1 : 0;
|
||||
if (s_on < 0)
|
||||
{
|
||||
const char *e = getenv("BT_PLANE_AUDIT");
|
||||
s_on = (e != NULL && *e == '0') ? 0 : 1;
|
||||
}
|
||||
if (!s_on) return;
|
||||
|
||||
int leak;
|
||||
@@ -1879,6 +1923,17 @@ static void BTPlaneAudit(const char *prim, int color, int bitmask,
|
||||
|
||||
static int s_count = 0;
|
||||
++s_count;
|
||||
// Surface the FIRST detection through the matchlog pipeline -- those files
|
||||
// are collected after every playtest round, so the evidence arrives even
|
||||
// if nobody was watching btl4.log.
|
||||
if (s_count == 1)
|
||||
{
|
||||
extern int BTMatchLogActive();
|
||||
extern void BTMatchLog(const char *tag, const char *format, ...);
|
||||
if (BTMatchLogActive())
|
||||
BTMatchLog("GLITCH", "kind=plane prim=%s x=%d y=%d color=0x%x mask=0x%x leak=0x%x op=%d",
|
||||
prim, x, y, color, bitmask, leak, (int)operation);
|
||||
}
|
||||
if (s_count <= 200 || (s_count % 500) == 0)
|
||||
DEBUG_STREAM << "[plane] LEAK #" << s_count << " " << prim
|
||||
<< " at(" << x << "," << y << ")"
|
||||
@@ -3921,7 +3976,7 @@ void
|
||||
{
|
||||
{
|
||||
static int s_paOn = -1;
|
||||
if (s_paOn < 0) s_paOn = (getenv("BT_PLANE_AUDIT") != NULL) ? 1 : 0;
|
||||
if (s_paOn < 0) { const char *e = getenv("BT_PLANE_AUDIT"); s_paOn = (e != NULL && *e == '0') ? 0 : 1; }
|
||||
if (s_paOn && translation_table != NULL)
|
||||
for (int _pa = 0; _pa < 256; ++_pa)
|
||||
if ((translation_table[_pa] & ~bitmask & 0xFFFF) != 0)
|
||||
@@ -6447,7 +6502,7 @@ void
|
||||
// #48 forensics: name the PORT + pixmap for any leaking table entries.
|
||||
{
|
||||
static int s_paOn = -1;
|
||||
if (s_paOn < 0) s_paOn = (getenv("BT_PLANE_AUDIT") != NULL) ? 1 : 0;
|
||||
if (s_paOn < 0) { const char *e = getenv("BT_PLANE_AUDIT"); s_paOn = (e != NULL && *e == '0') ? 0 : 1; }
|
||||
if (s_paOn)
|
||||
for (int _pa = 0; _pa < 256; ++_pa)
|
||||
if ((translationTable[_pa] & ~bitMask & 0xFFFF) != 0)
|
||||
|
||||
Reference in New Issue
Block a user