#!/usr/bin/env python3 """Frozen local Workbench drone fixture; no live server or installed asset writes.""" from pathlib import Path import hashlib,json,shutil,subprocess,sys ROOT=Path(__file__).resolve().parents[3] sys.path.insert(0,str(ROOT/'tools')) from source_manifest import addon_manifest from headless_game import assert_prefix_idle from external_addons import parse_project name=sys.argv[1] absent="--absent" in sys.argv[2:] if not name.isalnum(): raise ValueError('Use an alphanumeric run suffix') source=Path('/tmp/erri-drone-recon-source-'+name) profile=Path('/tmp/erri-drone-recon-run-'+name) compat=ROOT/'.local/playtest-compat-20260920' assert_prefix_idle(compat) source.mkdir();profile.mkdir() for rel in addon_manifest(ROOT): target=source/rel;target.parent.mkdir(parents=True,exist_ok=True);shutil.copy2(ROOT/rel,target) shutil.copytree(ROOT/'tools',source/'tools',ignore=shutil.ignore_patterns('__pycache__')) (source/'docs').mkdir();shutil.copy2(ROOT/'docs/external-addons.json',source/'docs/external-addons.json') fixture=Path(__file__).with_name('native-absent.c' if absent else 'native-fixture.c') shutil.copy2(fixture,source/'addons/core/Scripts/Game/ERR/00_Core/ERR_ZDroneFixture.c') # Opt-in fixture is independent of the historical generic Core smoke. p=source/'addons/core/Scripts/Game/ERR/00_Core/ERR_NativeBaseSmoke.c' p.write_text(p.read_text().replace('array independentFixtures = {','array independentFixtures = {"drone-recon", ')) p=source/'tools/headless_workbench.py' p.write_text(p.read_text().replace("choices=['coordinated-ai',", "choices=['drone-recon', 'coordinated-ai',").replace("a.smoke not in {'coordinated-ai',", "a.smoke not in {'drone-recon', 'coordinated-ai',")) # The installed optional drone addon is added to this isolated source's # exact dependency policy only. Runtime files are hashed and checked by the # normal runner, and all original dependency checks remain enabled. installed=Path.home()/'.local/share/Steam/steamapps/compatdata/1874880/pfx/drive_c/users/steamuser/Documents/My Games/ArmaReforger/addons' optional={'65AD60E204191D37':('Realistic Combat Drones','RealisticCombatDrones_65AD60E204191D37')} if absent: optional={} policy_file=source/'docs/external-addons.json';policy=json.loads(policy_file.read_text());allowed={} for guid,(title,folder) in optional.items(): project=installed/folder/'addon.gproj';actual,deps=parse_project(project.read_text());assert actual==guid allowed[guid]=deps policy['addons'][guid]={'title':title,'version':'2.3.7; installed 2026-10-21','dependencies':sorted(deps),'project_sha256':hashlib.sha256(project.read_bytes()).hexdigest()} policy['direct_ai_dependencies'].append(guid) policy_file.write_text(json.dumps(policy,indent=2)+'\n') p=source/'tools/external_addons.py' s=p.read_text();i=s.index('\ndef require(') s=s[:i]+'\nALLOWED.update('+repr(allowed)+')\nDIRECT += '+repr(tuple(optional))+'\n'+s[i:];p.write_text(s) if not absent: p=source/'addons/core/addon.gproj';s=p.read_text().replace(' Dependencies {',' Dependencies {\n "65AD60E204191D37"');p.write_text(s) (profile/'profile').mkdir() (profile/'profile/EscapistsFactions.json').write_text(json.dumps({'escapists':'ussr','enemy':'us','independent':'fia'})+'\n') (profiles:=profile/'fixture-inputs.json').write_text(json.dumps({'fixture_sha256':hashlib.sha256(fixture.read_bytes()).hexdigest(),'production_sources':addon_manifest(ROOT),'source':str(source),'profile':str(profile)},indent=2)+'\n') command=[sys.executable,str(source/'tools/headless_workbench.py'),'--compat',str(compat),'--profile',str(profile),'--external-addons',str(installed),'--project','core','--smoke','drone-recon','--rounds','1','--timeout','180'] with (profile/'runner-output.log').open('w') as output: result=subprocess.run(command,stdout=output,stderr=subprocess.STDOUT) print(json.dumps({'exit':result.returncode,'source':str(source),'profile':str(profile)})) print('\n'.join((profile/'runner-output.log').read_text().splitlines()[-45:])) raise SystemExit(result.returncode)