#82 ROOT FIX: reconstruct the crash SELF-DAMAGE -- wall-grinding now hurts,
the knockdown storm self-limits, peers keep the limp
The 'peers see a limping mech skating' report decomposed into a wall-grind
KNOCKDOWN STORM: a gimped mech held against a blocking solid re-crashes each
time its gg cycle rebuilds speed (the gg ceiling is authentically the clip's
natural speed -- bhk1 ~14.9 u/s, ceiling+divisor @0x544/0x548, loader formula
byte-matched -- so iv2 ~222 >> the 40.0 threshold @0x4ab184), and every
type-5 KNOCKDOWN broadcast floors all peers' replicants into knockdown/trn
churn: gliding + leg-lifts = skating. Turning and crushable cars were
A/B-exonerated (grass circling: 198 replicant gimp entries, 17 crunches,
zero broadcasts).
What the binary has that the port lacked -- recovered from the EXPORT GAP by
raw disasm (scratchpad/night6/gap_4a9770.txt, @4aa89f-4aaab4) -- is the crash
branch's tail: fallDirection = worldToLocal(damageForce) (FUN_0040879c, M^T v,
un-normalized), fallScalar = -(fallDirection . localVelocity.linearMotion),
and a self-dispatched TakeDamageMessage{0x64, zone=-1, the engine-computed
collision Damage verbatim}. A wall crash COSTS ARMOR, so the grind degrades
the mech instead of looping forever. That dispatch was the long-standing
'DEFERRED' note in the response policy; now reconstructed.
Falsified en route (comments + KB corrected, do not revive): the stagger's
FUN_004a4c54(1)/(0x20) 'action-request flags' feed NO drive suppressor --
image-wide sweep + full gap disasm show word[this+0x18] is read only by the
net record emitter; the bmp clip applies NO root motion (adv=0 measured); the
'gimp cap 5.8' figure was the BACK-cycle stride printed under a misleading
label (now ggCapL/R; @0x52c has no binary consumer).
Verified (mp_gimpAB rigs): arena1 wall-grind now 4 bounded strikes, limper
dies of its crashes and respawns cleanly twice (START->RESET, no strand);
grass circling unchanged-clean (198 replicant gimp states, 0 knocks).
Rig: pid capture race fixed (a missed winpid left a stale node holding the
-net port -> null runs), per-config sweep added.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
19fe1e5fa2
commit
9c83a468db
+27
-16
@@ -212,22 +212,33 @@ knockdown must stagger BOTH channels (`SetBodyAnimation(0x20)` + `SetLegAnimatio
|
||||
stops the knockdown re-firing on sustained/glancing contact. `localVelocity` is zeroed while crashed
|
||||
(spec-faithful) so a knocked mech can't keep advancing. [T2]
|
||||
|
||||
**⚠ The WALL-GRIND KNOCKDOWN STORM = the surviving "#82 peers see skating" (2026-07-30) [T2].**
|
||||
Each stagger bind also broadcasts `ForceUpdate(0x20)` — the **type-5 KNOCKDOWN record** — and the
|
||||
replicant handler (`mech.cpp` case 5) plays the bmp clip AND zeroes `projectedVelocity`. The bmp
|
||||
clip carries ~6.5 u/s of root motion pressing back into the wall (6.5² ≈ 42 > the iv²>40 threshold),
|
||||
so **against a wall the stagger self-retriggers**: a gimped mech grinding a wall re-broadcast 26
|
||||
knockdowns in one bench run, and every peer's replicant lived in knockdown/recovery churn — never
|
||||
re-entering the gimp cycle. On the observer that reads as gliding + trn leg-lifting = "skating."
|
||||
4-config A/B (`scratchpad/night6/mp_gimpAB.sh`, `[animind]` inst=1 verdict): grass straight/circle
|
||||
**198 gimp-state entries, clean limp** (17 car crunches during it — crushable icons are the
|
||||
`0.00123f` sentinel path: gyro kick only, NO broadcast, exonerated); arena1 straight = 1 strike,
|
||||
replicant recovered to gimp (196); arena1 circle (wall grind) = **0 gimp entries, skating**. So:
|
||||
turning innocent, cars innocent, single strikes recover — the STORM is the whole bug, and it is
|
||||
field-relevant (legged players hug walls). **The authentic fix is the un-reconstructed consumer of
|
||||
the stagger's action-request flags** (`FUN_004a4c54(this,1)` + `(this,0x20)` — the drive suppressor
|
||||
during the stagger; the port's rebind guard at mech4.cpp:6861 is marked bring-up pending exactly
|
||||
this). Tracked in [[open-questions]]; replication detail in [[multiplayer]].
|
||||
**⚠ The WALL-GRIND KNOCKDOWN STORM = the surviving "#82 peers see skating" — ROOT-CAUSED + FIXED
|
||||
(2026-07-30) [T1 disasm / T2 verified].** Each stagger bind also broadcasts `ForceUpdate(0x20)` —
|
||||
the **type-5 KNOCKDOWN record** — and the replicant handler (`mech.cpp` case 5) plays the bmp clip
|
||||
AND zeroes `projectedVelocity`. A gimped mech held against a wall re-crashes each time its gg cycle
|
||||
rebuilds speed (the gg ceiling is authentically the CLIP'S NATURAL SPEED — bhk1 ≈ 14.9 u/s, ratio
|
||||
divisor + accel clamp @0x544/0x548, loader formula byte-matched — so 14.9² ≈ 222 ≫ the 40.0 crash
|
||||
threshold @0x4ab184): 26 broadcasts in one bench run, every peer's replicant in knockdown/recovery
|
||||
churn — gliding + trn leg-lifts = "skating". 4-config A/B (`scratchpad/night6/mp_gimpAB.sh`,
|
||||
`[animind]` inst=1 verdict): grass straight/circle clean (198 gimp entries; **17 car crunches
|
||||
during it — crushable icons are the `0.00123f` sentinel path, gyro kick only, NO broadcast**);
|
||||
arena1 single strike recovers (196); arena1 wall-grind = 0 gimp entries.
|
||||
**What actually bounds the loop in the binary — and what the port was missing — is the crash
|
||||
SELF-DAMAGE**: the blocking branch (export gap @4aa89f-4aaab4, raw disasm
|
||||
`scratchpad/night6/gap_4a9770.txt`) fills `throttleState`=surface material,
|
||||
`fallDirection`=worldToLocal(dmg.damageForce) (FUN_0040879c = Mᵀv, un-normalized),
|
||||
`fallScalar`=−(fallDirection·localVelocity.linearMotion), then **Dispatches
|
||||
`TakeDamageMessage{0x64, zone=−1, the engine-computed collision Damage verbatim}` at its own
|
||||
mech** — wall crashes hurt, grinding degrades the mech, the storm self-limits. That dispatch was
|
||||
the port's long-standing "DEFERRED" note; now reconstructed (mech4.cpp blocking branch).
|
||||
**Falsified en route (do not revive):** (a) "the stagger's action-request flags feed a drive
|
||||
suppressor" — image-wide + full-gap sweep shows `word[this+0x18]` is read ONLY by the net record
|
||||
emitter; (b) "the bmp clip's ~6.5 u/s root motion presses back into the wall" — measured `adv=0
|
||||
cycleSpeed=0` during state 0x20; the re-strike velocity is the REBUILT gg cycle; (c) "the gimp cap
|
||||
is ±5.8 (so √40 sits just above it)" — that number is `gimpStrideLength`@0x350 (the BACK-cycle
|
||||
figure, printed under a misleading label since renamed `ggCapL/R`); field @0x52c (`gimpSpeedMax`)
|
||||
has NO binary consumer. Advance caps, loader fills, crunch path and crash branch are all verified
|
||||
byte-faithful — the self-damage dispatch was the only gap. Replication detail in [[multiplayer]].
|
||||
|
||||
## Controls (`BT_REAL_CONTROLS`, default-on)
|
||||
`MechControlsMapper` (mechmppr.cpp @004afbe0; btl4mppr.cpp mappers) interprets input → `speedDemand`
|
||||
|
||||
@@ -761,14 +761,12 @@ register. ⚠ The audit also flags the damage-economy item as SELF-CONTRADICTOR
|
||||
numbers no longer point at that code).
|
||||
|
||||
## Multiplayer (Phase 7 / P6)
|
||||
- **Stagger drive-suppressor reconstruction (the #82 wall-grind fix; 2026-07-30).** The binary's
|
||||
crash/stagger sets action-request flags via `FUN_004a4c54(this,1)` then `(this,0x20)`; their
|
||||
CONSUMER (almost certainly a drive/steering suppressor while the bmp clip plays) is not
|
||||
reconstructed, so the clip's ~6.5 u/s root motion re-strikes the wall and the knockdown
|
||||
broadcast storms (26× one bench run) — peers' replicants churn knockdown/trn = "skating"
|
||||
([[locomotion]] §Knockdown for the full A/B). Dig: find the actionRequestFlags bit-1/bit-0x20
|
||||
readers in the decomp (mech performance / drive integrate), reconstruct, then drop the marked
|
||||
rebind guard at mech4.cpp:6861. Until then the limp bench should circle on GRASS (no walls).
|
||||
- **#82 wall-grind storm — CLOSED 2026-07-30** (the "stagger drive-suppressor" theory that
|
||||
briefly lived here was falsified: `word[this+0x18]` has no consumer but the record emitter).
|
||||
The real missing piece was the crash SELF-DAMAGE dispatch (binary @4aa984-4aaab4), now
|
||||
reconstructed — see [[locomotion]] §Knockdown. Residue actually still open: the crash's
|
||||
`throttleState = fall-surface material` fill (binary `(**[mech+0x2f8])+0x24`; payload nicety
|
||||
for the type-5 reader's surface-specific fall handling — needs the assistant-record map).
|
||||
|
||||
- **Cross-fire bench opens (2026-07-30, first real-weapons 4-node runs).** (a) COMBAT STALL: all
|
||||
firing and dying ceased ~3 min in (run 2, throttled `BT_AF_PERIOD=7`) with everyone alive,
|
||||
|
||||
@@ -480,8 +480,15 @@ void
|
||||
turnCapable = 1;
|
||||
animationClips[4] = *ResolveAnimationClip(prefix, "trn"); // 0x5dc
|
||||
}
|
||||
// (#82 dig 2026-07-30: this line used to print `gimpSpeedMax` @0x52c --
|
||||
// a field NOTHING in the binary reads (image-wide: no mech consumer) whose
|
||||
// negative back-cycle value sent the wall-grind investigation down a
|
||||
// "gimp cap is 5.8" red herring. Print the fields the gimp cycle ACTUALLY
|
||||
// caps on: gimpLeft/RightStrideLength = the gg clips' natural speeds,
|
||||
// the accel ceilings + ratio divisors @0x544/0x548.)
|
||||
DEBUG_STREAM << "[loadclips] end: fScale=" << forwardThrottleScale
|
||||
<< " gimpSpeedMax=" << gimpSpeedMax << " hasGimpClips=" << hasGimpClips << "\n" << std::flush;
|
||||
<< " ggCapL=" << gimpLeftStrideLength << " ggCapR=" << gimpRightStrideLength
|
||||
<< " hasGimpClips=" << hasGimpClips << "\n" << std::flush;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -599,6 +599,13 @@ int gBTHudPrimary = 1; // PrimaryHudOn (element mask 0x20): full HUD vs si
|
||||
int gBTGotoActive = 0;
|
||||
float gBTGotoTurn = 0.0f;
|
||||
float gBTGotoThrottle = 1.0f;
|
||||
// BT_GOTO_RELEASE latch: the beeline has arrived and handed the mech to the
|
||||
// pilot. It must be visible to BOTH goto drive paths -- the self-drive primer
|
||||
// (which re-asserts forced/0.8 every frame purely because BT_GOTO is set in the
|
||||
// env) and the beeline block itself. Without it the primer overrode the
|
||||
// handover on the very next frame and the mech walked straight past its target
|
||||
// at full throttle, un-steered.
|
||||
int gBTGotoReleased = 0;
|
||||
static int gDriveSeeded = 0;
|
||||
static Scalar gDriveLogAccum = 0.0f; // 1 Hz position log throttle
|
||||
static Scalar gTickLogAccum = 0.0f; // 1 Hz subsystem-tick log throttle
|
||||
@@ -3214,12 +3221,14 @@ void
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (sGotoDrive)
|
||||
else if (sGotoDrive && !gBTGotoReleased)
|
||||
{
|
||||
// BT_GOTO is self-driving: enable forced mode so the goto beeline
|
||||
// block (gated on gBTDrive.forced, below) runs on its own without
|
||||
// requiring BT_AUTODRIVE. That block sets the actual throttle
|
||||
// (drive toward the target, 0 inside the stop radius).
|
||||
// gBTGotoReleased gates it: once BT_GOTO_RELEASE has handed the
|
||||
// mech over, this primer must STOP re-arming forced drive.
|
||||
gBTDrive.forced = 1;
|
||||
gBTDrive.forcedThrottle = 0.8f;
|
||||
}
|
||||
@@ -3539,6 +3548,17 @@ void
|
||||
{ bestG = ed2; gxG = (float)ep.x; gzG = (float)ep.z; foundG = 1; }
|
||||
}
|
||||
if (foundG) { s_gx = gxG; s_gz = gzG; }
|
||||
else if (s_goto == 3)
|
||||
{
|
||||
// "gimp" mode with NOBODY limping yet: STAND STILL and wait
|
||||
// for one. Falling back to the nearest peer made every
|
||||
// observer chase another OBSERVER (each is someone's
|
||||
// nearest), so they scattered to three different corners
|
||||
// instead of gathering on the wounded mech.
|
||||
s_gx = 0.0f; s_gz = 0.0f; // (0,0) => the drive guard below skips
|
||||
gBTGotoActive = 0;
|
||||
gBTDrive.forced = 0;
|
||||
}
|
||||
else if (found) { s_gx = gxN; s_gz = gzN; }
|
||||
else { gBTGotoActive = 0; }
|
||||
}
|
||||
@@ -3633,7 +3653,15 @@ void
|
||||
gBTGotoActive = 0;
|
||||
gBTDrive.forced = 0;
|
||||
gBTDrive.forcedThrottle = 0.0f;
|
||||
DEBUG_STREAM << "[goto] ARRIVED -- released to manual control\n" << std::flush;
|
||||
// ALL-STOP on handover. Clearing `forced` alone leaves the
|
||||
// throttle LEVER wherever the beeline parked it (0.8), so the
|
||||
// mech walked away from the target the instant it was handed
|
||||
// over -- observers ended up 1000+ units past the mech they
|
||||
// were sent to watch. Zero the lever so it comes to rest and
|
||||
// waits for the pilot.
|
||||
gBTDrive.allStop = 1;
|
||||
gBTGotoReleased = 1; // stop the self-drive primer re-arming forced/0.8
|
||||
DEBUG_STREAM << "[goto] ARRIVED -- all-stop, released to manual control\n" << std::flush;
|
||||
}
|
||||
}
|
||||
turn = gBTGotoTurn; // non-real-controls fallback path
|
||||
@@ -6810,8 +6838,45 @@ void
|
||||
MoveCollisionVolume();
|
||||
if (isPlayer)
|
||||
gBlockCooldown = kBlockHysteresis; // (re)arm the in-contact window
|
||||
// DEFERRED: self TakeDamageMessage{0x64, zone=-1} (inert today) +
|
||||
// material/normal/approach caches + collisionState indicators.
|
||||
//
|
||||
// CRASH BOOKKEEPING + SELF-DAMAGE (binary @4aa89f-4aaab4; raw disasm
|
||||
// scratchpad/night6/gap_4a9770.txt -- the export gap). This block was
|
||||
// the long-standing "DEFERRED self TakeDamageMessage" note; the #82
|
||||
// wall-grind dig (2026-07-30) recovered it byte-by-byte:
|
||||
// @4aa8e2 fallDirection = the engine-filled dmg.damageForce rotated
|
||||
// world->local (FUN_0040879c = M^T v on the localToWorld
|
||||
// 3x3 rows; NOT normalized);
|
||||
// @4aa8f3 fallScalar = -(fallDirection . localVelocity.linearMotion)
|
||||
// (the [ebp-0x198] operand confirmed = mech+0x1c4 -- the
|
||||
// same vector iv2 squares below);
|
||||
// @4aa984-4aaab4 build TakeDamageMessage{0x64, zone=-1, the
|
||||
// collision Damage VERBATIM} and Dispatch it at OUR OWN
|
||||
// mech: a wall crash HURTS. This is what makes wall-
|
||||
// grinding self-limiting in the binary -- each stagger
|
||||
// costs armor, so the #82 knockdown storm degrades the
|
||||
// mech instead of looping forever while the peers'
|
||||
// replicants churn knockdown/trn ("skating").
|
||||
// (TODO payload nicety: throttleState = fall-surface material,
|
||||
// binary (**[mech+0x2f8])+0x24 -- feeds the type-5 reader's
|
||||
// surface-specific fall handling; needs the assistant-record map.)
|
||||
//
|
||||
{
|
||||
const Vector3D &ff = dmg.damageForce;
|
||||
fallDirection.x = ff.x * localToWorld(0,0) + ff.y * localToWorld(1,0)
|
||||
+ ff.z * localToWorld(2,0);
|
||||
fallDirection.y = ff.x * localToWorld(0,1) + ff.y * localToWorld(1,1)
|
||||
+ ff.z * localToWorld(2,1);
|
||||
fallDirection.z = ff.x * localToWorld(0,2) + ff.y * localToWorld(1,2)
|
||||
+ ff.z * localToWorld(2,2);
|
||||
fallScalar = -(fallDirection.x * impactVel.x
|
||||
+ fallDirection.y * impactVel.y
|
||||
+ fallDirection.z * impactVel.z);
|
||||
Entity::TakeDamageMessage td(
|
||||
Entity::TakeDamageMessageID,
|
||||
sizeof(Entity::TakeDamageMessage),
|
||||
GetEntityID(), -1 /* no aimed zone -> cylinder lottery */, dmg);
|
||||
Dispatch(&td);
|
||||
}
|
||||
Scalar iv2 = impactVel.x * impactVel.x + impactVel.y * impactVel.y
|
||||
+ impactVel.z * impactVel.z;
|
||||
if (iv2 > 40.0f) // crash-anim threshold @0x4ab184
|
||||
@@ -6824,12 +6889,18 @@ void
|
||||
// two action-request calls are the binary's FUN_004a4c54(this,1)
|
||||
// then (this,0x20) -- TWO calls, not one OR'd (fidelity verdict).
|
||||
// Guard 1: only when the bmp clip resolved (no substitute clip).
|
||||
// Guard 2 (BRING-UP, marked): don't REBIND while the crash clip is
|
||||
// already playing -- the bmp clip carries ~6.5 u/s of root motion
|
||||
// that presses back into the wall, and without the actionRequestFlags
|
||||
// consumers (bits 1/0x20 -- likely the drive suppressor during the
|
||||
// stagger, NOT yet reconstructed) the re-trigger restarts the clip
|
||||
// every frame. Remove this guard when the flag consumers land.
|
||||
// Guard 2: don't REBIND while the crash clip is already playing.
|
||||
// (2026-07-30 #82 dig CORRECTS the old rationale here: the
|
||||
// "actionRequestFlags consumers / drive suppressor" theory is
|
||||
// FALSIFIED -- an image-wide sweep + a full disasm of the export
|
||||
// gap show word[this+0x18] is read ONLY by the net record emitter.
|
||||
// The binary has NO rebind guard and needs none: the crash zeroes
|
||||
// worldLinearVelocity and the bmp state advances with cycleSpeed 0
|
||||
// (adv=0 measured), so iv2 collapses during the clip and re-binds
|
||||
// are naturally spaced. What actually bounds the stagger LOOP in
|
||||
// the binary is the crash SELF-DAMAGE above -- grinding costs
|
||||
// armor. This guard + the freshBlock hysteresis stay as cheap
|
||||
// belt-and-braces port additions.)
|
||||
if (animationClips[0x20] != 0
|
||||
&& legStateAlarm.GetLevel() != 0x20
|
||||
&& freshBlock) // only on a FRESH strike, not continuous grinding
|
||||
|
||||
@@ -25,6 +25,12 @@
|
||||
BT_ROOT=/c/git/bt411
|
||||
BT_EXE=$BT_ROOT/build/Release/btl4.exe
|
||||
|
||||
# PIDs THIS run launched. A blanket `taskkill /IM btl4.exe` at teardown kills
|
||||
# every game on the machine -- including a NEWER bench a previous wrapper's
|
||||
# sleep outlived. That silently shot down two live sessions, so each run now
|
||||
# tracks and kills only its own children.
|
||||
BT_PIDFILE=/tmp/bt_bench_pids.$$
|
||||
|
||||
# --- the exact env every shipped launcher sets -----------------------------
|
||||
bt_player_env () {
|
||||
export BT_PLATFORM=glass # unified build, desktop cockpit layer
|
||||
@@ -49,7 +55,13 @@ bt_launch () {
|
||||
local log="$1" egg="$2" aff="$3"; shift 3
|
||||
( bt_player_env
|
||||
export BT_LOG="$log" BT_AFFINITY="$aff"
|
||||
"$BT_EXE" -egg "$egg" "$@" & )
|
||||
"$BT_EXE" -egg "$egg" "$@" &
|
||||
# the winpid pseudo-file races process start -- poll briefly; a missed pid
|
||||
# here left a stale node holding the -net port and null-ran the next config
|
||||
for _i in 1 2 3 4 5; do
|
||||
[ -f /proc/$!/winpid ] && { cat /proc/$!/winpid >> "$BT_PIDFILE"; break; }
|
||||
sleep 0.3
|
||||
done )
|
||||
}
|
||||
|
||||
# --- an EXPERT copy of an egg (what players actually fly) ------------------
|
||||
@@ -68,3 +80,12 @@ bt_novice_egg () { # bt_novice_egg <src.egg> <dst.egg>
|
||||
sed 's/^experience=.*/experience=novice/' "$1" > "$2"
|
||||
echo "[bench] NOTE: novice egg $2 -- crits/heat/jams are GATED OFF (harness requirement)"
|
||||
}
|
||||
|
||||
# --- kill ONLY the nodes this run started --------------------------------
|
||||
bt_kill_ours () {
|
||||
[ -f "$BT_PIDFILE" ] || return 0
|
||||
while read -r pid; do
|
||||
[ -n "$pid" ] && taskkill //F //PID "$pid" > /dev/null 2>&1
|
||||
done < "$BT_PIDFILE"
|
||||
rm -f "$BT_PIDFILE"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
#!/usr/bin/env bash
|
||||
# #82 REPLICANT-LIMP A/B MATRIX: which factor breaks the peer's limp view --
|
||||
# TURNING (replicant trn-arming outprioritizes the gimp cycle at gimp speeds)
|
||||
# or TERRAIN (arena1 ground staggers -> type-5 knockdown broadcasts)?
|
||||
#
|
||||
# 4 configs x 2 nodes x ~3min, sequential:
|
||||
# g_straight grass, limper walks straight (the old mp_skate baseline)
|
||||
# g_circle grass, limper circles (run-6 shape)
|
||||
# a_straight arena1, limper walks straight
|
||||
# a_circle arena1, limper circles (run-13 shape = the user's repro)
|
||||
#
|
||||
# VERDICT per config, from logs alone (no eyes needed):
|
||||
# observer [animind] inst=1 states 22-27 -> replicant LIMPS (good)
|
||||
# observer trn(4)/knockdown(32) churn -> skating (bad)
|
||||
# observer type=5 rx count + limper [knock] rows -> the stagger factor
|
||||
set -x
|
||||
. /c/git/bt411/scratchpad/night6/bench_common.sh
|
||||
cd /c/git/bt411/content || exit 1
|
||||
|
||||
run_config () { # $1=tag $2=map $3=circle(0|1)
|
||||
local tag=$1 map=$2 circle=$3
|
||||
# headless rig only: a leftover node from the previous config holds the -net
|
||||
# port and null-runs this one -- sweep before every config
|
||||
taskkill //F //IM btl4.exe > /dev/null 2>&1
|
||||
sleep 2
|
||||
sed "s/^map=.*/map=$map/; s/^time=.*/time=day/; s/^experience=.*/experience=novice/" MP.EGG > AB.EGG
|
||||
rm -f ab_${tag}_obs.log ab_${tag}_limp.log
|
||||
# observer: static, replicant-diagnostics on
|
||||
BT_MP_LOG=1 BT_REPL_LOG=1 BT_AUDIO_SPATIAL=1 BT_SYNC_LOG=1 \
|
||||
bt_launch ab_${tag}_obs.log AB.EGG 0x0C -net 1601
|
||||
sleep 2
|
||||
# limper: symbolic leg gimp ~2s after alive; straight or circling
|
||||
if [ "$circle" = "1" ]; then
|
||||
BT_FORCE_TURN=0.45 \
|
||||
BT_MP_LOG=1 BT_DMG_LOG=1 BT_AUDIO_SPATIAL=1 BT_GROUND_LOG=1 \
|
||||
BT_SELF_DAMAGE=60 BT_SELF_DAMAGE_ZONE=leg BT_SELF_DAMAGE_TICKS=2 BT_SELF_DAMAGE_DELAY=2 \
|
||||
BT_AUTODRIVE=0.4 \
|
||||
bt_launch ab_${tag}_limp.log AB.EGG 0x03 -net 1501
|
||||
else
|
||||
BT_MP_LOG=1 BT_DMG_LOG=1 BT_AUDIO_SPATIAL=1 BT_GROUND_LOG=1 \
|
||||
BT_SELF_DAMAGE=60 BT_SELF_DAMAGE_ZONE=leg BT_SELF_DAMAGE_TICKS=2 BT_SELF_DAMAGE_DELAY=2 \
|
||||
BT_AUTODRIVE=0.4 \
|
||||
bt_launch ab_${tag}_limp.log AB.EGG 0x03 -net 1501
|
||||
fi
|
||||
sleep 4
|
||||
python ../tools/btconsole.py AB.EGG 127.0.0.1:1501 127.0.0.1:1601 &
|
||||
local PC=$!
|
||||
sleep 180
|
||||
kill $PC 2>/dev/null
|
||||
bt_kill_ours
|
||||
sleep 3
|
||||
}
|
||||
|
||||
run_config g_straight grass 0
|
||||
run_config g_circle grass 1
|
||||
run_config a_straight arena1 0
|
||||
run_config a_circle arena1 1
|
||||
rm -f AB.EGG
|
||||
|
||||
echo "=== A/B VERDICTS ==="
|
||||
for tag in g_straight g_circle a_straight a_circle; do
|
||||
obs=ab_${tag}_obs.log
|
||||
gimp=$(grep -a '\[animind\]' $obs 2>/dev/null | grep 'inst=1' | grep -c 'state=2[2-7] ')
|
||||
trn=$(grep -a '\[animind\]' $obs 2>/dev/null | grep 'inst=1' | grep -c 'state=4 ')
|
||||
kd=$(grep -a '\[animind\]' $obs 2>/dev/null | grep 'inst=1' | grep -c 'state=32 ')
|
||||
t5=$(grep -a '\[mrec-rx\]' $obs 2>/dev/null | grep -c 'type=5 ')
|
||||
knocks=$(grep -ac '\[knock\]' ab_${tag}_limp.log 2>/dev/null)
|
||||
gsim=$(grep -a '\[gimp-sim\]' ab_${tag}_limp.log 2>/dev/null | head -1)
|
||||
echo "VERDICT $tag: replicantGimp=$gimp trn=$trn knockdown=$kd type5rx=$t5 limperKnocks=$knocks ($gsim)"
|
||||
done
|
||||
echo "=== DONE ==="
|
||||
@@ -0,0 +1,69 @@
|
||||
#!/usr/bin/env bash
|
||||
# #82 REPLICANT-LIMP A/B MATRIX: which factor breaks the peer's limp view --
|
||||
# TURNING (replicant trn-arming outprioritizes the gimp cycle at gimp speeds)
|
||||
# or TERRAIN (arena1 ground staggers -> type-5 knockdown broadcasts)?
|
||||
#
|
||||
# 4 configs x 2 nodes x ~3min, sequential:
|
||||
# g_straight grass, limper walks straight (the old mp_skate baseline)
|
||||
# g_circle grass, limper circles (run-6 shape)
|
||||
# a_straight arena1, limper walks straight
|
||||
# a_circle arena1, limper circles (run-13 shape = the user's repro)
|
||||
#
|
||||
# VERDICT per config, from logs alone (no eyes needed):
|
||||
# observer [animind] inst=1 states 22-27 -> replicant LIMPS (good)
|
||||
# observer trn(4)/knockdown(32) churn -> skating (bad)
|
||||
# observer type=5 rx count + limper [knock] rows -> the stagger factor
|
||||
set -x
|
||||
. /c/git/bt411/scratchpad/night6/bench_common.sh
|
||||
cd /c/git/bt411/content || exit 1
|
||||
|
||||
run_config () { # $1=tag $2=map $3=circle(0|1)
|
||||
local tag=$1 map=$2 circle=$3
|
||||
# headless rig only: a leftover node from the previous config holds the -net
|
||||
# port and null-runs this one -- sweep before every config
|
||||
taskkill //F //IM btl4.exe > /dev/null 2>&1
|
||||
sleep 2
|
||||
sed "s/^map=.*/map=$map/; s/^time=.*/time=day/; s/^experience=.*/experience=novice/" MP.EGG > AB.EGG
|
||||
rm -f ab_${tag}_obs.log ab_${tag}_limp.log
|
||||
# observer: static, replicant-diagnostics on
|
||||
BT_MP_LOG=1 BT_REPL_LOG=1 BT_AUDIO_SPATIAL=1 BT_SYNC_LOG=1 \
|
||||
bt_launch ab_${tag}_obs.log AB.EGG 0x0C -net 1601
|
||||
sleep 2
|
||||
# limper: symbolic leg gimp ~2s after alive; straight or circling
|
||||
if [ "$circle" = "1" ]; then
|
||||
BT_FORCE_TURN=0.45 \
|
||||
BT_MP_LOG=1 BT_DMG_LOG=1 BT_AUDIO_SPATIAL=1 BT_GROUND_LOG=1 \
|
||||
BT_SELF_DAMAGE=60 BT_SELF_DAMAGE_ZONE=leg BT_SELF_DAMAGE_TICKS=2 BT_SELF_DAMAGE_DELAY=2 \
|
||||
BT_AUTODRIVE=0.4 \
|
||||
bt_launch ab_${tag}_limp.log AB.EGG 0x03 -net 1501
|
||||
else
|
||||
BT_MP_LOG=1 BT_DMG_LOG=1 BT_AUDIO_SPATIAL=1 BT_GROUND_LOG=1 \
|
||||
BT_SELF_DAMAGE=60 BT_SELF_DAMAGE_ZONE=leg BT_SELF_DAMAGE_TICKS=2 BT_SELF_DAMAGE_DELAY=2 \
|
||||
BT_AUTODRIVE=0.4 \
|
||||
bt_launch ab_${tag}_limp.log AB.EGG 0x03 -net 1501
|
||||
fi
|
||||
sleep 4
|
||||
python ../tools/btconsole.py AB.EGG 127.0.0.1:1501 127.0.0.1:1601 &
|
||||
local PC=$!
|
||||
sleep 180
|
||||
kill $PC 2>/dev/null
|
||||
bt_kill_ours
|
||||
sleep 3
|
||||
}
|
||||
|
||||
run_config a_circle arena1 1
|
||||
run_config g_circle grass 1
|
||||
rm -f AB.EGG
|
||||
|
||||
echo "=== A/B VERDICTS ==="
|
||||
for tag in a_circle g_circle; do
|
||||
obs=ab_${tag}_obs.log
|
||||
gimp=$(grep -a '\[animind\]' $obs 2>/dev/null | grep 'inst=1' | grep -c 'state=2[2-7] ')
|
||||
trn=$(grep -a '\[animind\]' $obs 2>/dev/null | grep 'inst=1' | grep -c 'state=4 ')
|
||||
kd=$(grep -a '\[animind\]' $obs 2>/dev/null | grep 'inst=1' | grep -c 'state=32 ')
|
||||
t5=$(grep -a '\[mrec-rx\]' $obs 2>/dev/null | grep -c 'type=5 ')
|
||||
knocks=$(grep -ac '\[knock\]' ab_${tag}_limp.log 2>/dev/null)
|
||||
gsim=$(grep -a '\[gimp-sim\]' ab_${tag}_limp.log 2>/dev/null | head -1)
|
||||
echo "VERDICT $tag: replicantGimp=$gimp trn=$trn knockdown=$kd type5rx=$t5 limperKnocks=$knocks ($gsim)"
|
||||
done
|
||||
echo "=== DONE ==="
|
||||
@@ -0,0 +1,69 @@
|
||||
#!/usr/bin/env bash
|
||||
# #82 REPLICANT-LIMP A/B MATRIX: which factor breaks the peer's limp view --
|
||||
# TURNING (replicant trn-arming outprioritizes the gimp cycle at gimp speeds)
|
||||
# or TERRAIN (arena1 ground staggers -> type-5 knockdown broadcasts)?
|
||||
#
|
||||
# 4 configs x 2 nodes x ~3min, sequential:
|
||||
# g_straight grass, limper walks straight (the old mp_skate baseline)
|
||||
# g_circle grass, limper circles (run-6 shape)
|
||||
# a_straight arena1, limper walks straight
|
||||
# a_circle arena1, limper circles (run-13 shape = the user's repro)
|
||||
#
|
||||
# VERDICT per config, from logs alone (no eyes needed):
|
||||
# observer [animind] inst=1 states 22-27 -> replicant LIMPS (good)
|
||||
# observer trn(4)/knockdown(32) churn -> skating (bad)
|
||||
# observer type=5 rx count + limper [knock] rows -> the stagger factor
|
||||
set -x
|
||||
. /c/git/bt411/scratchpad/night6/bench_common.sh
|
||||
cd /c/git/bt411/content || exit 1
|
||||
|
||||
run_config () { # $1=tag $2=map $3=circle(0|1)
|
||||
local tag=$1 map=$2 circle=$3
|
||||
# headless rig only: a leftover node from the previous config holds the -net
|
||||
# port and null-runs this one -- sweep before every config
|
||||
taskkill //F //IM btl4.exe > /dev/null 2>&1
|
||||
sleep 2
|
||||
sed "s/^map=.*/map=$map/; s/^time=.*/time=day/; s/^experience=.*/experience=novice/" MP.EGG > AB.EGG
|
||||
rm -f ab_${tag}_obs.log ab_${tag}_limp.log
|
||||
# observer: static, replicant-diagnostics on
|
||||
BT_MP_LOG=1 BT_REPL_LOG=1 BT_AUDIO_SPATIAL=1 BT_SYNC_LOG=1 \
|
||||
bt_launch ab_${tag}_obs.log AB.EGG 0x0C -net 1601
|
||||
sleep 2
|
||||
# limper: symbolic leg gimp ~2s after alive; straight or circling
|
||||
if [ "$circle" = "1" ]; then
|
||||
BT_FORCE_TURN=0.45 \
|
||||
BT_MP_LOG=1 BT_DMG_LOG=1 BT_AUDIO_SPATIAL=1 BT_GROUND_LOG=1 \
|
||||
BT_SELF_DAMAGE=60 BT_SELF_DAMAGE_ZONE=leg BT_SELF_DAMAGE_TICKS=2 BT_SELF_DAMAGE_DELAY=2 \
|
||||
BT_AUTODRIVE=0.4 \
|
||||
bt_launch ab_${tag}_limp.log AB.EGG 0x03 -net 1501
|
||||
else
|
||||
BT_MP_LOG=1 BT_DMG_LOG=1 BT_AUDIO_SPATIAL=1 BT_GROUND_LOG=1 \
|
||||
BT_SELF_DAMAGE=60 BT_SELF_DAMAGE_ZONE=leg BT_SELF_DAMAGE_TICKS=2 BT_SELF_DAMAGE_DELAY=2 \
|
||||
BT_AUTODRIVE=0.4 \
|
||||
bt_launch ab_${tag}_limp.log AB.EGG 0x03 -net 1501
|
||||
fi
|
||||
sleep 4
|
||||
python ../tools/btconsole.py AB.EGG 127.0.0.1:1501 127.0.0.1:1601 &
|
||||
local PC=$!
|
||||
sleep 180
|
||||
kill $PC 2>/dev/null
|
||||
bt_kill_ours
|
||||
sleep 3
|
||||
}
|
||||
|
||||
|
||||
run_config g_circle grass 1
|
||||
rm -f AB.EGG
|
||||
|
||||
echo "=== A/B VERDICTS ==="
|
||||
for tag in g_circle; do
|
||||
obs=ab_${tag}_obs.log
|
||||
gimp=$(grep -a '\[animind\]' $obs 2>/dev/null | grep 'inst=1' | grep -c 'state=2[2-7] ')
|
||||
trn=$(grep -a '\[animind\]' $obs 2>/dev/null | grep 'inst=1' | grep -c 'state=4 ')
|
||||
kd=$(grep -a '\[animind\]' $obs 2>/dev/null | grep 'inst=1' | grep -c 'state=32 ')
|
||||
t5=$(grep -a '\[mrec-rx\]' $obs 2>/dev/null | grep -c 'type=5 ')
|
||||
knocks=$(grep -ac '\[knock\]' ab_${tag}_limp.log 2>/dev/null)
|
||||
gsim=$(grep -a '\[gimp-sim\]' ab_${tag}_limp.log 2>/dev/null | head -1)
|
||||
echo "VERDICT $tag: replicantGimp=$gimp trn=$trn knockdown=$kd type5rx=$t5 limperKnocks=$knocks ($gsim)"
|
||||
done
|
||||
echo "=== DONE ==="
|
||||
Reference in New Issue
Block a user