mirror of
https://github.com/horizon-rust/horizon.git
synced 2026-11-07 11:06:11 -10:00
Preserve inherited squid damage acceptance result
Co-Authored-By: Codex <[email protected]>
This commit is contained in:
1 parent
2266c4de14
commit
cd7d6a3dcc
4 files changed
+370
-3
No files matched your search
@@ -3547,7 +3547,10 @@ impl Mob {
|
||||
if self.species() == Species::GlowSquid && self.ai.last_hurt_by.is_some() {
|
||||
aquatic::hurt_server(self);
|
||||
}
|
||||
true
|
||||
// Squid.hurtServer (also GlowSquid's result): parent health damage has
|
||||
// happened, but without a last living attacker the subclass returns false.
|
||||
!matches!(self.species(), Species::Squid | Species::GlowSquid)
|
||||
|| self.ai.last_hurt_by.is_some()
|
||||
}
|
||||
/// `damage` by a source whose direct entity is `direct` (an entity type id), which `source`
|
||||
/// cannot carry: `DamageSources.indirectMagic(potion or cloud, owner)`.
|
||||
@@ -5502,6 +5505,70 @@ mod effect_refusal_tests {
|
||||
);
|
||||
}
|
||||
|
||||
fn check_squid_damage_result(id: &str) {
|
||||
let root = std::path::PathBuf::from(std::env::var_os("HORIZON_TEST_RESOURCES").unwrap());
|
||||
let types = MobTypes::load_for_server(&root).unwrap();
|
||||
let mut attacker = mob(&types, "minecraft:cow");
|
||||
attacker.id = 2;
|
||||
attacker.position = Vec3::new(4.0, 64.0, 4.0);
|
||||
assert!(attacker.alive());
|
||||
let by_cow = Damage {
|
||||
kind: DamageKind::Generic,
|
||||
..Damage::mob(attacker.id, [4.0, 64.0, 4.0]).with_species(Species::Cow)
|
||||
};
|
||||
// An unrelated parent's generic acceptance remains true.
|
||||
let mut cow = mob(&types, "minecraft:cow");
|
||||
assert!(cow.damage(1.0, Damage::of(DamageKind::Generic)));
|
||||
assert_eq!(cow.health, 9.0);
|
||||
// A parent's existing rejection never reaches the subclass aftermath.
|
||||
let mut protected = mob(&types, id);
|
||||
protected.invulnerable_flag = true;
|
||||
assert!(!protected.damage(1.0, by_cow));
|
||||
assert_eq!(protected.health, 10.0);
|
||||
if protected.species() == Species::GlowSquid {
|
||||
assert_eq!(aquatic::dark_ticks(&protected), 0);
|
||||
}
|
||||
for no_ai in [false, true] {
|
||||
for attributed in [true, false] {
|
||||
let mut victim = mob(&types, id);
|
||||
victim.no_ai = no_ai;
|
||||
assert_eq!(victim.health, 10.0);
|
||||
assert!(victim.ai.last_hurt_by.is_none());
|
||||
let source = if attributed {
|
||||
by_cow
|
||||
} else {
|
||||
Damage::of(DamageKind::Generic)
|
||||
};
|
||||
let accepted = victim.damage(1.0, source);
|
||||
// Check the parent's state first: false must not roll back damage.
|
||||
assert_eq!(victim.health, 9.0);
|
||||
assert_eq!(
|
||||
victim.ai.last_hurt_by,
|
||||
attributed.then_some(Who::Mob(attacker.id))
|
||||
);
|
||||
if victim.species() == Species::GlowSquid {
|
||||
assert_eq!(
|
||||
aquatic::dark_ticks(&victim),
|
||||
if attributed { 100 } else { 0 }
|
||||
);
|
||||
}
|
||||
assert_eq!(accepted, attributed, "{id} NoAI={no_ai}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore = "needs prepared entity types in HORIZON_TEST_RESOURCES"]
|
||||
fn glow_squid_damage_result_keeps_parent_health_side_effect() {
|
||||
check_squid_damage_result("minecraft:glow_squid");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore = "needs prepared entity types in HORIZON_TEST_RESOURCES"]
|
||||
fn squid_damage_result_keeps_parent_health_side_effect() {
|
||||
check_squid_damage_result("minecraft:squid");
|
||||
}
|
||||
|
||||
/// Squid.hurtServer reports a hit only when a living entity dealt it, so
|
||||
/// environmental damage leaves a glow squid glowing; a mob's blow dims it.
|
||||
#[test]
|
||||
@@ -5515,11 +5582,13 @@ mod effect_refusal_tests {
|
||||
let types = MobTypes::load_for_server(&root).unwrap();
|
||||
let mut burned = mob(&types, "minecraft:glow_squid");
|
||||
burned.health = 10.0;
|
||||
assert!(burned.damage(1.0, Damage::of(DamageKind::Campfire)));
|
||||
assert!(!burned.damage(1.0, Damage::of(DamageKind::Campfire)));
|
||||
assert_eq!(burned.health, 9.0);
|
||||
assert_eq!(aquatic::dark_ticks(&burned), 0);
|
||||
let mut struck = mob(&types, "minecraft:glow_squid");
|
||||
struck.health = 10.0;
|
||||
assert!(struck.damage(1.0, Damage::mob(2, [0.0, 64.0, 0.0])));
|
||||
assert_eq!(struck.health, 9.0);
|
||||
assert_eq!(aquatic::dark_ticks(&struck), 100);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
# Squid's inherited damage acceptance result
|
||||
|
||||
Pinned Java26.3 `Squid.hurtServer` first calls its parent, then reports true only
|
||||
with a non-null last living attacker. `GlowSquid.hurtServer` returns that same
|
||||
result and sets dark ticks100 only when true. A false subclass result does not
|
||||
undo the parent's health loss.
|
||||
|
||||
Current main already has the correct ink/dark guards, from #8864 (closed #8724).
|
||||
They remain untouched. The sole gameplay change replaces the shared adapter's
|
||||
final unconditional true with the inherited squid/glow acceptance condition.
|
||||
Other species retain true after their accepted shared parent path. The old guard
|
||||
test's erroneous environment-hit true assertion is corrected, adding health-loss
|
||||
checks without changing that guard's implementation.
|
||||
|
||||
No parent-health/identity refactor, H176 timer, stale-attacker lifecycle, ink RNG,
|
||||
particles, sound, packet, rendering or other-species behavior repair/claim.
|
||||
|
||||
## Declared actual Game/Java program
|
||||
|
||||
```
|
||||
python3 tools/test_squid_damage_result_parity.py --binary /path/to/game --work-dir /scratch/run
|
||||
python3 tools/test_squid_damage_result_parity.py --full-diagnostic --binary /path/to/game --work-dir /scratch/full
|
||||
```
|
||||
|
||||
`--reference /scratch/old/vanilla.jsonl` requires byte-identical fresh Java bytes.
|
||||
The existing command harness runs actual authoritative Game with its connected
|
||||
owner and independently runs pinned Java in isolated fresh data. No new observer.
|
||||
|
||||
Six fresh, unique-UUID victims: GlowSquid NoAI0/1 crossed with generic/cow-attributed
|
||||
hits; ordinary Squid NoAI1 is the independently Java-measured inherited control.
|
||||
Each starts Health10 and receives one generic1 hit. A real healthy NoAI cow causes
|
||||
the attributed hits. Actor UUID/type/Health/Pos/NoAI/NoGravity/Silent/Persistence,
|
||||
loaded water/floor/glass geometry, owner identity/position and actual relative
|
||||
clock0 guard admission. No world steps, reused victim, stale attacker or cooldown
|
||||
history; the cow remains independently alive/unchanged after all actions.
|
||||
|
||||
Each hit has fresh result/success sentinels set to-99. Fresh Java FIRST measured
|
||||
failure feedback/callback/stores: this program invokes false/0 and stores0 for
|
||||
unattributed hits, not an assumed missing callback. Health becomes9 and glow dark
|
||||
remains0. Cow-attributed hits give callback true/1 and both stores1, Health9 and
|
||||
glow dark100. Every damage message/style/callback/store remains strictly compared.
|
||||
|
||||
Predeclared before native captures: **193** complete raw records. Only full bodies,
|
||||
Motion vectors and absolute clock-start/end are diagnostic; all commands remain
|
||||
traced. `--full-diagnostic` strictly compares all **221** records. No masks,
|
||||
tolerances, feedback normalization or selection changes after capture. Complete
|
||||
Motion records are retained without an RNG/physics parity claim.
|
||||
|
||||
Frozen source: `2266c4de14561146044c422e6c525b3ce52b0f79`.
|
||||
|
||||
| Program | Exact script SHA256 |
|
||||
|---|---|
|
||||
| Scoped193 | `25eec1729a070935d3b4bf075978bd1a44399df11a86e770a6387a8d862e4a7b` |
|
||||
| Full221 | `548fe826c429f551e55012a16bdff078ffca9e5147e4c7e58b009674028c3dc1` |
|
||||
|
||||
Old current Game: **184/193**. Health9 and dark0 already match; historical dark100
|
||||
failures are not recycled as this baseline. Exactly these three suffixes differ
|
||||
for EACH of glow_ai0_generic, glow_ai1_generic and squid_generic (nine records):
|
||||
|
||||
| Suffix | Java expected | Old native actual |
|
||||
|---|---|---|
|
||||
| -hit | Invulnerable failure, callback false/0 | Applied1 damage success, true/1 |
|
||||
| -result | 0 | 1 |
|
||||
| -success | 0 | 1 |
|
||||
|
||||
Every attributed hit, actual health/dark/type/identity/geometry/clock control
|
||||
passes old. Full old diagnostic: **196/221**, including full bodies and Motion.
|
||||
|
||||
## Reachable regressions and retained evidence
|
||||
|
||||
`glow_squid_damage_result_keeps_parent_health_side_effect` and its Squid twin
|
||||
call actual `Mob.damage` on real prepared entity definitions. Both fail old at
|
||||
returned true versus false AFTER asserting Health9, last attacker and dark0.
|
||||
Both pass fixed. Fresh victims cover both NoAI modes, with prior positive cow-hit,
|
||||
other-species Cow generic acceptance and parent-invulnerability controls. No
|
||||
copied health algorithm, synthetic return value or clock setter. The existing
|
||||
resource-backed glow guard test also passes with its corrected return expectation.
|
||||
|
||||
Programs, byte-identical scoped references, raw styled feedback/callbacks, full
|
||||
bodies/Motion, transcripts, source/checkpoint hashes, pinned bytecode and immutable
|
||||
old/fixed executables are retained under the owner's `.local/h177`. No game files
|
||||
are published. Available file timestamps and logged stage durations are retained;
|
||||
unobserved event stage times remain unknown.
|
||||
|
||||
## Fixed result and complete diagnostic residuals
|
||||
|
||||
Final actual Game: **193/193**, versus **184/193** old, with byte-identical scoped
|
||||
script and fresh Java reference bytes. Both resource-backed Mob.damage tests and
|
||||
the corrected existing dark-guard test pass. Full221: **196/221 → 205/221**. No
|
||||
full221, whole-body, RNG/ink or raw-clock parity claim.
|
||||
|
||||
The full program is byte-identical across builds. Its fresh Java references differ
|
||||
only at absolute clocks; non-clock records are identical. All Motion records in
|
||||
this fixture match, but do not establish a general movement/RNG contract. All16
|
||||
remaining full-record differences are listed below. In addition to extra default
|
||||
attributes, the three attributed after-bodies omit Java's saved attacker fields.
|
||||
This saved-attacker projection remains an unrelated diagnostic; runtime attributed
|
||||
acceptance/dark100 are strictly covered. Complete raw bodies/styles/callbacks
|
||||
remain mismatches in the full diagnostic. No normalization changes the counts.
|
||||
|
||||
| Exact label | Java expected | Fixed native actual |
|
||||
|---|---|---|
|
||||
| `clock-start` | `305 (old fresh reference: 265)` | `0` |
|
||||
| `before-cow-body` | `attributes: []` | `attributes: [{id: "minecraft:movement_speed", base: 0.20000000298023224d}]` |
|
||||
| `glow_ai0_cow-before-body` | `attributes: []` | `attributes: [{id: "minecraft:movement_speed", base: 0.7d}]` |
|
||||
| `glow_ai0_cow-after-body` | `attributes: []; last_hurt_by_mob: [I; 177, 0, 0, 100]; ticks_since_last_hurt_by_mob: 0` | `attributes: [{id: "minecraft:movement_speed", base: 0.7d}]; both saved attacker fields absent` |
|
||||
| `glow_ai0_generic-before-body` | `attributes: []` | `attributes: [{id: "minecraft:movement_speed", base: 0.7d}]` |
|
||||
| `glow_ai0_generic-after-body` | `attributes: []` | `attributes: [{id: "minecraft:movement_speed", base: 0.7d}]` |
|
||||
| `glow_ai1_cow-before-body` | `attributes: []` | `attributes: [{id: "minecraft:movement_speed", base: 0.7d}]` |
|
||||
| `glow_ai1_cow-after-body` | `attributes: []; last_hurt_by_mob: [I; 177, 0, 0, 100]; ticks_since_last_hurt_by_mob: 0` | `attributes: [{id: "minecraft:movement_speed", base: 0.7d}]; both saved attacker fields absent` |
|
||||
| `glow_ai1_generic-before-body` | `attributes: []` | `attributes: [{id: "minecraft:movement_speed", base: 0.7d}]` |
|
||||
| `glow_ai1_generic-after-body` | `attributes: []` | `attributes: [{id: "minecraft:movement_speed", base: 0.7d}]` |
|
||||
| `squid_cow-before-body` | `attributes: []` | `attributes: [{id: "minecraft:movement_speed", base: 0.7d}]` |
|
||||
| `squid_cow-after-body` | `attributes: []; last_hurt_by_mob: [I; 177, 0, 0, 100]; ticks_since_last_hurt_by_mob: 0` | `attributes: [{id: "minecraft:movement_speed", base: 0.7d}]; both saved attacker fields absent` |
|
||||
| `squid_generic-before-body` | `attributes: []` | `attributes: [{id: "minecraft:movement_speed", base: 0.7d}]` |
|
||||
| `squid_generic-after-body` | `attributes: []` | `attributes: [{id: "minecraft:movement_speed", base: 0.7d}]` |
|
||||
| `after-cow-body` | `attributes: []` | `attributes: [{id: "minecraft:movement_speed", base: 0.20000000298023224d}]` |
|
||||
| `clock-end` | `305 (old fresh reference: 265)` | `0` |
|
||||
|
||||
Scoped reference SHA256: `6b3a5398408e2bcdac6d6d949fb5bed88e8cb6d25d96fc97b913321bfcb492ef`.
|
||||
Full fixed fresh reference SHA256: `2856764df3a97dbf707dc6ad5417a205f34b53c3a473ce05062f0e42b9a04f5e`.
|
||||
|
||||
## Local readiness and source checkpoint
|
||||
|
||||
Final production/test patch: rustfmt, clippy with warnings denied, doc tests and
|
||||
public-tree checks pass. `pr_ready.py --quick --base 2266c4de14561146044c422e6c525b3ce52b0f79`
|
||||
runs 1887 passing Rust tests, one failure, 64 ignored. The failure is the existing
|
||||
`drops_match_vanillas_motion_bit_for_bit` Z last-bit difference at yaw37.5,
|
||||
pitch-21.25: actual4597448471913462162 versus expected4597448471913462163.
|
||||
The own archived OLD executable reproduces exactly the same failure. No drop
|
||||
physics edit. Both new ignored resource-backed tests were explicitly run and pass,
|
||||
as does the corrected existing guard test.
|
||||
|
||||
Python: 696 tests, four failures and one skip. One is the installer signature
|
||||
fixture offering x86_64 assets on an ARM64 host; three are optional-release
|
||||
archive combinations hitting macOS Bash3.2 unset arrays. Narrow installer rerun
|
||||
confirms its prerequisite. No installer/release repair. Broad parity is skipped
|
||||
by `--quick`; the explicit actual Game/Java pairs above were run independently.
|
||||
The readiness command exits nonzero for these unrelated failures; it is not green.
|
||||
|
||||
Latest actual main pull/checkpoint: `42e6d9cc498998b7bd9d5648360f72531ae0fd8e`.
|
||||
The relevant Mob/species/damage-command and command-harness/Java-oracle files have
|
||||
no delta from the frozen proof base. No rebase, main merge or combined-latest
|
||||
runtime claim. Evidence-only documentation was completed after the required run;
|
||||
whitespace/public-tree checks are repeated on the final staged files.
|
||||
@@ -1,6 +1,7 @@
|
||||
Squid.hurtServer inline crates/horizon-client/src/mobs.rs::Mob::damage_with_player_context after shared parent damage, require last living attacker for true; retain health loss and existing ink guard
|
||||
GlowSquid.aiStep port crates/horizon-client/src/mobs/aquatic.rs::glow_squid_ai_step
|
||||
GlowSquid.checkGlowSquidSpawnRules port crates/horizon-client/src/mobs/spawn_rules.rs::check_spawn_rules S::GlowSquid arm: 33 blocks below sea level, dark, in water
|
||||
GlowSquid.hurtServer port crates/horizon-client/src/mobs/aquatic.rs::hurt_server
|
||||
GlowSquid.hurtServer inline crates/horizon-client/src/mobs.rs::Mob::damage_with_player_context inherited Squid acceptance is returned after parent health effects; existing dim guard calls aquatic::hurt_server
|
||||
WolfSoundVariants skip:data registry bootstrap of the wolf sound sets; sounds are in species.rs and finalize.rs
|
||||
AbstractDragonPhaseInstance.getTurnSpeed inline crates/horizon-client/src/game/dragon.rs::Game::tick_dragon 0.7 / dist / rot turn for every phase but Landing; only applied while a fly target is set
|
||||
DragonChargePlayerPhase.doServerTick inline crates/horizon-client/src/game/dragon.rs::Game::tick_phase Charging arm: 10-tick countdown once close; gap: minor: no collision re-target
|
||||
|
||||
|
@@ -0,0 +1,152 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Fresh Squid/GlowSquid damage results through actual Game and pinned Java.
|
||||
|
||||
No world steps. Full bodies, Motion and absolute clocks remain executable raw
|
||||
traces; --full-diagnostic strictly compares those too. No RNG/ink/timer claim.
|
||||
"""
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
import test_command_exec_parity as driver
|
||||
|
||||
COW = '000000b1-0000-0000-0000-000000000064'
|
||||
CASES = [('glow_ai0_cow', 'glow_squid', 0, True),
|
||||
('glow_ai0_generic', 'glow_squid', 0, False),
|
||||
('glow_ai1_cow', 'glow_squid', 1, True),
|
||||
('glow_ai1_generic', 'glow_squid', 1, False),
|
||||
('squid_cow', 'squid', 1, True), ('squid_generic', 'squid', 1, False)]
|
||||
|
||||
|
||||
def program(full=False):
|
||||
ops = []
|
||||
|
||||
def cmd(text, label, result=None, callback=None, admission=True, diagnostic=False):
|
||||
op = {'op': 'cmd', 'cmd': text, 'label': label, 'admission': admission}
|
||||
if result is not None:
|
||||
op['expect_result'] = result
|
||||
if callback is not None:
|
||||
op['expect_callback'] = callback
|
||||
if diagnostic and not full:
|
||||
op.update(record=False, trace=True)
|
||||
ops.append(op)
|
||||
|
||||
cmd('/tick freeze', 'freeze')
|
||||
cmd('/gamerule spawn_mobs false', 'no-spawn')
|
||||
cmd('/gamerule advance_time false', 'no-daylight')
|
||||
cmd('/forceload add 0 0', 'force', 1)
|
||||
ops.append({'op': 'settle', 'required_chunks': [[0, 0]]})
|
||||
cmd('/fill 0 199 0 15 199 15 stone', 'floor', 256)
|
||||
cmd('/fill 1 200 1 14 204 14 glass', 'walls-roof', 980)
|
||||
cmd('/fill 2 200 2 13 203 13 water[level=0]', 'water', 576)
|
||||
ops.append({'op': 'settle', 'required_chunks': [[0, 0]]})
|
||||
cmd('/tp @s 8.5 205 14.5 0 0', 'owner-position', 1)
|
||||
cmd('/gamemode creative @s', 'owner-mode')
|
||||
cmd('/execute if block 8 204 14 glass', 'owner-support', 1)
|
||||
cmd('/scoreboard objectives add h177 dummy', 'objective')
|
||||
cmd('/execute store result score start h177 run time query gametime', 'clock-start', diagnostic=True)
|
||||
cmd('/execute if entity @s[type=player,name=Player]', 'owner-type', 1)
|
||||
for field, result in [('UUID', 4), ('Health', 20), ('playerGameType', 1), ('Pos', 3)]:
|
||||
cmd(f'/data get entity @s {field}', 'owner-' + field, result)
|
||||
cmd('/summon cow 12.5 201 10.5 {UUID:[I;177,0,0,100],Health:10f,NoAI:1b,NoGravity:1b,Silent:1b,PersistenceRequired:1b,CustomName:"H177_cow"}', 'cow-summon', 1)
|
||||
|
||||
def cow(label):
|
||||
cmd(f'/execute as {COW} if entity @s[type=minecraft:cow]', label + '-cow-type', 1)
|
||||
cmd('/execute if loaded 12 201 10', label + '-cow-loaded', 1)
|
||||
cmd('/execute if block 12 201 10 water[level=0]', label + '-cow-water', 1)
|
||||
for field, result in [('UUID', 4), ('Health', 10), ('Pos', 3), ('NoAI', 1), ('NoGravity', 1)]:
|
||||
cmd(f'/data get entity {COW} {field}', label + '-cow-' + field, result)
|
||||
cmd(f'/data get entity {COW}', label + '-cow-body', diagnostic=True)
|
||||
|
||||
cow('before')
|
||||
for n, (case, kind, no_ai, attributed) in enumerate(CASES, 1):
|
||||
x = n * 2
|
||||
uuid = f'000000b1-0000-0000-0000-{n:012x}'
|
||||
dark = ',DarkTicksRemaining:0' if kind == 'glow_squid' else ''
|
||||
cmd(f'/execute if loaded {x} 201 4', case + '-loaded', 1)
|
||||
cmd(f'/execute if block {x} 201 4 water[level=0]', case + '-water', 1)
|
||||
cmd(f'/execute if block {x} 199 4 stone', case + '-floor', 1)
|
||||
cmd(f'/summon {kind} {x + .5} 201 4.5 {{UUID:[I;177,0,0,{n}],Health:10f,NoAI:{no_ai}b,'
|
||||
f'NoGravity:1b,Silent:1b,PersistenceRequired:1b,CustomName:"H177_{case}"{dark}}}', case + '-summon', 1)
|
||||
for phase in ['before', 'after']:
|
||||
if phase == 'after':
|
||||
for holder in ['result', 'success']:
|
||||
cmd(f'/scoreboard players set {holder} h177 -99', case + '-reset-' + holder, -99)
|
||||
source = f' by {COW}' if attributed else ''
|
||||
cmd(f'/execute store result score result h177 store success score success h177 '
|
||||
f'run damage {uuid} 1 minecraft:generic{source}', case + '-hit', admission=attributed)
|
||||
for holder in ['result', 'success']:
|
||||
cmd(f'/scoreboard players get {holder} h177', case + '-' + holder, admission=attributed)
|
||||
cmd(f'/execute as {uuid} if entity @s[type=minecraft:{kind}]', case + '-' + phase + '-type', 1)
|
||||
for field, result in [('UUID', 4), ('Health', 10 if phase == 'before' else 9), ('Pos', 3),
|
||||
('NoGravity', 1), ('Silent', 1), ('PersistenceRequired', 1)]:
|
||||
cmd(f'/data get entity {uuid} {field}', case + '-' + phase + '-' + field, result)
|
||||
cmd(f'/data get entity {uuid} NoAI', case + '-' + phase + '-NoAI',
|
||||
callback=[True, 1] if no_ai else [False, 0])
|
||||
if kind == 'glow_squid':
|
||||
expected = 100 if phase == 'after' and attributed else 0
|
||||
cmd(f'/data get entity {uuid} DarkTicksRemaining', case + '-' + phase + '-dark', expected)
|
||||
cmd(f'/data get entity {uuid} Motion', case + '-' + phase + '-Motion', diagnostic=True)
|
||||
cmd(f'/data get entity {uuid}', case + '-' + phase + '-body', diagnostic=True)
|
||||
cow('after')
|
||||
cmd('/execute store result score now h177 run time query gametime', 'clock-end', diagnostic=True)
|
||||
cmd('/scoreboard players operation now h177 -= start h177', 'clock-delta-operation', 0)
|
||||
cmd('/scoreboard players get now h177', 'clock-delta', 0)
|
||||
return ops
|
||||
|
||||
|
||||
def main():
|
||||
full = '--full-diagnostic' in sys.argv
|
||||
if full:
|
||||
sys.argv.remove('--full-diagnostic')
|
||||
reference = None
|
||||
if '--reference' in sys.argv:
|
||||
i = sys.argv.index('--reference')
|
||||
reference = Path(sys.argv[i + 1]).resolve()
|
||||
del sys.argv[i:i + 2]
|
||||
if '--work-dir' not in sys.argv:
|
||||
sys.argv += ['--work-dir', str(driver.lab.LOCAL / 'squid-damage-result-parity')]
|
||||
build = Path(sys.argv[sys.argv.index('--work-dir') + 1]).resolve()
|
||||
ops = program(full)
|
||||
driver.corpus_module.groups = lambda: [('squid inherited damage result', ops)]
|
||||
sys.argv.append('--allow-gaps')
|
||||
driver.main()
|
||||
recorded = [op for op in ops if op['op'] == 'cmd' and op.get('record', True)]
|
||||
java = [json.loads(s) for s in (build / 'vanilla.jsonl').read_text().splitlines()]
|
||||
for op, row in zip(recorded, java):
|
||||
if 'expect_results' in op:
|
||||
assert row['results'] == op['expect_results'], ('Java action admission', op, row)
|
||||
rows_by_label = {op['label']: row for op, row in zip(recorded, java)}
|
||||
# Measured on the fresh reference before either native capture: an error
|
||||
# here invokes false/0, not an assumed missing callback or stale sentinel.
|
||||
for case, _, _, attributed in CASES:
|
||||
expected = [[True, 1]] if attributed else [[False, 0]]
|
||||
assert rows_by_label[case + '-hit']['results'] == expected
|
||||
for holder in ['result', 'success']:
|
||||
assert rows_by_label[case + '-' + holder]['results'] == [[True, int(attributed)]]
|
||||
if '--reference-only' in sys.argv:
|
||||
return
|
||||
source = reference or build / 'vanilla.jsonl'
|
||||
if reference:
|
||||
assert source.read_bytes() == (build / 'vanilla.jsonl').read_bytes(), 'fresh Java bytes changed'
|
||||
expected = [json.loads(s) for s in source.read_text().splitlines()]
|
||||
native = [json.loads(s) for s in (build / 'rust.jsonl').read_text().splitlines()]
|
||||
assert len(expected) == len(native) == len(recorded)
|
||||
differences = [{'label': op['label'], 'expected': a, 'actual': b}
|
||||
for op, a, b in zip(recorded, expected, native) if a != b]
|
||||
report = {'records': len(native), 'passed': len(native) - len(differences),
|
||||
'reference_sha256': hashlib.sha256(source.read_bytes()).hexdigest(),
|
||||
'comparison': 'complete raw records, no masks/tolerances', 'differences': differences}
|
||||
(build / 'strict-report.json').write_text(json.dumps(report, indent=2) + '\n')
|
||||
if not full:
|
||||
for op, a, b in zip(recorded, expected, native):
|
||||
if op['admission']:
|
||||
assert a == b, ('actual fixture admission', op, a, b)
|
||||
print(f'Strict raw records: {report["passed"]}/{report["records"]}')
|
||||
if differences:
|
||||
raise SystemExit(1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in new issue
Block a user