mirror of
https://github.com/horizon-rust/horizon.git
synced 2026-11-07 02:56:17 -10:00
Widen four parity harnesses' inputs
Cover all block states, registered enchantments, lore styles and representative Lunge tiers using the existing vanilla oracles. Co-Authored-By: pi coding agent (DeepSeek-V4.1-Flash) <[email protected]> Co-Authored-By: Codex (gpt-6.1-sol) <[email protected]> Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01WZzq8KX3vDMZy53y1vXqeT
This commit is contained in:
1 parent
41f822b659
commit
f7190cff67
4 files changed
+161
-20
No files matched your search
@@ -1,8 +1,10 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Compare level-free block-model tints with actual 26.3 BlockTintSources.
|
||||
|
||||
Every state of blocks with registered sources, plus untinted carried blocks,
|
||||
is checked at indices -1, 0, 1 and 2. Geometry and framebuffer are separate checks.
|
||||
Every state of every registered block -- not only the hand-picked tinted
|
||||
families -- is checked at indices -1, 0, 1 and 2: a block Horizon tints and
|
||||
vanilla does not (or the other way round) only shows up in that sweep.
|
||||
Geometry and framebuffer are separate checks.
|
||||
"""
|
||||
import json
|
||||
try:
|
||||
@@ -18,9 +20,15 @@ attached_pumpkin_stem melon_stem pumpkin_stem lily_pad stone poppy red_mushroom
|
||||
brown_mushroom crimson_fungus warped_roots carved_pumpkin""".split()
|
||||
|
||||
|
||||
def sweep(states):
|
||||
"""The hand-picked families plus every other registered block name."""
|
||||
names = {name.split(':', 1)[-1] for name in states.by_name}
|
||||
return sorted(set(BLOCKS) | names)
|
||||
|
||||
|
||||
def make_input(build):
|
||||
states = mesh.States(lab.RESOURCES)
|
||||
ids = sorted({state for name in BLOCKS for state in states.by_name["minecraft:" + name]})
|
||||
ids = sorted({state for name in sweep(states) for state in states.by_name.get("minecraft:" + name, [])})
|
||||
rows = [[state, index] for state in ids for index in [-1, 0, 1, 2]]
|
||||
path = build / "input.json"
|
||||
path.write_text(json.dumps(dict(resources=str(lab.RESOURCES), cases=rows)))
|
||||
@@ -29,7 +37,9 @@ def make_input(build):
|
||||
|
||||
def main():
|
||||
harness.differential("block-tint-parity", "VanillaBlockTintOracle", "block_tint_probe",
|
||||
make_input, "BlockTintSource.color(state), all states of tinted blocks and untinted carried blocks; per-face tint indices, no level/biome. Not model or framebuffer parity.",
|
||||
make_input, "BlockTintSource.color(state) at indices -1, 0, 1 and 2 for every state of every registered block, "
|
||||
"not just the hand-picked tinted families; per-face tint indices, no level/biome. "
|
||||
"Not model or framebuffer parity.",
|
||||
sources=[lab.ROOT / "tools/test_block_tint_parity.py", lab.ROOT / "crates/horizon-client-render/src/mesher.rs",
|
||||
lab.ROOT / "crates/horizon-client/src/item_render.rs"])
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Compare persistent enchantment levels and display names with vanilla 26.3."""
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
@@ -11,6 +12,16 @@ except ImportError:
|
||||
import test_rust_parity as base
|
||||
|
||||
|
||||
def registry():
|
||||
"""Every enchantment the prepared data pack registers, with its max_level."""
|
||||
root = lab.RESOURCES / 'data/minecraft/enchantment'
|
||||
out = []
|
||||
for path in sorted(root.glob('*.json')):
|
||||
data = json.loads(path.read_text(encoding='utf-8'))
|
||||
out.append((f'minecraft:{path.stem}', int(data.get('max_level', 1))))
|
||||
return out
|
||||
|
||||
|
||||
def corpus():
|
||||
values = [
|
||||
'-2147483648', '-1', '0', '1', '10', '11', '254', '255', '256', '32767',
|
||||
@@ -26,6 +37,26 @@ def corpus():
|
||||
for label, levels in cases:
|
||||
yield (f'{index}:{label}\t{{"minecraft:damage":12,"{key}":'
|
||||
f'{levels},"{other}":{{mending:1}}}}')
|
||||
# Every registered enchantment at every interesting level, alone and beside a
|
||||
# second enchantment: the level ranges each definition declares (its
|
||||
# max_level, one below, one above, far above), the byte range the component
|
||||
# stores, and levels the display name turns into a numeral or a number.
|
||||
enchantments = registry()
|
||||
for index, key in enumerate(keys):
|
||||
other = keys[1 - index]
|
||||
for name, maximum in enchantments:
|
||||
levels = sorted({-2147483648, -1, 0, 1, 2, max(1, maximum - 1), maximum,
|
||||
maximum + 1, 255, 256, 32767, 2147483647})
|
||||
for level in levels:
|
||||
yield (f'{index}:{name[10:]}-{level}\t{{"minecraft:damage":12,"{key}":'
|
||||
f'{{"{name}":{level}}},"{other}":{{"minecraft:mending":1}}}}')
|
||||
for level in sorted({0, 1, maximum, maximum + 1}) if name != "minecraft:sharpness" else ():
|
||||
yield (f'{index}:{name[10:]}-{level}-pair\t{{"minecraft:damage":12,"{key}":'
|
||||
f'{{"minecraft:sharpness":{maximum},"{name}":{level}}}}}')
|
||||
for label, level in [('all-max', None), ('all-one', 1), ('all-zero', 0)]:
|
||||
levels = {name: (maximum if level is None else level) for name, maximum in enchantments}
|
||||
body = ','.join(f'"{name}":{value}' for name, value in levels.items())
|
||||
yield f'{index}:{label}\t{{"minecraft:damage":12,"{key}":{{{body}}}}}'
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
@@ -5,6 +5,8 @@ Vanilla prints what ``ItemLore.addToTooltip`` adds after ``ComponentUtils.mergeS
|
||||
with dark purple italic (VanillaLoreTooltipOracle). Horizon prints what
|
||||
``item_tooltip::lore_texts`` gives (examples/probe/lore_tooltip_fixture).
|
||||
"""
|
||||
import json
|
||||
|
||||
try:
|
||||
from . import lab, oracle_harness as harness
|
||||
except ImportError:
|
||||
@@ -30,21 +32,103 @@ CASES = [
|
||||
]
|
||||
|
||||
|
||||
COLORS = ['black', 'dark_blue', 'dark_green', 'dark_aqua', 'dark_red', 'dark_purple', 'gold',
|
||||
'gray', 'dark_gray', 'blue', 'green', 'aqua', 'red', 'light_purple', 'yellow', 'white',
|
||||
'#000000', '#ffffff', '#12ab34', '#c0ffee']
|
||||
STYLES = ['bold', 'italic', 'underlined', 'strikethrough', 'obfuscated']
|
||||
|
||||
|
||||
def stack(entries, display=None):
|
||||
"""SNBT of a stick whose lore is the given list of NBT text components."""
|
||||
components = '"minecraft:lore":[' + ','.join(entries) + ']'
|
||||
if display is not None:
|
||||
components += ',"minecraft:tooltip_display":' + display
|
||||
return '{id:"minecraft:stick",count:1,components:{' + components + '}}'
|
||||
|
||||
|
||||
def component(value):
|
||||
"""A lore entry: a plain string stays a string, an object is JSON in NBT single quotes."""
|
||||
return "'" + json.dumps(value, separators=(',', ':'), ensure_ascii=False) + "'"
|
||||
|
||||
|
||||
def lore(*entries):
|
||||
return stack([component(entry) for entry in entries])
|
||||
|
||||
|
||||
def generated():
|
||||
"""Styles, colours and nesting across the whole text-component surface."""
|
||||
out = []
|
||||
for color in COLORS:
|
||||
out.append(lore({'text': 'C', 'color': color}))
|
||||
for style in STYLES:
|
||||
out.append(lore({'text': 'S', style: True}))
|
||||
out.append(lore({'text': 'S', style: False}))
|
||||
# The dark purple italic line style under an explicit false, and a child
|
||||
# that turns the line's style back on or off.
|
||||
out.append(lore({'text': 'P', style: False, 'extra': [{'text': 'C', style: True}]}))
|
||||
out.append(lore({'text': 'P', style: True, 'extra': [{'text': 'C', style: False}]}))
|
||||
out.append(lore({'text': 'P', style: True, 'extra': ['plain']}))
|
||||
for color in COLORS[:16]:
|
||||
for style in STYLES:
|
||||
out.append(lore({'text': 'P', 'color': color, style: True,
|
||||
'extra': [{'text': 'C', 'color': 'red', 'italic': False}]}))
|
||||
# Nesting chains: each level overrides one more field.
|
||||
for depth in range(1, 5):
|
||||
child = {'text': 'd' + str(depth), 'color': COLORS[depth % len(COLORS)],
|
||||
'bold': bool(depth % 2), 'italic': bool((depth + 1) % 2),
|
||||
'underlined': bool(depth % 3 == 0), 'strikethrough': bool(depth % 2 == 0)}
|
||||
for level in range(depth - 1, 0, -1):
|
||||
child = {'text': 'd' + str(level), 'color': COLORS[level % len(COLORS)],
|
||||
'extra': [child], 'bold': bool(level % 2),
|
||||
'obfuscated': bool(level % 4 == 0)}
|
||||
out.append(lore(child))
|
||||
# Siblings at one level, one plain string and one object, and empty texts.
|
||||
out.append(lore({'extra': [{'text': 'A', 'color': 'gold'}, 'B', {'text': '', 'color': 'red'},
|
||||
{'extra': [{'text': 'C', 'italic': False}]}]}))
|
||||
out.append(lore({'text': ''}))
|
||||
out.append(lore({'text': '', 'color': 'aqua'}))
|
||||
out.append(lore('', {'text': ''}))
|
||||
# Many lines: order, mixed styles and repeated colours.
|
||||
out.append(lore(*[{'text': 'L' + str(i), 'color': COLORS[i % len(COLORS)],
|
||||
'bold': bool(i % 2), 'italic': bool(i % 3 == 0)} for i in range(10)]))
|
||||
out.append(lore(*['line' + str(i) for i in range(6)]))
|
||||
# Text outside ASCII, with styles, and a nested non-ASCII child.
|
||||
out.append(lore({'text': '\u00e9\u4e2d\U0001F600', 'color': 'gold', 'bold': True}))
|
||||
out.append(lore({'text': 'p', 'extra': [{'text': '\U0001F600'}], 'italic': False}))
|
||||
# tooltip_display: hidden lore, another hidden component, an empty and a
|
||||
# repeated entry, and hide_tooltip (which must not hide the lore lines).
|
||||
out.append(stack([component({'text': 'H'})], '{hidden_components:["minecraft:lore"]}'))
|
||||
out.append(stack([component({'text': 'H'})], '{hidden_components:["minecraft:enchantments"]}'))
|
||||
out.append(stack([component({'text': 'H'})], '{hidden_components:[]}'))
|
||||
out.append(stack([component({'text': 'H'})], '{hidden_components:["minecraft:lore","minecraft:lore"]}'))
|
||||
out.append(stack([component({'text': 'H'})], '{hide_tooltip:1b}'))
|
||||
out.append(stack([component({'text': 'H'})], '{hide_tooltip:1b,hidden_components:["minecraft:enchantments"]}'))
|
||||
out.append(stack([component({'text': 'H'})], '{hide_tooltip:0b,hidden_components:["minecraft:lore"]}'))
|
||||
return out
|
||||
|
||||
|
||||
def corpus():
|
||||
return CASES + generated()
|
||||
|
||||
|
||||
def cases(build):
|
||||
lines = corpus()
|
||||
path = build / 'cases.txt'
|
||||
path.write_text(''.join(line + '\n' for line in CASES))
|
||||
return path, len(CASES)
|
||||
path.write_text(''.join(line + '\n' for line in lines))
|
||||
return path, len(lines)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
harness.differential(
|
||||
'lore-tooltip-parity', 'VanillaLoreTooltipOracle', 'lore_tooltip_fixture', cases,
|
||||
'Item lore tooltip lines: dark purple italic, child colours, explicit non-italic, a line with no text of its own,'
|
||||
' plain-string children and packed colours, and tooltip_display',
|
||||
' plain-string children and packed colours, and tooltip_display; every named and packed colour, every style flag'
|
||||
' explicit and inherited, nesting four levels deep, sibling strings and objects, empty texts, ten-line lore,'
|
||||
' non-ASCII text and the hidden_components/hide_tooltip combinations',
|
||||
sources=[
|
||||
lab.ROOT / 'crates/horizon-client/src/item_tooltip.rs',
|
||||
lab.ROOT / 'crates/horizon-items/src/item_data.rs',
|
||||
lab.ROOT / 'crates/horizon-client-core/src/text.rs',
|
||||
lab.ROOT / 'crates/horizon-client-core/src/component.rs',
|
||||
],
|
||||
expected_lines=len(CASES))
|
||||
expected_lines=len(corpus()))
|
||||
@@ -17,17 +17,33 @@ except ImportError:
|
||||
|
||||
NAME = 'lunge-motion-parity'
|
||||
# server_dir and generate_world write these server.properties.
|
||||
locator.PROPERTIES = dict(locator.PROPERTIES, gamemode='survival')
|
||||
SCRIPT = [
|
||||
# The jump ends on the ground: the join's position said otherwise, and a server's copy goes by what it is told.
|
||||
('setup', ['cmd Alice give @s minecraft:netherite_spear[enchantments={lunge:3}]', 'arc Alice -1']),
|
||||
('ground', ['stab Alice']),
|
||||
('same-tick', ['arc Alice 0']),
|
||||
('rising', ['arc Alice 3']),
|
||||
('falling', ['arc Alice 8']),
|
||||
locator.PROPERTIES = dict(locator.PROPERTIES, gamemode='survival', difficulty='peaceful')
|
||||
|
||||
# Exercise the lowest and an intermediate tier/level as well as the original maximum.
|
||||
# The extra tiers jab standing: repeating every jump phase drifts Alice far enough
|
||||
# from the spawn that vanilla's own real-time steps occasionally miss the jab, and
|
||||
# the original maximum keeps all four phases (the same distance as before).
|
||||
PHASES = [('ground', ['stab Alice']), ('same-tick', ['arc Alice 0']), ('rising', ['arc Alice 3']),
|
||||
('falling', ['arc Alice 8'])]
|
||||
GROUND = [PHASES[0]]
|
||||
SPEARS = [('wooden_spear', 1, GROUND), ('iron_spear', 2, GROUND), ('netherite_spear', 3, PHASES)]
|
||||
|
||||
|
||||
def script():
|
||||
# Peaceful: the flat world's slimes would otherwise hit Alice while the
|
||||
# script is idle before the first command (and kill her), adding motions.
|
||||
steps = [('setup', ['arc Alice -1'])]
|
||||
for spear, level, phases in SPEARS:
|
||||
steps.append(('setup', ['cmd Alice clear @s',
|
||||
f'cmd Alice give @s minecraft:{spear}[enchantments={{lunge:{level}}}]']))
|
||||
for phase, actions in phases:
|
||||
steps.append((f'{spear}-{level}-{phase}', list(actions)))
|
||||
# jumpFromGround gives a sprinting server copy 0.2 forward too.
|
||||
('sprinting', ['sprint Alice', 'arc Alice 3']),
|
||||
]
|
||||
steps.append(('sprinting', ['sprint Alice', 'arc Alice 3']))
|
||||
return steps
|
||||
|
||||
|
||||
SCRIPT = script()
|
||||
|
||||
|
||||
def shown(lines):
|
||||
@@ -38,13 +54,13 @@ def shown(lines):
|
||||
|
||||
def check(vanilla, vanilla_all):
|
||||
steps = {line.split()[1] for line in vanilla}
|
||||
missing = {f'step={step}' for step, _ in SCRIPT[1:]} - steps
|
||||
missing = {f'step={step}' for step, _ in SCRIPT if step != 'setup'} - steps
|
||||
return f'vanilla sent no motion in {sorted(missing)}' if missing else None
|
||||
|
||||
|
||||
def main():
|
||||
lines = locator.one_player(NAME, SCRIPT, shown, check, "A spear's Lunge standing and at three points of a jump "
|
||||
'on a dedicated server: the motion the player is sent.', step_seconds=2.0)
|
||||
'on a dedicated server: motion for wooden Lunge I, iron Lunge II and netherite Lunge III.', step_seconds=2.0)
|
||||
print(f'lunge motion: {len(lines)} lines identical')
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user