#156 the mission start/end fade RECONSTRUCTED: POVStartEndRenderable was an inert stub (rounds hard-cut) -- the binary's 5-phase fog machine (ctor @00454394, Execute @0045447c; constants byte-read @0x454740-50: flash 0.1s, fades 1.0s, plane floors 0.01/0.05) now runs at the renderer frame head keyed on the local player's SimulationState (3 MissionStarting -> white flash + 1.0s fade-in from white; 4 MissionEnding -> 1.0s fade-to-black, planes closing to the floors, re-arm). Drives the live fog pipeline (currentFogNear/Far + FOGCOLOR, which the frame Clear also samples -- the fade owns sky/void too) and re-reads the STYLE members per frame exactly as the binary re-reads live view fog (mid-fade searchlight swaps track). Armed by the now-real ctor in btl4vid.cpp (house pattern: inert object, module machine). Bench fade_bench.sh: 40s relay-clocked round, both nodes PASS armed + phases 1/2/3/4 in order with the end edge on simState=4. Rider: the [rio] STALL detector now keys on REPLY drought instead of request age (which the 50ms poll refreshed forever -- the detector could never fire; #160 telemetry).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
2938d4918c
commit
fc71f9c0bb
@@ -1377,11 +1377,19 @@ Logical
|
||||
if (operational)
|
||||
{
|
||||
DWORD now = GetTickCount();
|
||||
// #160 telemetry fix: the detector used to key on REQUEST age -- but the
|
||||
// poll loop sends a fresh request every ~50ms regardless of replies, so
|
||||
// request-age never reached 500ms and the STALL line could never fire
|
||||
// (the field logs' age= field carried the signal instead). Key on the
|
||||
// REPLY drought: requests outstanding AND no AnalogReply for 500ms
|
||||
// since the last good one. Requires one good reply first (boot-mute
|
||||
// shows in the req/rep counters, not here).
|
||||
if (!gRioStallActive && gRioReqPending
|
||||
&& now - gRioLastReqTick > 500)
|
||||
&& gRioLastReplyTick != 0
|
||||
&& now - gRioLastReplyTick > 500)
|
||||
{
|
||||
gRioStallActive = 1;
|
||||
gRioStallStart = gRioLastReqTick;
|
||||
gRioStallStart = gRioLastReplyTick;
|
||||
gRioStallBtnBase = gRioBtnCount;
|
||||
}
|
||||
gRioPresent = 1;
|
||||
|
||||
@@ -8441,6 +8441,107 @@ void DPLRenderer::ExecuteImplementation(RendererComplexity, RendererOrigin::Inte
|
||||
lastFrameTime = mTargetRenderTime;
|
||||
currentFrameTime = Now();
|
||||
|
||||
// #156 -- the MISSION START/END FADE (POVStartEndRenderable, ctor @00454394,
|
||||
// Execute @0045447c; the port class is armed by MakeEntityRenderables'
|
||||
// BTPlayer master case via BTMissionFadeArm). The 1995 renderable drives
|
||||
// the POV zone fog through a 5-phase machine keyed on the local player's
|
||||
// SimulationState; constants byte-read from BTL4OPT.EXE @0x454740-0x454750:
|
||||
// flash hold 0.1s, fades 1.0s, plane floors near 0.01 / far 0.05. Phases:
|
||||
// 0 armed -> (MissionStarting) 1 WHITE FLASH (fog 1,1,1 @ 0.01/0.05) ->
|
||||
// 2 FADE-IN (white -> live style; planes floor -> authored) -> 3 running ->
|
||||
// (MissionEnding) 4 FADE-OUT (color x local -> BLACK, planes close) ->
|
||||
// re-arm. The binary re-reads the LIVE fog each frame mid-fade (style
|
||||
// swaps track); we read the STYLE members (fogRed/G/B, fogNear/Far), never
|
||||
// currentFogNear/Far which we are overriding. Placed at the frame head:
|
||||
// the Clear below samples FOGCOLOR, so the fade owns the WHOLE frame
|
||||
// including sky/void. Receipts: [fade] phase lines under BT_FADE_LOG or
|
||||
// BT_FOG_LOG.
|
||||
{
|
||||
extern void *gBTFadeIndicator; // BTMissionFadeArm (btl4vid.cpp)
|
||||
extern int gBTFadeStart, gBTFadeEnd;
|
||||
static int s_phase = 0;
|
||||
static float s_T = 0.0f;
|
||||
if (gBTFadeIndicator != 0)
|
||||
{
|
||||
const float kFlash = 0.1f, kFade = 1.0f; // @0x454740 / @0x454744
|
||||
const float kNearFloor = 0.01f, kFarFloor = 0.05f; // @0x454750 / @0x45474c
|
||||
int st = ((StateIndicator *)gBTFadeIndicator)->GetState();
|
||||
float fdt = (float)dT;
|
||||
if (fdt < 0.0f) fdt = 0.0f; else if (fdt > 0.1f) fdt = 0.1f;
|
||||
float r = -1.0f, g = 0.0f, b = 0.0f, fn = 0.0f, ff = 0.0f;
|
||||
switch (s_phase)
|
||||
{
|
||||
case 0:
|
||||
if (st == gBTFadeStart) { s_phase = 1; s_T = 0.0f; }
|
||||
break;
|
||||
case 1:
|
||||
r = 1.0f; g = 1.0f; b = 1.0f; fn = kNearFloor; ff = kFarFloor;
|
||||
s_T += fdt;
|
||||
if (s_T >= kFlash) { s_phase = 2; s_T = 0.0f; }
|
||||
break;
|
||||
case 2:
|
||||
{
|
||||
float local = kFade - s_T; if (local < 0.0f) local = 0.0f;
|
||||
float el = kFade - local;
|
||||
r = (1.0f - fogRed) * local + fogRed;
|
||||
g = (1.0f - fogGreen) * local + fogGreen;
|
||||
b = (1.0f - fogBlue) * local + fogBlue;
|
||||
fn = fogNear * el + kNearFloor;
|
||||
ff = fogFar * el + kFarFloor;
|
||||
s_T += fdt;
|
||||
if (local <= 0.0f) { s_phase = 3; r = -1.0f; } // fog back to the style
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
if (st == gBTFadeEnd) { s_phase = 4; s_T = 0.0f; }
|
||||
break;
|
||||
case 4:
|
||||
{
|
||||
float local = kFade - s_T; if (local < 0.0f) local = 0.0f;
|
||||
r = fogRed * local;
|
||||
g = fogGreen * local;
|
||||
b = fogBlue * local;
|
||||
fn = fogNear * local + kNearFloor;
|
||||
ff = fogFar * local + kFarFloor;
|
||||
s_T += fdt;
|
||||
if (local <= 0.0f) { s_phase = 0; } // re-armed; final frame stays black
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (r >= 0.0f)
|
||||
{
|
||||
currentFogNear = fn;
|
||||
currentFogFar = ff;
|
||||
mDevice->SetRenderState(D3DRS_FOGCOLOR,
|
||||
D3DCOLOR_XRGB((int)(255 * r), (int)(255 * g), (int)(255 * b)));
|
||||
mDevice->SetRenderState(D3DRS_FOGENABLE, TRUE);
|
||||
}
|
||||
else if (s_phase == 3 || s_phase == 0)
|
||||
{
|
||||
// Out of the fade: restore the style planes once so a fade that
|
||||
// ended mid-style leaves the world exactly on its authored fog.
|
||||
static int s_prev = -1;
|
||||
if (s_prev != s_phase)
|
||||
{
|
||||
currentFogNear = fogNear;
|
||||
currentFogFar = fogFar;
|
||||
mDevice->SetRenderState(D3DRS_FOGCOLOR,
|
||||
D3DCOLOR_XRGB((int)(255 * fogRed), (int)(255 * fogGreen),
|
||||
(int)(255 * fogBlue)));
|
||||
}
|
||||
s_prev = s_phase;
|
||||
}
|
||||
static int s_lastLogged = -1;
|
||||
if (s_phase != s_lastLogged)
|
||||
{
|
||||
s_lastLogged = s_phase;
|
||||
if (getenv("BT_FADE_LOG") || getenv("BT_FOG_LOG"))
|
||||
DEBUG_STREAM << "[fade] phase " << s_phase
|
||||
<< " (simState=" << st << ")\n" << std::flush;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DIAG (turn-hitch hunt): time the render phases -- draw CPU vs Present
|
||||
// (GPU-queue block). Logged on slow frames + 1 Hz stats.
|
||||
LARGE_INTEGER _rt0; QueryPerformanceCounter(&_rt0);
|
||||
|
||||
@@ -3687,6 +3687,35 @@ namespace {
|
||||
|
||||
extern void BTSetWorldDead(int dead); // L4VIDRND.cpp bridge -> l4_application->SetIsDead
|
||||
|
||||
//===========================================================================//
|
||||
// #156 -- the mission start/end fade arming seam. The phase machine itself
|
||||
// runs at the renderer frame head (L4VIDEO.cpp, beside the FOGCOLOR clear);
|
||||
// these globals are what MakeEntityRenderables' BTPlayer master case arms
|
||||
// through the now-real BTPOVStartEndRenderable ctor below.
|
||||
//===========================================================================//
|
||||
void *gBTFadeIndicator = 0;
|
||||
int gBTFadeStart = 3, gBTFadeEnd = 4;
|
||||
|
||||
// The real ctor (was a btstubs.cpp inert stub -- the #156 root cause: rounds
|
||||
// hard-cut instead of fading). Binary ctor @00454394 stores the indicator +
|
||||
// the authored fog params and registers on the view's watcher list; the port
|
||||
// keeps the object inert (house pattern, like the translocation renderable)
|
||||
// and arms the frame-head machine instead. The fog params ride the renderer
|
||||
// members live, exactly as the binary re-reads them per fade frame.
|
||||
BTPOVStartEndRenderable::BTPOVStartEndRenderable(
|
||||
Entity *entity, int, dpl_VIEW *, dpl_ZONE *, dpl_ZONE *,
|
||||
StateIndicator *sim_state,
|
||||
float, float, float, float, float, int start_state, int end_state)
|
||||
: BTRenderableBase(entity)
|
||||
{
|
||||
gBTFadeIndicator = (void *)sim_state;
|
||||
gBTFadeStart = start_state;
|
||||
gBTFadeEnd = end_state;
|
||||
if (getenv("BT_FADE_LOG") || getenv("BT_FOG_LOG"))
|
||||
DEBUG_STREAM << "[fade] armed (states " << start_state << "/"
|
||||
<< end_state << ")\n" << std::flush;
|
||||
}
|
||||
|
||||
// The renderable objects the entity tree builds for the translocation wiring are
|
||||
// inert now (the warp is the self-contained one-shot below); the ctor/dtor just
|
||||
// satisfy MakeEntityRenderables.
|
||||
|
||||
@@ -336,10 +336,9 @@ BTMarkerWatcherRenderable::BTMarkerWatcherRenderable(
|
||||
// BTTranslocationRenderable (the "blue warp" translocation sphere) is now a real
|
||||
// reconstruction in btl4vid.cpp (task #52) -- no longer a stub here.
|
||||
|
||||
BTPOVStartEndRenderable::BTPOVStartEndRenderable(
|
||||
Entity *entity, int, dpl_VIEW *, dpl_ZONE *, dpl_ZONE *, StateIndicator *,
|
||||
float, float, float, float, float, int, int)
|
||||
: BTRenderableBase(entity) {}
|
||||
// BTPOVStartEndRenderable (the mission start/end fade) is now a real
|
||||
// reconstruction: the arming ctor lives in btl4vid.cpp and the 5-phase fog
|
||||
// machine at the renderer frame head (L4VIDEO.cpp) -- #156.
|
||||
|
||||
BTTracerEffectRenderable::BTTracerEffectRenderable(
|
||||
Entity *entity, int, void *, int, dpl_ZONE *, dpl_DCS *, LinearMatrix *)
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env bash
|
||||
# #156: the mission start/end fade. 2-node, 40s round -- the relay owns the
|
||||
# clock and sends StopMission at length end (btconsole.py), so both fade edges
|
||||
# fire: MissionStarting (white flash 0.1s + 1.0s fade-in from white) and
|
||||
# MissionEnding (1.0s fade-out to black; the app stops ~2s later inside the
|
||||
# player's 3.0s ladder). Receipts: [fade] armed + phase 1/2/3/4 transitions
|
||||
# (BT_FADE_LOG). PASS: node A's log shows armed, then phases 1,2,3,4 in
|
||||
# order, and the phase-4 line's simState is the ending state (4).
|
||||
set -x
|
||||
. /c/git/bt411/scratchpad/night6/bench_common.sh
|
||||
cd /c/git/bt411/content || exit 1
|
||||
taskkill //F //IM btl4.exe >/dev/null 2>&1; sleep 3
|
||||
rm -f fade_a.log fade_b.log fade_r.log
|
||||
bt_expert_egg MP.EGG FADE.EGG
|
||||
sed -i "s/^map=.*/map=grass/; s/^time=.*/time=day/; s/^vehicle=.*/vehicle=madcat/; s/^length=.*/length=40/" FADE.EGG
|
||||
grep -aq "^length=40" FADE.EGG || { echo "FAIL: egg length not set"; exit 1; }
|
||||
|
||||
( export BT_FADE_LOG=1 BT_MATCHLOG=1
|
||||
bt_launch fade_b.log FADE.EGG 0x0C -net 1601 )
|
||||
sleep 2
|
||||
( export BT_FADE_LOG=1 BT_MATCHLOG=1
|
||||
bt_launch fade_a.log FADE.EGG 0x03 -net 1501 )
|
||||
sleep 5
|
||||
python ../tools/btconsole.py FADE.EGG 127.0.0.1:1501 127.0.0.1:1601 > fade_r.log 2>&1 &
|
||||
R=$!; sleep 110; kill $R 2>/dev/null; sleep 2
|
||||
bt_kill_ours; sleep 2; taskkill //F //IM btl4.exe >/dev/null 2>&1
|
||||
|
||||
python - <<'PY'
|
||||
import io, re, sys
|
||||
ok = True
|
||||
for node in ("fade_a.log", "fade_b.log"):
|
||||
txt = io.open(node, encoding="latin-1", errors="replace").read()
|
||||
armed = "[fade] armed" in txt
|
||||
phases = [int(m.group(1)) for m in re.finditer(r"\[fade\] phase (\d)", txt)]
|
||||
end4 = re.search(r"\[fade\] phase 4 \(simState=4\)", txt) is not None
|
||||
print("%s: armed=%d phases=%s end-edge-ok=%d" % (node, armed, phases, end4))
|
||||
want = [1, 2, 3, 4]
|
||||
seq_ok = [p for p in phases if p in want] [:4] == want
|
||||
if not (armed and seq_ok and end4):
|
||||
ok = False
|
||||
print("VERDICT:", "PASS -- flash + fade-in + fade-out ran on both nodes"
|
||||
if ok else "FAIL -- phase sequence incomplete (see above)")
|
||||
sys.exit(0 if ok else 1)
|
||||
PY
|
||||
Reference in New Issue
Block a user