from pathlib import Path import os,sys,subprocess,time,signal,shutil,json,fcntl root=Path('/home/jakub/devel/escapistsrereintroduced');run=Path('/tmp')/sys.argv[1];run.mkdir() lock=(Path('/tmp')/'erri-atlas-dedicated.lock').open('a');fcntl.flock(lock,fcntl.LOCK_EX|fcntl.LOCK_NB) addons=run/'addons';addons.mkdir();shutil.copytree(root/'addons/core',addons/'ERR_Core') probe=addons/'ERR_Core/Scripts/Game/ERR/30_Map/ERRM_DedicatedAtlasProbe.c' probe.write_text('''modded class ERR_GameMode { override void EOnInit(IEntity owner) { super.EOnInit(owner); GetGame().GetCallqueue().CallLater(ERR_AtlasDedicatedProbe, 3000, false); } void ERR_AtlasDedicatedProbe() { SCR_MapEntity nativeMap = SCR_MapEntity.GetMapInstance(); vector low, high; GetGame().GetWorldEntity().GetWorldBounds(low, high); PrintFormat("ERR ATLAS DS: map=%1 worldBounds=%2..%3", nativeMap, low, high); if (nativeMap) PrintFormat("ERR ATLAS DS: nativeMapSize=%1 offset=%2", nativeMap.Size(), nativeMap.Offset()); IEntity item = ERR_Actors.Spawn("{A46500E4A30B653B}Prefabs/Items/Equipment/ERRM_SharedFieldMap.et", "5000 100 5000"); if (!item) { Print("ERR ATLAS DS: spawn failed"); return; } ERRM_SharedMapComponent paper = ERRM_SharedMapComponent.Cast(item.FindComponent(ERRM_SharedMapComponent)); float zoom; int columns, rows; bool layout = paper.PaperLayout(zoom, columns, rows); PrintFormat("ERR ATLAS DS: layout=%1 zoom=%2 columns=%3 rows=%4 pageBefore=%5", layout, zoom, columns, rows, paper.PaperPage()); paper.SelectAtlasPage(37); PrintFormat("ERR ATLAS DS: pageAfter=%1 expectedNumber37=%2", paper.PaperPage(), ERRM_PaperPages.Number(paper.PaperPage(), columns)); Print("ERR ATLAS DS COMPLETE"); } } ''') # Reuse read-only dependency links prepared by the successful hidden compile. for p in Path('/tmp/err-held-pages-consume-b/addons').iterdir(): if p.name.startswith('Dependency_'): (addons/p.name).symlink_to(p.resolve(),target_is_directory=True) exe=Path('/tmp/erri-takeover-dedicated-runtime/ArmaReforgerServer') cmd=[str(exe),'-profile',str(run/'profile'),'-noBackend','-noSound','-noThrow','-disableCrashReporter','-bindIP','127.0.0.1','-bindPort','22997','-maxFPS','30','-addonsDir',str(addons),'-addons','2528466DE220D91C','-server','{C1E20667DCE324ED}Missions/ERR_Everon.conf'] start=time.monotonic();result='timeout' with (run/'stdout.log').open('w') as f: child=subprocess.Popen(cmd,cwd=exe.parent,stdout=f,stderr=subprocess.STDOUT,start_new_session=True) try: while time.monotonic()-start<100: logs=list((run/'profile/logs').glob('*/console.log'));text=logs[-1].read_text(errors='replace') if logs else '' if 'ERR ATLAS DS COMPLETE' in text:result='complete';break if "Can't compile" in text or 'Unable to initialize' in text:result='startup-failed';break if child.poll() is not None:result='exited';break time.sleep(.5) finally: if child.poll() is None: os.killpg(child.pid,signal.SIGTERM) try:child.wait(5) except subprocess.TimeoutExpired:os.killpg(child.pid,signal.SIGKILL);child.wait() (run/'result.json').write_text(json.dumps({'result':result,'exit':child.returncode,'reaped':child.poll() is not None},indent=2)) print(result);print('\n'.join(l for l in text.splitlines() if 'ERR ATLAS DS' in l or 'SCRIPT (E)' in l))