j134 fix: tracker must take the mover's switch per-call (instance flag was silently off on the object-literal construction path) + apply the correction on the reading that carries the server's turn-N+1 energy change (two-slot buffer); live-verified alignment
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
## Unit checks for the j133 STRAFE fire-detection fix (`TR_STRAFE_FIRE_FIX`).
|
||||
## Unit checks for the j133/j134 fire-detection fix (`TR_STRAFE_FIRE_FIX`).
|
||||
##
|
||||
## NO battle, NO Java, NO server. Run with:
|
||||
## nim c -r --nimcache:/tmp/nc_j133 --path:common_libs \
|
||||
@@ -17,6 +17,16 @@
|
||||
## The fix undoes both from the events (`noteEnemyBulletHit` / `noteDamageDealt`)
|
||||
## and splits any still-too-large delta instead of dropping it. These checks pin
|
||||
## the four behaviours plus the OFF-switch parity with the shipped detector.
|
||||
##
|
||||
## ── The one-SCAN delay (j134, MEASURED LIVE) ────────────────────────────────
|
||||
## The correction is NOT applied to the immediately-next reading. The server
|
||||
## emits the hit event on turn N but applies the energy change to turn N+1's
|
||||
## reading (live evidence: `EV hit getTurn=67` -> the `raw=-5.803` gain appears
|
||||
## in the `getTurn=68` reading). Because `note*` runs during `go()`, AFTER that
|
||||
## turn's `endScan`, the tracker keeps a one-slot double buffer so the
|
||||
## correction lands on the reading that carries the change. A unit test must
|
||||
## therefore feed: (1) a scan to rotate the event in, then (2) the reading whose
|
||||
## raw shows the effect. `advance` below does exactly that.
|
||||
|
||||
import std/[math, os]
|
||||
import gun_harness/gun_interface
|
||||
@@ -44,13 +54,10 @@ proc freshModule(): StrafeModule =
|
||||
|
||||
# ── 1. plain fire (unchanged behaviour) ───────────────────────────────────────
|
||||
block:
|
||||
let m = freshModule()
|
||||
var mm = m
|
||||
var mm = freshModule()
|
||||
mm.detectFires(ws(100.0))
|
||||
mm.detectFires(ws(97.95)) # fired 2.05? use a clean power
|
||||
# second reading: 100 -> 97.95 is a 2.05 drop, in range -> one wave
|
||||
check "plain fire creates exactly one wave",
|
||||
mm.bullets.len == 1
|
||||
mm.detectFires(ws(97.95)) # 2.05 drop, in range -> one wave
|
||||
check "plain fire creates exactly one wave", mm.bullets.len == 1
|
||||
check "plain fire wave power == drop",
|
||||
mm.bullets.len == 1 and abs(mm.bullets[0].power - 2.05) < 1e-9
|
||||
|
||||
@@ -58,12 +65,12 @@ block:
|
||||
block:
|
||||
# Enemy at 100 fires 0.35 while an EARLIER 0.45 bullet of its own hits us
|
||||
# (+3*0.45 = +1.35). Net energy = 100 - 0.35 + 1.35 = 101.0 -> raw drop = -1.0.
|
||||
let m = freshModule()
|
||||
var mm = m
|
||||
mm.noteEnemyBulletHit(0.45)
|
||||
mm.detectFires(ws(101.0))
|
||||
check "masked fire: WITH fix a wave is created",
|
||||
mm.bullets.len == 1
|
||||
var mm = freshModule()
|
||||
mm.detectFires(ws(100.0)) # establish prev = 100
|
||||
mm.noteEnemyBulletHit(0.45) # event noted (staged)
|
||||
mm.detectFires(ws(100.0)) # rotates the staged correction in
|
||||
mm.detectFires(ws(101.0)) # the reading carrying the change
|
||||
check "masked fire: WITH fix a wave is created", mm.bullets.len == 1
|
||||
check "masked fire: WITH fix the power is recovered",
|
||||
mm.bullets.len == 1 and abs(mm.bullets[0].power - 0.35) < 1e-9
|
||||
|
||||
@@ -71,24 +78,21 @@ block:
|
||||
block:
|
||||
# Enemy at 50 fires 2.0 and our bullet hits it for 1.8 -> energy 46.2,
|
||||
# raw drop 3.8 > 3.01 (shipped detector rejects it outright).
|
||||
let m = freshModule()
|
||||
var mm = m
|
||||
mm.prevEnergySet(1, 50.0) # last reading before the contested tick
|
||||
var mm = freshModule()
|
||||
mm.prevEnergySet(1, 50.0)
|
||||
mm.noteDamageDealt(1.8)
|
||||
mm.detectFires(ws(46.2))
|
||||
check "inflated drop: WITH fix a wave is created",
|
||||
mm.bullets.len == 1
|
||||
mm.detectFires(ws(50.0)) # rotates the staged correction in
|
||||
mm.detectFires(ws(46.2)) # raw 3.8 - 1.8 = 2.0 -> wave
|
||||
check "inflated drop: WITH fix a wave is created", mm.bullets.len == 1
|
||||
check "inflated drop: WITH fix the power is recovered",
|
||||
mm.bullets.len == 1 and abs(mm.bullets[0].power - 2.0) < 1e-9
|
||||
|
||||
# ── 4. never silently drop a too-large delta (unobserved contamination) ───────
|
||||
block:
|
||||
let m = freshModule()
|
||||
var mm = m
|
||||
var mm = freshModule()
|
||||
mm.detectFires(ws(100.0))
|
||||
mm.detectFires(ws(93.0)) # 7.0 with no events -> split, not dropped
|
||||
check "too-large drop is SPLIT, never dropped",
|
||||
mm.bullets.len == 3
|
||||
check "too-large drop is SPLIT, never dropped", mm.bullets.len == 3
|
||||
check "split waves all within the power cap",
|
||||
mm.bullets.len == 3 and mm.bullets[0].power <= 3.0 and mm.bullets[1].power <= 3.0
|
||||
check "split waves sum back to the drop",
|
||||
@@ -98,15 +102,18 @@ block:
|
||||
block:
|
||||
putEnv("TR_STRAFE_FIRE_FIX", "0")
|
||||
loadStrafeEnv()
|
||||
# (a) masked fire: shipped detector sees drop = -1.0 -> NO wave.
|
||||
# (a) masked fire: shipped detector sees raw = -1.0 -> NO wave.
|
||||
var a = freshModule()
|
||||
a.detectFires(ws(100.0))
|
||||
a.noteEnemyBulletHit(0.45) # must be a no-op when the fix is off
|
||||
a.detectFires(ws(100.0))
|
||||
a.detectFires(ws(101.0))
|
||||
check "OFF: masked fire is NOT detected (shipped behaviour)", a.bullets.len == 0
|
||||
# (b) inflated drop: shipped detector sees 3.8 -> rejected -> NO wave.
|
||||
var b = freshModule()
|
||||
b.prevEnergySet(1, 50.0)
|
||||
b.noteDamageDealt(1.8)
|
||||
b.detectFires(ws(50.0))
|
||||
b.detectFires(ws(46.2))
|
||||
check "OFF: inflated drop is rejected (shipped behaviour)", b.bullets.len == 0
|
||||
# (c) in-range drop is still caught exactly once.
|
||||
@@ -121,9 +128,18 @@ block:
|
||||
# ── 6. default is ON ─────────────────────────────────────────────────────────
|
||||
block:
|
||||
delEnv("TR_STRAFE_FIRE_FIX")
|
||||
delEnv("TR_FIRE_FIX")
|
||||
loadStrafeEnv()
|
||||
check "default TR_STRAFE_FIRE_FIX is ON", StrafeFireFix
|
||||
|
||||
# ── 7. the global TR_FIRE_FIX also gates STRAFE ──────────────────────────────
|
||||
block:
|
||||
putEnv("TR_FIRE_FIX", "0")
|
||||
loadStrafeEnv()
|
||||
check "TR_FIRE_FIX=0 disables STRAFE too", not StrafeFireFix
|
||||
delEnv("TR_FIRE_FIX")
|
||||
loadStrafeEnv()
|
||||
|
||||
if failures == 0:
|
||||
echo "\nAll STRAFE fire-detection fix checks passed."
|
||||
else:
|
||||
|
||||
Reference in New Issue
Block a user