Observe native prison lock geometry and requalify Core loops

This commit is contained in:
2026-09-19 18:58:12 +02:00
parent e10b8799e9
commit b7fba094d7
39 changed files with 9884 additions and 12 deletions
+1 -1
View File
@@ -59,7 +59,7 @@ def check():
assert set(mapped)==set(inventory['script_paths']), 'Original inventory has untracked or unknown entries'
print(f'PASS: {len(mapped)} original script entries tracked across {len(feature_ids)} unfinished responsibilities')
print(f'PASS: {len(ids)} isolated addons, acyclic dependencies, {len(resources)} unique resources, exact vanilla references')
for name in ['test_ordinary_look_observer.py', 'test_ordinary_look_evidence.py', 'test_relative_look_runner.py', 'test_radio_waiting_text.py', 'test_inventory_control_runner.py', 'test_inventory_layout.py', 'test_headless_input_proof.py', 'test_headless_input_exercise_runner.py', 'test_private_prefix_cleanup.py', 'test_runner_private_cleanup.py', 'test_ordinary_input_runner.py', 'test_ordinary_input_observer.py', 'test_aircraft_route_envelope.py', 'test_aircraft_route_policy.py', 'test_aircraft_route_probe.py', 'test_rough_intel_feedback.py', 'test_headless_display_probe.py', 'test_headless_display_runner.py', 'test_preparation_status.py', 'test_hint_delivery.py', 'test_native_end_lifecycle.py', 'test_native_end_runner.py', 'test_escape_result_wording.py', 'test_native_stats.py', 'test_aircraft_translation.py', 'test_spawn_retry_budget.py', 'test_action_feedback.py', 'test_native_aircraft_control.py', 'test_aircraft_flight_policy.py', 'test_aircraft_flight_envelope.py', 'test_ordinary_screenshot.py', 'test_participant_selection.py', 'test_native_shared_map_ownership.py', 'test_shared_map_ownership.py', 'test_intel_registration.py', 'test_mortar_activity_completion.py', 'test_packed_chat_game.py', 'test_native_mortar_refresh.py', 'test_archive_packed.py', 'test_mortar_refresh.py', 'test_packed_core_game.py', 'test_pak_inventory.py', 'test_native_mortar_salvo.py', 'test_spectator_guidance.py', 'test_native_package.py', 'test_item_station_teardown.py', 'test_source_bundle.py', 'test_native_flight_footprint.py', 'test_native_flight_input_diagnostics.py', 'test_mortar_logistics.py', 'test_mortar_courier_storage.py','test_powered_flight_pad.py','test_prison_escape_policy.py','test_powered_flight_policy.py','test_flight_action_runner.py','test_native_local_admission_probe.py','test_mortar_report_ledger.py','test_dedicated_native.py','test_native_mortar_transfer.py','test_mortar_aim_renewal.py','test_dedicated_preflight.py','test_mortar_transactions.py','test_native_mortar_probe.py','test_diagnostic_no_splash.py','test_casualty_incapacitation.py','test_casualty_remove_probe.py','test_extraction_placement_policy.py','test_guard_cancellation_policy.py','test_casualty_ground_policy.py','test_casualty_reservation_death.py','test_casualty_loading_policy.py','test_initial_guard_policy.py','test_headless_game_runner.py','test_vanilla_game_control.py','test_archive_game_control.py','test_site_discovery_policy.py','test_faction_catalog_scope.py','test_mission_rules.py','test_admin_setting_policy.py', 'test_player_admission_policy.py','test_progression_identity.py','test_discovery_map_position.py','test_local_admission.py','test_prison_placement_policy.py', 'test_recovery_policy.py','test_time_policy.py','test_environment_policy.py','test_environment_restart_runner.py', 'test_inventory_enumeration_policy.py','test_radio_defence_policy.py','test_radio_feedback_policy.py','test_performance_comparison.py','test_release_check.py','test_settings_migration.py','hq_network_regression.py','helicopter_availability_regression.py','mortar_salvo_regression.py']:
for name in ['test_ordinary_gate_evidence.py', 'test_ordinary_gate_observer.py', 'test_ordinary_look_observer.py', 'test_ordinary_look_evidence.py', 'test_relative_look_runner.py', 'test_radio_waiting_text.py', 'test_inventory_control_runner.py', 'test_inventory_layout.py', 'test_headless_input_proof.py', 'test_headless_input_exercise_runner.py', 'test_private_prefix_cleanup.py', 'test_runner_private_cleanup.py', 'test_ordinary_input_runner.py', 'test_ordinary_input_observer.py', 'test_aircraft_route_envelope.py', 'test_aircraft_route_policy.py', 'test_aircraft_route_probe.py', 'test_rough_intel_feedback.py', 'test_headless_display_probe.py', 'test_headless_display_runner.py', 'test_preparation_status.py', 'test_hint_delivery.py', 'test_native_end_lifecycle.py', 'test_native_end_runner.py', 'test_escape_result_wording.py', 'test_native_stats.py', 'test_aircraft_translation.py', 'test_spawn_retry_budget.py', 'test_action_feedback.py', 'test_native_aircraft_control.py', 'test_aircraft_flight_policy.py', 'test_aircraft_flight_envelope.py', 'test_ordinary_screenshot.py', 'test_participant_selection.py', 'test_native_shared_map_ownership.py', 'test_shared_map_ownership.py', 'test_intel_registration.py', 'test_mortar_activity_completion.py', 'test_packed_chat_game.py', 'test_native_mortar_refresh.py', 'test_archive_packed.py', 'test_mortar_refresh.py', 'test_packed_core_game.py', 'test_pak_inventory.py', 'test_native_mortar_salvo.py', 'test_spectator_guidance.py', 'test_native_package.py', 'test_item_station_teardown.py', 'test_source_bundle.py', 'test_native_flight_footprint.py', 'test_native_flight_input_diagnostics.py', 'test_mortar_logistics.py', 'test_mortar_courier_storage.py','test_powered_flight_pad.py','test_prison_escape_policy.py','test_powered_flight_policy.py','test_flight_action_runner.py','test_native_local_admission_probe.py','test_mortar_report_ledger.py','test_dedicated_native.py','test_native_mortar_transfer.py','test_mortar_aim_renewal.py','test_dedicated_preflight.py','test_mortar_transactions.py','test_native_mortar_probe.py','test_diagnostic_no_splash.py','test_casualty_incapacitation.py','test_casualty_remove_probe.py','test_extraction_placement_policy.py','test_guard_cancellation_policy.py','test_casualty_ground_policy.py','test_casualty_reservation_death.py','test_casualty_loading_policy.py','test_initial_guard_policy.py','test_headless_game_runner.py','test_vanilla_game_control.py','test_archive_game_control.py','test_site_discovery_policy.py','test_faction_catalog_scope.py','test_mission_rules.py','test_admin_setting_policy.py', 'test_player_admission_policy.py','test_progression_identity.py','test_discovery_map_position.py','test_local_admission.py','test_prison_placement_policy.py', 'test_recovery_policy.py','test_time_policy.py','test_environment_policy.py','test_environment_restart_runner.py', 'test_inventory_enumeration_policy.py','test_radio_defence_policy.py','test_radio_feedback_policy.py','test_performance_comparison.py','test_release_check.py','test_settings_migration.py','hq_network_regression.py','helicopter_availability_regression.py','mortar_salvo_regression.py']:
subprocess.run([sys.executable,str(ROOT/'tests'/name)],check=True)
print('All repository checks passed. Native compilation and multiplayer tests are separate requirements.')
if __name__=='__main__':check()
+10 -3
View File
@@ -622,6 +622,8 @@ def display_probe_result(context, session):
def validate_input_observer(a):
if getattr(a, 'observe_prison_gate', False) and not getattr(a, 'observe_player_input', False):
refuse('Prison gate observation requires the existing input observer')
if not getattr(a, 'observe_player_input', False):
return
if a.project == CONTROL or getattr(a, 'packed_core', None) or getattr(a, 'packed_addons', None):
@@ -644,18 +646,22 @@ def validate_input_exercise(a):
refuse('Input exercise requires --settle >=50')
def input_observer_command(command, project, packed, nonce, look=False):
def input_observer_command(command, project, packed, nonce, look=False, gate=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 type(look) is not bool:
refuse('Relative look selection must be boolean')
if any(value.startswith(('-errOrdinaryInput', '-errOrdinaryLook')) for value in command):
if type(gate) is not bool:
refuse('Prison gate observation selection must be boolean')
if any(value.startswith(('-errOrdinaryInput', '-errOrdinaryLook', '-errOrdinaryGate')) for value in command):
refuse('Input observer command already has observer options')
options = ['-errOrdinaryInputObserve', nonce, '-errOrdinaryInputScreenshots', '1']
if look:
options += ['-errOrdinaryLookObserve', nonce]
if gate:
options += ['-errOrdinaryGateObserve', nonce]
return command + options
@@ -1035,7 +1041,7 @@ 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, getattr(a, 'exercise_relative_look', False))
command = input_observer_command(command, a.project, packed, observation_nonce, getattr(a, 'exercise_relative_look', False), getattr(a, 'observe_prison_gate', 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', ''))
@@ -1205,6 +1211,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('--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')
parser.add_argument('--timeout', type=int, default=180)
+206
View File
@@ -0,0 +1,206 @@
"""Read-only native gate telemetry parsing and geometry qualification.
These functions do not send input, select actions or establish that a prisoner
approached/unlocked the gate. Raw INPUT/LOOK observations supply all joins; helper
geometry estimates are not accepted. Process/display/session provenance remains
an independent runner responsibility.
"""
import math
import re
from ordinary_look_evidence import _native, _vector, _distance, _norm
PREFIX = 'ERR GATE OBSERVE: '
NUMBER = r'-?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?'
BOOL = r'(0|1|true|false)'
HEADER = re.compile(r'run=([A-Za-z0-9]{1,32}) sample=(\d+) real=(\d+) (.+)')
SAMPLE = re.compile(r'SAMPLE phase=(\d+) selected='+BOOL+r' context='+BOOL+
r' cachedPerformable='+BOOL+r' progressBound='+BOOL+r' performing='+BOOL+
r' progress=('+NUMBER+r') duration=('+NUMBER+r') pressed=('+NUMBER+r')')
GEOMETRY = re.compile(r'GEOMETRY body=(.+?) context=(.+?) radius=('+NUMBER+r') doorState=('+NUMBER+
r') doorControl=('+NUMBER+r') prison=(.+?) gate=(.+?) gateForward=(.+)')
CANCELLATIONS = {'explicit-scope-lost', 'original-prison-lock-unavailable', 'original-binding-changed',
'sample-or-clock-bound', 'native-player-or-prison-context-lost', 'invalid-native-action-sample'}
SCOPE = 'same-session native gate/input/camera telemetry and stable prison/lock geometry only; no approach, held-action completion, visual or multiplayer qualification'
def _require(condition, reason):
if not condition:
raise ValueError(reason)
def _nonce(nonce):
_require(type(nonce) is str and re.fullmatch(r'[A-Za-z0-9]{1,32}', nonce), 'invalid gate nonce')
def _finite(value, lower, upper, reason):
value = float(value)
_require(math.isfinite(value) and lower <= value <= upper, reason)
return value
def parse_gate_record(line, nonce):
"""Parse one native line. Unrelated -> None; invalid matching -> ValueError.
Parsing a line does not authenticate its sequence or native actor identity.
Use the full parser/join/validator before treating a snapshot as qualified.
"""
_nonce(nonce)
_require(type(line) is str, 'gate record is not text')
if PREFIX.rstrip() not in line:
return None
_require(PREFIX in line and line.count(PREFIX) == 1 and len(line) <= 4096, 'malformed gate prefix or line bound')
match = HEADER.fullmatch(line.split(PREFIX, 1)[1].strip())
_require(match is not None, 'malformed gate record')
actual, index, real, detail = match.groups()
_require(actual == nonce, 'foreign gate nonce')
index, real = int(index), int(real)
_require(1 <= index <= 450 and 0 <= real <= 2147483647, 'gate clock/sample outside bound')
record = {'sample': index, 'real_ms': real}
if detail == 'BASIS registered-prison-lock-native-interaction':
return {**record, 'kind': 'basis'}
if detail.startswith('CANCELLED '):
_require(detail[10:] in CANCELLATIONS, 'unknown gate cancellation')
return {**record, 'kind': 'cancelled', 'reason': detail[10:]}
match = SAMPLE.fullmatch(detail)
if match:
values = match.groups()
phase = int(values[0])
_require(phase in (1, 2), 'gate phase outside native observation contract')
flags = dict(zip(('selected', 'context_selected', 'cached_performable', 'progress_bound', 'performing'),
(v in ('1', 'true') for v in values[1:6])))
progress = _finite(values[6], 0, 60, 'invalid native action progress')
duration = _finite(values[7], 4, 4, 'native lock duration is not four seconds')
pressed = _finite(values[8], 0, 1, 'invalid native action input')
_require(not flags['cached_performable'] or flags['context_selected'], 'cached permission without original context')
_require(not flags['progress_bound'] or (flags['selected'] and flags['context_selected']), 'progress not bound to original action/context')
_require(flags['progress_bound'] or (not flags['performing'] and progress == 0), 'unbound native action progress')
return {**record, 'kind': 'sample', 'phase': phase, **flags,
'progress': progress, 'duration': duration, 'pressed': pressed}
match = GEOMETRY.fullmatch(detail)
if match:
body, context, radius, state, control, prison, gate, forward = match.groups()
forward = _vector(forward)
_require(.95 <= _norm(forward) <= 1.05 and math.hypot(forward[0], forward[2]) > .1,
'gate forward is not normalized or horizontal direction unusable')
radius = _finite(radius, 0, 10, 'native gate context radius outside bound')
_require(radius > 0, 'native gate context radius must be positive')
return {**record, 'kind': 'geometry', 'body': _vector(body), 'context': _vector(context), 'radius': radius,
'door_state': _finite(state, -1, 1, 'invalid native door state'),
'door_control': _finite(control, -1, 1, 'invalid native door control'),
'prison': _vector(prison), 'gate': _vector(gate), 'gate_forward': forward}
raise ValueError('unknown or malformed gate event')
def parse_gate_records(content, nonce):
"""Strict complete native gate sequence; returns records and paired snapshots.
Snapshot keys are integer sample indices. Incomplete final SAMPLE/GEOMETRY
pairs fail, so a live reader must buffer a partial pair before calling this.
"""
_nonce(nonce)
_require(type(content) is str and len(content) <= 32*1024*1024, 'gate console content outside bound')
records, snapshots = [], {}
basis = None; cancelled = None; pending = None
previous_index = previous_real = -1
for ordinal, line in enumerate(content.splitlines()):
record = parse_gate_record(line, nonce)
if record is None:
continue
_require(len(records) < 902, 'gate record count bound exceeded')
_require(cancelled is None, 'gate records after cancellation')
record['ordinal'] = ordinal
index, real, kind = record['sample'], record['real_ms'], record['kind']
_require(index >= previous_index and real >= previous_real, 'gate clocks or sample indices reversed')
previous_index, previous_real = index, real
if kind == 'basis':
_require(basis is None and not records and index == 1, 'duplicate or late native gate acknowledgement')
basis = record
elif kind == 'sample':
_require(basis is not None and pending is None and index == len(snapshots)+1, 'gate SAMPLE missing acknowledgement, duplicated or noncontiguous')
_require(index != 1 or real == basis['real_ms'], 'gate acknowledgement clock differs from first sample')
pending = record
elif kind == 'geometry':
_require(pending is not None and index == pending['sample'] and real == pending['real_ms'], 'gate GEOMETRY has no exact preceding SAMPLE')
snapshots[index] = {'sample': index, 'real_ms': real, 'action': pending, 'geometry': record}
pending = None
else:
_require(pending is None, 'gate cancellation interrupted a sample/geometry pair')
cancelled = record
records.append(record)
_require(basis is not None and snapshots and pending is None, 'missing native gate acknowledgement or complete geometry snapshots')
_require(snapshots[max(snapshots)]['real_ms']-basis['real_ms'] < 45000, 'gate observations exceed original lifetime')
return {'records': records, 'snapshots': snapshots, 'basis': basis, 'cancelled': cancelled}
def join_gate_snapshots(content, nonce):
"""Return raw gate snapshots joined with native input and camera samples.
Both native acknowledgements and INPUT READY are required. The full archived
console is expected, including the first samples, not a helper's metrics.
"""
gate = parse_gate_records(content, nonce)
poses, inputs, ready, look_cancelled = _native(content, nonce)
joined = {}
for index, snapshot in gate['snapshots'].items():
_require(index in inputs and index in poses, 'gate snapshot missing raw input or camera peer')
pose, sample = poses[index], inputs[index]
_require(snapshot['real_ms'] == pose['real_ms'] == sample['real_ms'], 'gate/input/camera timestamp mismatch')
# Gate and INPUT both read actor.GetOrigin. LOOK independently reads
# actor.GetTransform; the native18-44-37 walking capture differs by up
# to3cm at identical sample/clock. Keep that cross-API comparison bounded
# to5cm, while retaining the tighter same-API1cm agreement.
_require(_distance(snapshot['geometry']['body'], sample['origin']) <= .01 and
_distance(snapshot['geometry']['body'], pose['body']) <= .05, 'gate/input/camera body position mismatch')
joined[index] = {**snapshot, 'input': sample, 'look': pose}
return {**gate, 'snapshots': joined, 'ready': ready, 'look_cancelled': look_cancelled}
def validate_gate_geometry(content, nonce, start_sample=None, end_sample=None):
"""Qualify only telemetry geometry during an explicit inclusive sample span.
Defaults select READY through the last joined sample. Caller references are
indices into raw native records; no estimated position or metric is accepted.
"""
result = {'verified': False, 'errors': [], 'scope': SCOPE,
'approach_verified': False, 'held_action_verified': False}
try:
data = join_gate_snapshots(content, nonce)
snapshots = data['snapshots']; ready = data['ready']['sample']
if start_sample is None:
start_sample = ready
if end_sample is None:
end_sample = max(snapshots)
_require(type(start_sample) is int and type(end_sample) is int and
ready <= start_sample <= end_sample <= 450, 'invalid gate observation span')
_require(end_sample-start_sample >= 1, 'geometry stability requires at least two native samples')
_require(start_sample in snapshots and end_sample in snapshots, 'requested gate span is missing')
first = snapshots[start_sample]; initial = first['geometry']
for cancellation in (data['cancelled'], data['look_cancelled']):
_require(cancellation is None or cancellation['sample'] > end_sample, 'native context cancelled during requested gate span')
max_geometry_drift = 0.0
for index in range(start_sample, end_sample+1):
_require(index in snapshots, 'gate sample missing within requested span')
snapshot = snapshots[index]; geometry = snapshot['geometry']; action = snapshot['action']
_require(action['phase'] == 1, 'requested geometry span is not entirely PRISON phase')
_require(not snapshot['input']['inventory'], 'inventory menu open during requested gate span')
if index > start_sample:
_require(0 < snapshot['real_ms']-snapshots[index-1]['real_ms'] <= 500, 'gate sampling gap or nonadvancing clock')
for field in ('prison', 'gate', 'context'):
drift = _distance(geometry[field], initial[field]); max_geometry_drift = max(max_geometry_drift, drift)
_require(drift <= .01, 'native '+field+' geometry changed during requested span')
_require(_distance(geometry['gate_forward'], initial['gate_forward']) <= .001, 'native gate orientation changed')
_require(abs(geometry['radius']-initial['radius']) <= .001, 'native action context radius changed')
_require(abs(geometry['door_state']-initial['door_state']) <= .001 and
abs(geometry['door_control']-initial['door_control']) <= .001, 'native door state/control changed')
last = snapshots[end_sample]
result.update(verified=True, start_sample=start_sample, end_sample=end_sample,
native_duration_ms=last['real_ms']-first['real_ms'], snapshot_count=end_sample-start_sample+1,
geometry_max_drift_metres=max_geometry_drift, prison=list(initial['prison']),
gate=list(initial['gate']), context=list(initial['context']), radius=initial['radius'],
gate_forward=list(initial['gate_forward']),
initial_context_distance_metres=_distance(first['geometry']['body'], initial['context']),
final_context_distance_metres=_distance(last['geometry']['body'], initial['context']))
except (ValueError, TypeError, KeyError, OverflowError) as error:
result['errors'].append(str(error))
return result