Verify private relative mouse input against native player camera
This commit is contained in:
+40
-8
@@ -631,6 +631,8 @@ def validate_input_observer(a):
|
||||
|
||||
|
||||
def validate_input_exercise(a):
|
||||
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):
|
||||
return
|
||||
if (a.project not in {'core', 'chat'} or getattr(a, 'packed_core', None)
|
||||
@@ -642,14 +644,19 @@ def validate_input_exercise(a):
|
||||
refuse('Input exercise requires --settle >=50')
|
||||
|
||||
|
||||
def input_observer_command(command, project, packed, nonce):
|
||||
def input_observer_command(command, project, packed, nonce, look=False):
|
||||
if project not in {'core', 'chat'} or packed is not None:
|
||||
refuse('Input observation requires loose Core or Chat')
|
||||
if not re.fullmatch(r'[A-Za-z0-9]{1,32}', nonce):
|
||||
refuse('Input observer nonce must contain 1..32 ASCII letters/digits')
|
||||
if any(value.startswith('-errOrdinaryInput') for value in command):
|
||||
if type(look) is not bool:
|
||||
refuse('Relative look selection must be boolean')
|
||||
if any(value.startswith(('-errOrdinaryInput', '-errOrdinaryLook')) for value in command):
|
||||
refuse('Input observer command already has observer options')
|
||||
return command + ['-errOrdinaryInputObserve', nonce, '-errOrdinaryInputScreenshots', '1']
|
||||
options = ['-errOrdinaryInputObserve', nonce, '-errOrdinaryInputScreenshots', '1']
|
||||
if look:
|
||||
options += ['-errOrdinaryLookObserve', nonce]
|
||||
return command + options
|
||||
|
||||
|
||||
def input_observer_evidence(content, nonce):
|
||||
@@ -819,7 +826,7 @@ 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):
|
||||
def input_exercise_command(command, root, compat, profile, executable, run_id, host_display, display_context, look=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':
|
||||
@@ -830,6 +837,14 @@ 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(look) is not bool:
|
||||
refuse('Relative look selection must be boolean')
|
||||
look_options = [i for i,value in enumerate(child) if value.startswith('-errOrdinaryLook')]
|
||||
if look:
|
||||
if len(look_options) != 1 or child[look_options[0]] != '-errOrdinaryLookObserve' or child[look_options[0]+1:look_options[0]+2] != [run_id]:
|
||||
refuse('Relative look requires its exact native observer nonce')
|
||||
elif look_options:
|
||||
refuse('Unexpected native relative-look observer')
|
||||
report = profile/('input-proof-'+run_id+'.json')
|
||||
if helper.is_symlink() or not helper.is_file() or report.exists() or report.is_symlink():
|
||||
refuse('Input exercise requires a regular helper and a new report')
|
||||
@@ -840,6 +855,9 @@ def input_exercise_command(command, root, compat, profile, executable, run_id, h
|
||||
wrapper = [sys.executable, str(helper), '--enable-private-keyboard-proof', '--host-display', host_display,
|
||||
'--prefix', context['prefix'], '--profile', str(profile), '--game-executable', str(executable),
|
||||
'--report', str(report), '--nonce', run_id, '--']
|
||||
if look:
|
||||
context['look_requested'] = True
|
||||
wrapper.insert(-1, '--enable-private-look-proof')
|
||||
return command[:separator+1] + wrapper + child, context
|
||||
|
||||
|
||||
@@ -861,6 +879,8 @@ 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 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 (
|
||||
report.get('sequence_completed') is False
|
||||
and report.get('whole_process_group_cleanup_verified') is False
|
||||
@@ -902,6 +922,11 @@ 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)
|
||||
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')
|
||||
if not look_requested and report.get('look_proof') is not None:
|
||||
refuse('Unexpected relative mouse evidence in keyboard-only run')
|
||||
if type(report.get('input_sent')) is bool:
|
||||
proof['input_sent'] = report['input_sent']
|
||||
if (report.get('schema') != 1 or report.get('kind') != 'headless-private-keyboard-experiment'
|
||||
@@ -909,7 +934,7 @@ def input_exercise_result(result, context, session, observation, display_result)
|
||||
or report.get('hard_supervision') is not True or report.get('supervision_failed')
|
||||
or report.get('all_attempted_presses_released') is not True
|
||||
or report.get('error') or report.get('paired_release_errors') != [] or report.get('unconfirmed_held_key') is not None):
|
||||
refuse('Private keyboard sequence did not complete with confirmed paired release')
|
||||
refuse('Private input sequence did not complete with confirmed paired release')
|
||||
import math
|
||||
events = report.get('events', [])
|
||||
expected = [('W', True), ('W', False), ('Tab', True), ('Tab', False), ('Tab', True), ('Tab', False)]
|
||||
@@ -935,6 +960,12 @@ def input_exercise_result(result, context, session, observation, display_result)
|
||||
or not observation.get('movement', {}).get('physical_motion_observed')
|
||||
or not observation.get('movement', {}).get('released_input_observed')):
|
||||
refuse('Native movement and inventory open/close proof is incomplete')
|
||||
if 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'
|
||||
if proof['look_proof'].get('verified') is not True:
|
||||
refuse('Independent native relative-look evidence is incomplete')
|
||||
proof['verified'] = True
|
||||
except (OSError, ValueError, TypeError, AttributeError, KeyError) as error:
|
||||
proof['error'] = str(error)
|
||||
@@ -1004,13 +1035,13 @@ def run_locked(a, steam, compat, profile, packed=None):
|
||||
run_id = uuid.uuid4().hex
|
||||
observation_nonce = run_id if getattr(a, 'observe_player_input', False) else None
|
||||
if observation_nonce:
|
||||
command = input_observer_command(command, a.project, packed, observation_nonce)
|
||||
command = input_observer_command(command, a.project, packed, observation_nonce, getattr(a, 'exercise_relative_look', False))
|
||||
display_context = None
|
||||
if getattr(a, 'inspect_headless_display', False):
|
||||
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)
|
||||
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))
|
||||
silent_sink = 'erri_game_'+run_id
|
||||
application_id = 'org.escapistsrereintroduced.game.'+run_id
|
||||
env = os.environ.copy()
|
||||
@@ -1127,7 +1158,7 @@ def run_locked(a, steam, compat, profile, packed=None):
|
||||
if exercise_context:
|
||||
result, exercise_result = input_exercise_result(result, exercise_context, session, observation_result, display_result)
|
||||
final['input_exercise'] = exercise_result
|
||||
final['scope'] = 'ordinary game startup with an explicitly requested bounded private keyboard experiment; see input_exercise for sent-input and native-proof results; no full escape, multiplayer or FPS qualification'
|
||||
final['scope'] = 'ordinary game startup with an explicitly requested bounded private input experiment; see input_exercise for sent-input and native-proof results; no full escape, multiplayer or FPS qualification'
|
||||
source_bindings = {} if packed else bindings(captured)
|
||||
summary = {**sealed, **source_bindings, 'started_unix': started, 'result': result,
|
||||
'session': str(session) if session else None, 'project': a.project,
|
||||
@@ -1174,6 +1205,7 @@ 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('--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')
|
||||
parser.add_argument('--timeout', type=int, default=180)
|
||||
parser.add_argument('--settle', type=int, default=15)
|
||||
|
||||
Reference in New Issue
Block a user