mirror of
https://github.com/horizon-rust/horizon.git
synced 2026-11-07 02:56:17 -10:00
Harnesses: retry rejected vanilla runs, observe boat dips at once, fixed piglin barter check
- one_player: vanilla's run is judged by the harness's check before the comparison, and run again (up to 3 times) when it is no proof, so a vanilla bobber that missed its armor stand on a loaded runner no longer reads as a Horizon diff (fishing_hook_entities). Horizon's run is never retried. - boat_submerged: the short dip and the re-submersion are observed at the end of their own 20-tick step, not a step later; at ~40 ticks plus the client's syncs a loaded host pushed them past the 60 that eject. - command_exec piglin pickups: what an adult's barter throws comes from vanilla's unseeded level random and the piglin picks some of it back up; check that none of the gold lies on the ground instead of the item count. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
7e5a520e3d
commit
2518376ee0
3 files changed
+28
-10
No files matched your search
@@ -1670,7 +1670,11 @@ def piglin_pickups():
|
||||
{'op': 'tick', 'n': 140},
|
||||
cmd('/execute if items entity @e[tag=barter,limit=1] weapon.offhand *'),
|
||||
cmd('/execute if items entity @e[tag=barter,limit=1] weapon.mainhand minecraft:gold_ingot'),
|
||||
cmd('/execute if entity @e[type=minecraft:item]'),
|
||||
# A baby drops nothing. What an adult's barter throws comes from the level's random (vanilla's is
|
||||
# unseeded), and the piglin picks some of it back up, so only the gold's fate is fixed: none of it lies
|
||||
# on the ground.
|
||||
cmd('/execute if entity @e[type=minecraft:item]' if baby
|
||||
else '/execute if items entity @e[type=minecraft:item] contents minecraft:gold_ingot'),
|
||||
]
|
||||
if baby:
|
||||
ops.append(cmd('/execute if data entity @e[tag=barter,limit=1] Inventory[{id:"minecraft:golden_sword"}]'))
|
||||
|
||||
@@ -16,10 +16,11 @@ EXPECTED = []
|
||||
PROBES = []
|
||||
|
||||
|
||||
def probe(label, mounted, player='Alice'):
|
||||
def probe(label, mounted, player='Alice', actions=()):
|
||||
"""A step running `actions`, at whose end `player` must be (or no longer be) a passenger."""
|
||||
PROBES.append((label, player))
|
||||
EXPECTED.append(f'{label} mounted={str(mounted).lower()}')
|
||||
SCRIPT.append((label, []))
|
||||
SCRIPT.append((label, list(actions)))
|
||||
|
||||
|
||||
for kind, water in [('oak_boat', 'water'), ('birch_chest_boat', 'water'),
|
||||
@@ -38,11 +39,12 @@ for kind, water in [('oak_boat', 'water'), ('birch_chest_boat', 'water'),
|
||||
for n in range(3):
|
||||
SCRIPT.append((f'{case}-surface-wait-{n}', []))
|
||||
probe(case + '-surface', True)
|
||||
SCRIPT.append((case + '-short-dip', [f'cmd Alice fill -3 81 -3 3 84 3 minecraft:{water}']))
|
||||
probe(case + '-short', True)
|
||||
# Each dip is one step (20 ticks, plus the ticks the client's syncs take) observed at its own end, far from
|
||||
# the 60 that eject: observed a step later, a loaded machine's syncs pushed a short dip past 60 (2026-11-02).
|
||||
probe(case + '-short', True, actions=[f'cmd Alice fill -3 81 -3 3 84 3 minecraft:{water}'])
|
||||
SCRIPT.append((case + '-surface-reset', ['cmd Alice fill -3 81 -3 3 84 3 minecraft:air']))
|
||||
SCRIPT.append((case + '-submerge', [f'cmd Alice fill -3 81 -3 3 84 3 minecraft:{water}']))
|
||||
probe(case + '-reset', True)
|
||||
probe(case + '-reset', True, actions=[f'cmd Alice fill -3 81 -3 3 84 3 minecraft:{water}'])
|
||||
# 20 + 40 + 20 ticks under water by the last observation: past 60 however fast the steps go.
|
||||
for n in range(2):
|
||||
SCRIPT.append((f'{case}-underwater-wait-{n}', []))
|
||||
probe(case + '-ejected', False)
|
||||
|
||||
@@ -270,6 +270,10 @@ def sides(name):
|
||||
return lock, build, vanilla_server, horizon_server, client
|
||||
|
||||
|
||||
# How many times one_player runs vanilla's side before giving up on a run its `check` rejects.
|
||||
VANILLA_ATTEMPTS = 3
|
||||
|
||||
|
||||
def one_player(name, script, shown, check, scope, step_seconds=4.0):
|
||||
"""A harness of one player, Alice: on each server she joins, then runs `script`, a list of (step, client
|
||||
actions) that each last `step_seconds` of the server's time (20 ticks a second). The lines `shown(transcript)` keeps must match vanilla's exactly, and
|
||||
@@ -308,7 +312,17 @@ def one_player(name, script, shown, check, scope, step_seconds=4.0):
|
||||
return out.read_text(encoding='utf-8').splitlines()
|
||||
|
||||
world = generate_world(vanilla_server, build)
|
||||
vanilla_all = run('vanilla', vanilla_server, world)
|
||||
# Vanilla's run is the specification only when `check` accepts it. On a loaded machine a real-time step can
|
||||
# miss its moment (a bobber that lands before the armor stand is there), so a run it rejects is made again,
|
||||
# up to VANILLA_ATTEMPTS times, before Horizon's is compared with it; Horizon's run is never retried.
|
||||
for attempt in range(1, VANILLA_ATTEMPTS + 1):
|
||||
vanilla_all = run('vanilla', vanilla_server, world)
|
||||
why = check(shown(vanilla_all), vanilla_all)
|
||||
if why is None:
|
||||
break
|
||||
print(f'{name}: vanilla run {attempt} of {VANILLA_ATTEMPTS} is no proof ({why})', flush=True)
|
||||
else:
|
||||
raise AssertionError(f'{why}: the script did not run on vanilla in {VANILLA_ATTEMPTS} runs (see {build})')
|
||||
horizon_all = run('horizon', horizon_server, world)
|
||||
errors = [l for l in vanilla_all + horizon_all if ' error ' in f' {l} ' or 'disconnected' in l]
|
||||
vanilla, horizon = shown(vanilla_all), shown(horizon_all)
|
||||
@@ -317,8 +331,6 @@ def one_player(name, script, shown, check, scope, step_seconds=4.0):
|
||||
diff = '\n'.join(difflib.unified_diff(vanilla, horizon, 'vanilla', 'horizon', n=1, lineterm=''))
|
||||
raise AssertionError(f'{len(errors)} errors, transcripts differ (see {build}/vanilla.txt and horizon.txt):\n'
|
||||
+ '\n'.join(errors[:10]) + '\n' + diff[:6000])
|
||||
if (why := check(vanilla, vanilla_all)) is not None:
|
||||
raise AssertionError(f'{why}: the script did not run (see {build})')
|
||||
lab.write_json(build / 'report.json', dict(
|
||||
schema=1, minecraft=lock['minecraft']['version'], platform=platform.platform(),
|
||||
java=subprocess.check_output(['java', '--version'], text=True).splitlines()[0],
|
||||
|
||||
Reference in new issue
Block a user