Spaces:
Paused
Paused
Download runtime.py from ziyjiang/WorldAuditBench: direct link, hf CLI and curl.
- Browser
- Download file 4.79 kB
-
https://huggingface.co/spaces/ziyjiang/WorldAuditBench/resolve/main/runtime.py
- Command line
-
hf download hf://spaces/ziyjiang/WorldAuditBench/runtime.py
-
curl -L -o runtime.py https://huggingface.co/spaces/ziyjiang/WorldAuditBench/resolve/main/runtime.py
4.79 kB
| """One isolated Unreal process. No connection to the private evaluation services.""" | |
| import ctypes, hashlib, json, os, signal, subprocess, time | |
| from pathlib import Path | |
| ROOT = Path(__file__).resolve().parent | |
| class UnrealRuntime: | |
| def __init__(self): | |
| self.process = None | |
| self.log = None | |
| self.config = None | |
| self.error = 'The interactive environment is being prepared. Please check back soon.' | |
| path = os.environ.get('WAB_RUNTIME_CONFIG', str(ROOT / 'runtime.json')) | |
| if not Path(path).is_file(): | |
| print('Runtime unavailable: set WAB_RUNTIME_CONFIG after restoring the Unreal Linux package.', flush=True) | |
| return | |
| try: | |
| self.config = json.loads(Path(path).read_text()) | |
| binary = Path(self.config['binary']) | |
| if not binary.is_file() or not os.access(binary, os.X_OK): | |
| raise ValueError('Unreal binary is missing or not executable') | |
| expected = self.config['sha256'] | |
| if len(expected) != 64 or hashlib.file_digest(binary.open('rb'),'sha256').hexdigest() != expected: | |
| raise ValueError('Unreal binary hash does not match the configured release') | |
| gpu=subprocess.run(['nvidia-smi','--query-gpu=name,driver_version','--format=csv,noheader'], check=True, capture_output=True, text=True, timeout=10) | |
| ctypes.CDLL('libvulkan.so.1') | |
| ctypes.CDLL('libnvidia-encode.so.1') | |
| subprocess.run(['vulkaninfo', '--summary'], check=True, capture_output=True, timeout=20) | |
| print('Runtime GPU ready: '+gpu.stdout.strip(),flush=True) | |
| self.error = None | |
| except Exception as exc: | |
| print(f'Runtime preflight failed: {exc}', flush=True) | |
| self.error = 'The interactive environment is temporarily unavailable.' | |
| def start(self, case, session_id): | |
| if self.error: raise RuntimeError(self.error) | |
| self.stop() | |
| folder = Path('/tmp/worldauditbench-sessions') / session_id | |
| folder.mkdir(parents=True, exist_ok=True) | |
| binary = Path(self.config['binary']) | |
| args = [str(binary), case['map'], '-RenderOffscreen', '-vulkan', '-sm5', | |
| '-AuditorSkipSceneMenu', '-AuditorRemoteInput', | |
| '-DefaultViewportMouseCaptureMode=NoCapture', | |
| '-ini:Input:[/Script/Engine.InputSettings]:bCaptureMouseOnLaunch=False', | |
| '-ini:Input:[/Script/Engine.InputSettings]:DefaultViewportMouseLockMode=DoNotLock', | |
| '-ForceRes', '-ResX=1920', '-ResY=1080', '-Unattended', '-AudioMixer', | |
| '-PixelStreamingConnectionURL=ws://127.0.0.1:8888', '-PixelStreamingID=DefaultStreamer', | |
| '-PixelStreamingEncoderCodec=H264', '-PixelStreamingWebRTCFps=30', | |
| '-PixelStreamingWebRTCMaxBitrate=12000000', '-PixelStreamingWebRTCStartBitrate=5000000', | |
| '-PixelStreamingAllowPixelStreamingCommands=false', | |
| '-PixelStreamingKeyFilter="M,One,Two,Three,Four,Tilde"', | |
| '-ExecCmds=t.MaxFPS 30,set HUD bShowHUD false', '-SaveToUserDir', f'-UserDir={folder}/user/', | |
| f'-AuditorReviewIPC={folder}/ipc', '-log', f'-abslog={folder}/unreal.log'] | |
| policy = self.config.get('policy') | |
| if policy: | |
| args += [f'-AuditorExplorationPolicy={policy}', f'-AuditorExplorationTask={case["id"]}'] | |
| args += self.config.get('extra_args', []) | |
| self.log = (folder/'stdout.log').open('ab') | |
| # libwebsockets allocates an entry per allowed fd; Docker may advertise billions. | |
| self.process = subprocess.Popen(['/bin/sh', '-c', 'ulimit -n 4096; exec "$@"', 'unreal', *args], cwd=binary.parent, stdout=self.log, | |
| stderr=subprocess.STDOUT, start_new_session=True) | |
| def alive(self): | |
| return self.process is not None and self.process.poll() is None | |
| def ready(self, case, session_id): | |
| try: | |
| ack=json.loads((Path('/tmp/worldauditbench-sessions')/session_id/'ipc/response.json').read_text()) | |
| return (self.alive() and ack.get('status')=='ready' and | |
| ack.get('pid')==self.process.pid and ack.get('task')==case['id'] and | |
| ack.get('map')==case['map'].partition('?')[0]) | |
| except (OSError,ValueError):return False | |
| def stop(self): | |
| if self.process is not None: | |
| if self.process.poll() is None: | |
| os.killpg(self.process.pid, signal.SIGTERM) | |
| try: self.process.wait(timeout=8) | |
| except subprocess.TimeoutExpired: | |
| os.killpg(self.process.pid, signal.SIGKILL) | |
| self.process.wait(timeout=5) | |
| self.process = None | |
| if self.log: self.log.close(); self.log=None | |