Verify bounded ordinary-input approach to the prison gate
This commit is contained in:
+66
-13
@@ -348,13 +348,15 @@ def validate_paths(root, steam, compat, profile):
|
||||
refuse(f'Unexpected profile symlink: {path}')
|
||||
|
||||
|
||||
def command_for(root, steam, compat, profile, project, packed=None, capture_prison_view=False):
|
||||
def command_for(root, steam, compat, profile, project, packed=None, capture_prison_view=False, private_width=1280):
|
||||
if project not in {'core', 'chat', CONTROL}:
|
||||
refuse('Unsupported ordinary game project/control')
|
||||
if capture_prison_view and (project == CONTROL or packed is not None):
|
||||
refuse('Prison view capture requires loose successor source with its opt-in observer')
|
||||
command = ['gamescope', '--backend', 'headless', '-W', '1280', '-H', '720',
|
||||
'-w', '1280', '-h', '720', '-r', '30', '--',
|
||||
if type(private_width) is not int or private_width not in (1280, 2560) or (private_width != 1280 and (project == CONTROL or packed is not None)):
|
||||
refuse('Unsupported private headless viewport')
|
||||
command = ['gamescope', '--backend', 'headless', '-W', str(private_width), '-H', '720',
|
||||
'-w', str(private_width), '-h', '720', '-r', '30', '--',
|
||||
str(steam/'steamapps/common/Proton - Experimental/proton'), 'run',
|
||||
str(steam/'steamapps/common/Arma Reforger/ArmaReforgerSteam.exe')]
|
||||
if packed is not None:
|
||||
@@ -633,6 +635,10 @@ def validate_input_observer(a):
|
||||
|
||||
|
||||
def validate_input_exercise(a):
|
||||
if getattr(a, 'approach_wide_display', False) and not getattr(a, 'exercise_prison_approach', False):
|
||||
refuse('Wide private viewport requires the explicit prison approach experiment')
|
||||
if getattr(a, 'exercise_prison_approach', False) and not (getattr(a, 'exercise_relative_look', False) and getattr(a, 'observe_prison_gate', False)):
|
||||
refuse('Prison approach requires explicit relative look and gate observation')
|
||||
if getattr(a, 'exercise_relative_look', False) and not getattr(a, 'exercise_player_input', False):
|
||||
refuse('Relative look requires the existing explicit keyboard exercise')
|
||||
if not getattr(a, 'exercise_player_input', False):
|
||||
@@ -832,7 +838,17 @@ def input_observer_result(result, content, nonce):
|
||||
return result, evidence
|
||||
|
||||
|
||||
def input_exercise_command(command, root, compat, profile, executable, run_id, host_display, display_context, look=False):
|
||||
def approach_module_hashes(root):
|
||||
hashes = {}
|
||||
for name in ('prison_approach_proof.py', 'ordinary_gate_evidence.py', 'ordinary_look_evidence.py', 'prison_approach_evidence.py'):
|
||||
path = root/'tools'/name
|
||||
if path.is_symlink() or not path.is_file():
|
||||
refuse('Approach requires regular local proof modules')
|
||||
hashes[name] = hashlib.sha256(path.read_bytes()).hexdigest()
|
||||
return hashes
|
||||
|
||||
|
||||
def input_exercise_command(command, root, compat, profile, executable, run_id, host_display, display_context, look=False, approach=False):
|
||||
"""Put the input supervisor around the display inspector, inside Gamescope."""
|
||||
separator = command.index('--')
|
||||
if command[:separator].count('--backend') != 1 or command[command.index('--backend')+1] != 'headless':
|
||||
@@ -843,6 +859,12 @@ def input_exercise_command(command, root, compat, profile, executable, run_id, h
|
||||
if (not display_context or len(child) < 3 or child[:2] != [sys.executable, str(ownership)]
|
||||
or not re.fullmatch(r'[A-Za-z0-9]{1,32}', run_id)):
|
||||
refuse('Input exercise requires the existing scoped display wrapper and observer nonce')
|
||||
if type(approach) is not bool or (approach and not look):
|
||||
refuse('Prison approach requires explicit relative look')
|
||||
if approach:
|
||||
indices = [i for i,value in enumerate(child) if value.startswith('-errOrdinaryGate')]
|
||||
if len(indices) != 1 or child[indices[0]] != '-errOrdinaryGateObserve' or child[indices[0]+1:indices[0]+2] != [run_id]:
|
||||
refuse('Prison approach requires its exact native gate observer nonce')
|
||||
if type(look) is not bool:
|
||||
refuse('Relative look selection must be boolean')
|
||||
look_options = [i for i,value in enumerate(child) if value.startswith('-errOrdinaryLook')]
|
||||
@@ -864,6 +886,10 @@ def input_exercise_command(command, root, compat, profile, executable, run_id, h
|
||||
if look:
|
||||
context['look_requested'] = True
|
||||
wrapper.insert(-1, '--enable-private-look-proof')
|
||||
if approach:
|
||||
context['approach_requested'] = True
|
||||
context['approach_module_sha256'] = approach_module_hashes(root)
|
||||
wrapper.insert(-1, '--enable-private-approach-proof')
|
||||
return command[:separator+1] + wrapper + child, context
|
||||
|
||||
|
||||
@@ -885,6 +911,10 @@ def input_exercise_poll_failure(context):
|
||||
for key in ('helper_sha256', 'ownership_helper_sha256', 'prefix', 'profile', 'game_executable', 'report', 'command', 'nonce'):
|
||||
if report.get(key) != context[key]:
|
||||
return False
|
||||
if context.get('approach_requested') and report.get('approach_module_sha256') != context.get('approach_module_sha256'):
|
||||
return None
|
||||
if report.get('approach_requested', False) != context.get('approach_requested', False):
|
||||
return None
|
||||
if report.get('look_requested', False) != context.get('look_requested', False):
|
||||
return False
|
||||
return bool(report.get('error') or report.get('supervision_failed') is True or (
|
||||
@@ -928,6 +958,13 @@ def input_exercise_result(result, context, session, observation, display_result)
|
||||
for key in ('helper_sha256', 'ownership_helper_sha256', 'prefix', 'profile', 'game_executable', 'report', 'command', 'nonce'):
|
||||
if report.get(key) != context[key]:
|
||||
refuse('Input report binding mismatch: '+key)
|
||||
approach_requested = context.get('approach_requested', False)
|
||||
if type(report.get('approach_requested', False)) is not bool or report.get('approach_requested', False) != approach_requested:
|
||||
refuse('Prison approach request does not match launch context')
|
||||
if approach_requested and (report.get('approach_module_sha256') != context.get('approach_module_sha256') or context.get('approach_module_sha256') != approach_module_hashes(ROOT)):
|
||||
refuse('Approach module sources changed or differ from launch hashes')
|
||||
if not approach_requested and report.get('approach_proof') is not None:
|
||||
refuse('Unexpected approach evidence in fixed input run')
|
||||
look_requested = context.get('look_requested', False)
|
||||
if type(report.get('look_requested', False)) is not bool or report.get('look_requested', False) != look_requested:
|
||||
refuse('Relative look request does not match launch context')
|
||||
@@ -944,11 +981,15 @@ def input_exercise_result(result, context, session, observation, display_result)
|
||||
import math
|
||||
events = report.get('events', [])
|
||||
expected = [('W', True), ('W', False), ('Tab', True), ('Tab', False), ('Tab', True), ('Tab', False)]
|
||||
if approach_requested:
|
||||
if not isinstance(events, list) or not 2 <= len(events) <= 24 or len(events)%2:
|
||||
refuse('Prison approach requires bounded W pairs')
|
||||
expected = [('W', i%2 == 0) for i in range(len(events))]
|
||||
if (not isinstance(events, list) or len(events) != len(expected)
|
||||
or any(not isinstance(e, dict) or type(e.get('down')) is not bool or (e.get('key'), e['down']) != pair or e.get('finally') for e, pair in zip(events, expected))):
|
||||
refuse('Input experiment does not contain the exact fixed key pairs')
|
||||
times = [e.get('monotonic') for e in events]
|
||||
if any(type(t) not in {int, float} or not math.isfinite(t) or t < 0 for t in times) or times != sorted(times) or times[-1]-times[0] > 25:
|
||||
if any(type(t) not in {int, float} or not math.isfinite(t) or t < 0 for t in times) or times != sorted(times) or times[-1]-times[0] > (35 if approach_requested else 25):
|
||||
refuse('Input event clock is invalid or outside its bound')
|
||||
if not display_result or display_result.get('verified') is not True:
|
||||
refuse('Input experiment lacks verified private display ownership')
|
||||
@@ -958,15 +999,25 @@ def input_exercise_result(result, context, session, observation, display_result)
|
||||
refuse('Input owner does not match the independently observed private game window')
|
||||
native = report.get('observer', {})
|
||||
if (report.get('console') != str(session/'console.log')
|
||||
or native.get('events') != ['READY', 'INVENTORY_OPEN', 'INVENTORY_CLOSED']):
|
||||
or native.get('events') != (['READY'] if approach_requested else ['READY', 'INVENTORY_OPEN', 'INVENTORY_CLOSED'])):
|
||||
refuse('Input experiment lacks the exact native session/transition handshake')
|
||||
if (not observation or observation.get('nonce') != context['nonce'] or not observation.get('sampler_complete')
|
||||
or not observation.get('inventory_open_observed') or not observation.get('inventory_close_observed')
|
||||
or not observation.get('movement', {}).get('action_observed')
|
||||
or (not approach_requested and (not observation.get('inventory_open_observed') or not observation.get('inventory_close_observed')))
|
||||
or (not approach_requested and (not observation.get('movement', {}).get('action_observed')
|
||||
or not observation.get('movement', {}).get('physical_motion_observed')
|
||||
or not observation.get('movement', {}).get('released_input_observed')):
|
||||
or not observation.get('movement', {}).get('released_input_observed')))):
|
||||
refuse('Native movement and inventory open/close proof is incomplete')
|
||||
if look_requested:
|
||||
if approach_requested:
|
||||
from prison_approach_evidence import validate_approach_evidence
|
||||
route = report.get('approach_proof', {})
|
||||
route_events = route.get('events', [])
|
||||
if len(route_events) != len(events) or any(not isinstance(a, dict) or a.get('key') != b.get('key') or a.get('down') is not b.get('down') or type(a.get('monotonic')) not in (int, float) or not math.isfinite(a['monotonic']) or abs(a['monotonic']-b['monotonic']) > .01 for a,b in zip(route_events,events)):
|
||||
refuse('Approach adapter and route key evidence differ')
|
||||
proof['approach_proof'] = validate_approach_evidence((session/'console.log').read_text(errors='replace'), context['nonce'], report.get('look_proof'), route)
|
||||
proof['scope'] = 'private relative mouse and W input with independently observed interior prison approach; no held action, gate exit, full escape or multiplayer qualification'
|
||||
if proof['approach_proof'].get('verified') is not True:
|
||||
refuse('Independent native prison approach evidence is incomplete')
|
||||
elif look_requested:
|
||||
from ordinary_look_evidence import validate_look_evidence
|
||||
proof['look_proof'] = validate_look_evidence((session/'console.log').read_text(errors='replace'), context['nonce'], report.get('look_proof'), events)
|
||||
proof['scope'] = 'bounded private relative mouse and keyboard delivery with native view/movement/inventory evidence; no gate, full escape or multiplayer qualification'
|
||||
@@ -1013,7 +1064,7 @@ def run(a):
|
||||
|
||||
|
||||
def run_locked(a, steam, compat, profile, packed=None):
|
||||
command = command_for(ROOT, steam, compat, profile, a.project, packed, getattr(a, 'capture_prison_view', False))
|
||||
command = command_for(ROOT, steam, compat, profile, a.project, packed, getattr(a, 'capture_prison_view', False), 2560 if getattr(a, 'approach_wide_display', False) else 1280)
|
||||
separator = command.index("--")
|
||||
proton, executable = Path(command[separator+1]), Path(command[separator+3])
|
||||
if not proton.is_file() or not executable.is_file():
|
||||
@@ -1047,7 +1098,7 @@ def run_locked(a, steam, compat, profile, packed=None):
|
||||
command, display_context = display_probe_command(command, ROOT, compat, profile, executable, run_id, os.environ.get('DISPLAY', ''))
|
||||
exercise_context = None
|
||||
if getattr(a, 'exercise_player_input', False):
|
||||
command, exercise_context = input_exercise_command(command, ROOT, compat, profile, executable, run_id, os.environ.get('DISPLAY', ''), display_context, getattr(a, 'exercise_relative_look', False))
|
||||
command, exercise_context = input_exercise_command(command, ROOT, compat, profile, executable, run_id, os.environ.get('DISPLAY', ''), display_context, getattr(a, 'exercise_relative_look', False), getattr(a, 'exercise_prison_approach', False))
|
||||
silent_sink = 'erri_game_'+run_id
|
||||
application_id = 'org.escapistsrereintroduced.game.'+run_id
|
||||
env = os.environ.copy()
|
||||
@@ -1173,7 +1224,7 @@ def run_locked(a, steam, compat, profile, packed=None):
|
||||
'failure_observed_during_polling': monitor.first_failure_at is not None,
|
||||
'first_failure_elapsed_seconds': (monitor.first_failure_at - monitor.started) if monitor.first_failure_at is not None else None,
|
||||
'executable': str(executable), 'dedicated_server': False,
|
||||
'display': 'headless Gamescope', 'audio': '-noSound plus own silent sink',
|
||||
'display': 'headless Gamescope', 'private_viewport': {'width':2560 if getattr(a, 'approach_wide_display', False) else 1280,'height':720}, 'audio': '-noSound plus own silent sink',
|
||||
'control_only': a.project == CONTROL,
|
||||
'source_manifest_scope': 'none: packed input provenance is separate; loose source manifests cannot qualify this run' if packed else 'successor addon snapshot retained for provenance bookkeeping only; none loaded by vanilla control' if a.project == CONTROL else 'successor addon sources',
|
||||
'scope': final.get('scope', 'ordinary game compile/startup; no scripted fixture, full escape or multiplayer acceptance')}
|
||||
@@ -1211,6 +1262,8 @@ def main():
|
||||
parser.add_argument('--inspect-headless-display', action='store_true', help='Read-only ownership inspection within the hidden display; no input sent, loose Core/Chat only')
|
||||
parser.add_argument('--capture-prison-view', action='store_true', help='Opt-in single genuine local prison screenshot; loose successor source only, no camera or input changes')
|
||||
parser.add_argument('--observe-player-input', action='store_true', help='Read-only native local-input sampler; loose Core/Chat only, requires --settle >=50; sends no input')
|
||||
parser.add_argument('--approach-wide-display', action='store_true', help='Use a disposable 2560x720 headless viewport for the explicit approach experiment; no physical display changes')
|
||||
parser.add_argument('--exercise-prison-approach', action='store_true', help='Explicit bounded relative-mouse/W approach inside prison; requires relative look and gate observer; never operates gate')
|
||||
parser.add_argument('--observe-prison-gate', action='store_true', help='Read-only native registered prison lock/context/progress telemetry; requires --observe-player-input')
|
||||
parser.add_argument('--exercise-relative-look', action='store_true', help='Explicit tiny relative right/return mouse experiment before W/Tab; requires --exercise-player-input and native view proof')
|
||||
parser.add_argument('--exercise-player-input', action='store_true', help='Explicit bounded private W/Tab keyboard experiment; requires observer, display inspection, loose Core/Chat and settle >=50')
|
||||
|
||||
Reference in New Issue
Block a user