874b6f20c2
Основа — Horizon (MIT). История апстрима не переносится: этот репозиторий ведёт свою.
122 lines
7.4 KiB
Python
122 lines
7.4 KiB
Python
#!/usr/bin/env python3
|
|
"""Launch the Rust development scene and check bounded GPU presentation.
|
|
|
|
Requires prepared private resources and a desktop display (or --vscreen on
|
|
headless Linux). This is a renderer startup check, not a gameplay parity gate.
|
|
"""
|
|
import argparse
|
|
import hashlib
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
import subprocess
|
|
import sys
|
|
|
|
try:
|
|
from . import lab, test_rust_parity as base, test_model_baking_parity as baking
|
|
except ImportError:
|
|
import lab
|
|
import test_rust_parity as base
|
|
import test_model_baking_parity as baking
|
|
|
|
|
|
def resource_fingerprint(root):
|
|
files = [root / 'facts.json', root / 'model-states.json', root / 'render-definitions.json', root / 'occlusion.nbt', root / 'cached-collision.nbt', root / 'movement-properties.nbt', root / 'attributes.json', root / 'landing-definitions.json', root / 'damage-types.json', root / 'damage-tags.json', root / 'damage-catalog.json', root / 'player-float-ids.json', root / 'block-tags.json', root / 'horizon-resources.json',
|
|
*sorted(path for path in (root / 'assets').rglob('*') if path.is_file())]
|
|
digest = hashlib.sha256()
|
|
for path in files:
|
|
data = path.read_bytes()
|
|
digest.update(path.relative_to(root).as_posix().encode() + b'\0')
|
|
digest.update(len(data).to_bytes(8, 'big')); digest.update(data)
|
|
return {'files': len(files), 'sha256': digest.hexdigest()}
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument('--vscreen', action='store_true')
|
|
parser.add_argument('--prepare', action='store_true')
|
|
args = parser.parse_args()
|
|
if args.prepare:
|
|
subprocess.run([sys.executable, str(lab.ROOT / 'tools/prepare_native_client.py'),
|
|
'--prepare'], cwd=lab.ROOT, check=True)
|
|
root = lab.LOCAL / 'horizon-client'
|
|
root.mkdir(parents=True, exist_ok=True)
|
|
report = root / 'startup-smoke.json'
|
|
report.unlink(missing_ok=True)
|
|
resources = resource_fingerprint(root / lab.VERSION)
|
|
sources = [lab.ROOT / 'Cargo.toml', lab.ROOT / 'Cargo.lock', Path(__file__).resolve(),
|
|
lab.ROOT / 'crates/horizon-client/Cargo.toml', lab.ROOT / 'crates/horizon-assets/Cargo.toml',
|
|
*sorted((lab.ROOT / 'crates/horizon-client/src').rglob('*')),
|
|
*sorted((lab.ROOT / 'crates/horizon-assets/src').rglob('*.rs'))]
|
|
oracle = lab.ROOT / 'mods/horizon/src/test/java/dev/horizonlab/parity/VanillaGallerySelectionOracle.java'
|
|
helper = oracle.with_name('VanillaCuboidModelOracle.java')
|
|
heightmap = oracle.with_name('VanillaHeightmapOracle.java')
|
|
sources += [oracle, helper, heightmap, lab.ROOT/'tools/test_model_baking_parity.py', lab.ROOT/'tools/test_rust_parity.py', lab.ROOT/'tools/lab.py', lab.ROOT/'tools/prepare_native_client.py']
|
|
for crate in ['horizon-core', 'horizon-world']:
|
|
sources += [lab.ROOT/'crates'/crate/'Cargo.toml', *sorted((lab.ROOT/'crates'/crate/'src').rglob('*.rs'))]
|
|
tested_sources = {
|
|
str(path.relative_to(lab.ROOT)): lab.digest(path, 'sha256')
|
|
for path in sources
|
|
if path.is_file()
|
|
}
|
|
environment = dict(os.environ, RUSTUP_TOOLCHAIN='stable')
|
|
subprocess.run(['cargo', 'build', '--locked', '-p', 'horizon-client'],
|
|
cwd=lab.ROOT, env=environment, check=True, timeout=3600)
|
|
metadata = json.loads(subprocess.check_output(
|
|
['cargo', 'metadata', '--locked', '--no-deps', '--format-version', '1'],
|
|
cwd=lab.ROOT, env=environment))
|
|
executable = Path(metadata['target_directory']) / 'debug' / (
|
|
'horizon-client.exe' if os.name == 'nt' else 'horizon-client')
|
|
command = (['vscreen', 'run'] if args.vscreen else []) + [
|
|
str(executable), '--radius', '1', '--frames', '30',
|
|
'--smoke-report', str(report), '--timeout-seconds', '60']
|
|
with (root / 'startup-smoke.log').open('w') as log:
|
|
subprocess.run(command, cwd=lab.ROOT, env=lab.game_env(environment), check=True,
|
|
stdout=log, stderr=subprocess.STDOUT, timeout=120)
|
|
result = json.loads(report.read_text())
|
|
if (result.get('error') is not None or result.get('presented_frames') != 30
|
|
or result.get('resident_chunks') != 9 or result.get('merged_quads') != 3169
|
|
or result.get('visible_model_faces') != 5991 or result.get('culled_model_faces') != 59636
|
|
or result.get('model_blocks') != 10919
|
|
or result.get('model_quads') != 179 or len(result.get('model_gallery', [])) != 16
|
|
or result.get('sprite_layers') != 18 or result.get('animated_sprites') != 1
|
|
or not result.get('adapter') or min(result.get('window_size', [0])) <= 0):
|
|
raise RuntimeError('Incomplete native client presentation: ' + str(report))
|
|
lock, jars, _ = base.verify_artifacts()
|
|
classes = root/'gallery-classes'; classes.mkdir(exist_ok=True)
|
|
dependencies = os.pathsep.join(map(str, [jars['client'], *baking.client_libraries()]))
|
|
native = root/'gallery-selection.json'
|
|
with (root/'gallery-selection.log').open('w') as log:
|
|
subprocess.run(['javac', '--release', str(lock['minecraft']['java_major']), '-proc:none', '-cp', dependencies,
|
|
'-d', str(classes), str(oracle), str(helper), str(heightmap)], cwd=lab.ROOT, check=True,
|
|
stdout=log, stderr=subprocess.STDOUT, timeout=3600)
|
|
subprocess.run(['java', '-Xmx512M', '-cp', str(classes)+os.pathsep+dependencies,
|
|
'dev.horizonlab.parity.VanillaGallerySelectionOracle', str(report), str(native), str(root / lab.VERSION)],
|
|
cwd=lab.ROOT, check=True, stdout=log, stderr=subprocess.STDOUT, timeout=3600)
|
|
native_result = json.loads(native.read_text())
|
|
selections = native_result['gallery']
|
|
result['native_scene_culling'] = native_result['scene']
|
|
if len(selections) != len(result['model_gallery']):
|
|
raise RuntimeError('Incomplete native gallery selection check')
|
|
result['native_selection'] = {'matched_placements': len(selections),
|
|
'selected_parts': sum(len(s['models']) for s in selections),
|
|
'client_jar_sha256': lab.digest(jars['client'], 'sha256'),
|
|
'output_sha256': lab.digest(native, 'sha256'),
|
|
'scope': 'Actual native registry states, blockstate dispatch, tagged root baking and legacy collection using state.getSeed(position). All 16 featured placements match model names/transforms. An independently constructed native scene also matches total occupied blocks and visible/culled model faces using native parent resolution, rotated culling directions and Block.shouldRenderFace. No shader/pixel parity claim.'}
|
|
if resource_fingerprint(root / lab.VERSION) != resources:
|
|
raise RuntimeError('Resources changed during native client verification')
|
|
result['resource_files'] = resources
|
|
result['executable_sha256'] = lab.digest(executable, 'sha256')
|
|
result['resource_manifest_sha256'] = lab.digest(
|
|
root / lab.VERSION / 'horizon-resources.json', 'sha256')
|
|
actual_sources = {str(path.relative_to(lab.ROOT)): lab.digest(path, 'sha256') for path in sources if path.is_file()}
|
|
if actual_sources != tested_sources:
|
|
raise RuntimeError('Sources changed during native client verification')
|
|
result['sources'] = tested_sources
|
|
lab.write_json(report, result)
|
|
print(f'Native renderer startup passed: {report}')
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|