"""Offline checks of the pinned experimental engine and shipped local patch. Reads a local vanilla/patched checkout. Applies the patch only to a temporary projection and generates the real ARM64 Ninja graph without fetching tools, compiling a game, using retail data, or connecting a headset. """ from __future__ import annotations import argparse import io import json from pathlib import Path, PurePosixPath import re import subprocess import sys import tarfile import tempfile import xml.etree.ElementTree as ET ROOT = Path(__file__).resolve().parents[1] SOURCE_COMMIT = "2ae0ee4e3e8a4dfdadfd528a5b085ca699fc9ea4" PATCH_FILES = frozenset(( "port/linux/src/vr.c", "port/linux/src/vr.h", "port/linux/src/vr_locomotion.h", "port/linux/src/port_config.c", "source/game/player_control.c", "tools/test_vr_locomotion.py", "port/linux/game/menu_functions.c", "port/linux/game/vr_render.c", "port/linux/src/d3d8_gl.c", "port/linux/src/nv2a_psh.c", "port/linux/src/xgpu.h", "source/rasterizer/xbox/rasterizer_xbox_lights.c", "tools/test_online_coop_menu.py", "tools/test_vr_sun_glow.py", "source/items/weapons.c", "source/items/weapons.h", "source/game/aim_assist.c", "source/game/aim_assist.h", "tools/test_vr_projectile_reticle.py", "port/assets/menus/ce/main_menu.multiplayer_type_select.coop.xml", "tools/port_settings.py", "source/units/units.c", "source/units/units.h", "source/hs/hs_library_external.c", "port/linux/arm64/host_vr.c", "tools/test_vr_tutorial.py", "tools/test_vr_tracking.py", "tools/test_vr_tutorial_buttons.py", "source/objects/object_lights.c", "tools/test_vr_flashlight.py", )) class EngineCheckError(RuntimeError): pass def require(condition, message): if not condition: raise EngineCheckError(message) def git(source, *arguments, check=True): result = subprocess.run(["git", "-C", str(source), *arguments], capture_output=True, timeout=30) if check: require(result.returncode == 0, "A local Git source check failed; no engine files were changed.") return result def patch_files(text): files = re.findall(r"^diff --git a/(\S+) b/(\S+)$", text, re.M) require(bool(files), "The local engine patch has no ordinary file diffs.") for before, after in files: require(before == after and after in PATCH_FILES, "The engine patch changes a path outside the reviewed integration files.") return frozenset(after for _, after in files) def c_function(text, name): # Keep offsets while hiding comments and string contents from brace counting. comments = re.compile(r"/\*.*?\*/|//[^\n]*", re.S) clean = comments.sub(lambda match: " " * len(match.group()), text) masked = re.sub(r'"(?:\\.|[^"\\])*"', lambda match: " " * len(match.group()), clean) start = re.search(r"\b" + re.escape(name) + r"\s*\([^;{}]*\)\s*\{", masked) require(start is not None, "Missing engine function: " + name) depth = 1 for offset in range(start.end(), len(masked)): depth += (masked[offset] == "{") - (masked[offset] == "}") if not depth: return clean[start.end():offset] raise EngineCheckError("Unterminated engine function: " + name) def check_texture_binding(text): body = c_function(text, "bind_textures") loops = list(re.finditer(r"for\s*\(stage\s*=\s*0;\s*stage\s*<\s*D3DTSS_MAXSTAGES;\s*stage\+\+\)", body)) bindings = list(re.finditer(r"\bstate_texture\s*\(", body)) require(len(loops) == 2 and len(bindings) == 1 and bindings[0].start() > loops[1].start(), "Texture binding is not delayed until the second complete stage pass.") resolve = body.find("xgpu_texture_get(") require(loops[0].start() < resolve < loops[1].start(), "Texture uploads must finish before the final stage-binding pass.") require("state_texture(stage, gl_targets[stage], gl_textures[stage])" in body, "Final texture bindings do not use the resolved per-stage arrays.") def check_upstream(source): read = lambda path: (source / path).read_text(encoding="utf-8") limits = read("port/linux/include/halo_port_limits.h") require(re.search(r"^#define HALO_PORT_NETWORK_VERSION 17\s*$", limits, re.M), "The candidate must use native peer protocol 17.") check_texture_binding(read("port/linux/src/d3d8_gl.c")) shader = read("port/linux/src/nv2a_vsh.c") require("clip_position = oPos" in shader and "clip_captured = true" in shader and "if (!(abs(position.w) > 0.0))" in shader and "position = vec4(0.0, 0.0, 0.0, -1.0)" in shader, "The pinned vertex clip-position safeguards are missing.") distributed = read("port/linux/game/network_distributed.c") coop = read("port/linux/game/network_coop.c") for hook in ("network_coop_host_tick", "network_coop_client_tick", "network_coop_handle_presentation", "network_coop_handle_events"): require(hook + "(" in distributed and hook in coop, "Missing integrated co-op hook: " + hook) config = read("port/linux/src/port_config.c") require('"network.coop_enemies_mode"' in config and '"network.coop_enemies"' in config and '"network.brokers_file"' in config, "Co-op/broker configuration is missing.") require((source / "port/assets/network/brokers.txt").is_file(), "The candidate broker list is missing.") require("saved games of builds before it no longer load" in read("port/linux/include/halo_port_capacity.h"), "The candidate saved-game layout change needs explicit compatibility handling.") def check_controls(source): read = lambda path: (source / path).read_text(encoding="utf-8") vr = read("port/linux/src/vr.c") config = read("port/linux/src/port_config.c") require(re.search(r'"vr\.movement",\s*_config_string,\s*"\\"head\\"",\s*"HALO_VR_MOVEMENT"', config), "Head-directed walking is not the configurable default.") movement = c_function(vr, "halo_vr_locomotion") for guard in ("!vr.initialized", "!settings.head_movement", "!settings.controller_aim", "settings.gamepad_aim"): require(guard in movement, "Missing unchanged-input mode guard: " + guard) for guard in ("vr.views.focused", "vr.views.head.valid", "vr.origin_valid", "vr.aim_synced", "!vr.menus", "halo_vr_locomotion_orientation"): require(guard in movement, "Missing valid-tracking locomotion guard: " + guard) require("halo_vr_locomotion_project(" in movement, "The tested production movement math is not wired into VR input.") player = read("source/game/player_control.c") handling = c_function(player, "handle_one_player_input") call = handling.find("halo_vr_locomotion(player->desired_angles.yaw") require(call >= 0 and handling.find("player->desired_angles.yaw = vr_yaw") < call < handling.find("player->throttle = input.throttle"), "Movement must be transformed after aiming and before the player/action throttle is stored.") guard = handling[max(0, call - 300):call] for condition in ("local_player_index == 0", "unit->object.parent_object_index == NONE", "!director_inhibited_facing", "!cinematic_in_progress"): require(condition in guard, "Missing local on-foot movement gate: " + condition) gamepad = c_function(vr, "halo_vr_gamepad") require("XINPUT_GAMEPAD_WHITE, buttons & VR_BUTTON_RIGHT_BUMPER" in gamepad and "XINPUT_GAMEPAD_BLACK, buttons & VR_BUTTON_LEFT_BUMPER" in gamepad, "The agreed Xbox bumper layout was not preserved.") require("halo_vr_tutorial_look(" in c_function(player, "player_control_action_test_note"), "The physical-head tutorial hook was not ported to the new co-op action test.") c_function(vr, "halo_vr_tutorial_look") require((source / "port/linux/src/vr_locomotion.h").is_file(), "The production locomotion header is missing.") def check_stereo_glow_and_online_menu(source): read = lambda path: (source / path).read_text(encoding="utf-8") lights = read("source/rasterizer/xbox/rasterizer_xbox_lights.c") draw = c_function(lights, "rasterizer_sun_glow_draw") require("vr_render_sun_glow_begin(" in draw and "vr_render_sun_glow_end(" in draw, "The per-eye sun-glow wrapper is not integrated into the actual renderer.") require('config_real("vr.sun_glow_strength")' in draw and 'rasterizer_sun_glow_draw_internal(flare, 1.0f)' in draw, "The VR sun-strength option must preserve the original flat-screen composite.") strength = c_function(lights, "rasterizer_vr_sun_glow_strength") require("isfinite(configured)" in strength and "configured < 0.0" in strength and "configured > 1.0" in strength and "return 0.5f" in strength, "Sun-glow intensity must reject non-finite values and stay within 0–1.") require('"vr.sun_glow_strength", _config_real, "0.5"' in read("port/linux/src/port_config.c"), "The bounded half-strength sun-glow default is missing.") renderer = read("port/linux/game/vr_render.c") begin = c_function(renderer, "vr_render_sun_glow_begin") for integration in ("vr_render.eye_cameras[eye]", "vr_render.eyes[eye]", "halo_vr_screen_space(1)", "halo_vr_draw_eye(eye)"): require(integration in begin, "Missing per-eye glow state: " + integration) require("halo_vr_screen_space(0)" in c_function(renderer, "vr_render_sun_glow_end"), "Glow screen-space state is not restored.") backend = c_function(read("port/linux/src/d3d8_gl.c"), "prepare_draw") require(0 <= backend.find("key.sample_eye") < backend.find("fragment_shader_get(&key)"), "Source-eye selection must participate in the actual fragment cache key.") shader = read("port/linux/src/nv2a_psh.c") require("key->multiview == XGPU_MULTIVIEW_NONE && key->sample_eye" in shader, "A mono glow scratch target does not sample the selected stereo source eye.") menu = read("port/linux/game/menu_functions.c") route = c_function(menu, "cooperative_begin") routes = { "coop_host_lan": "cooperative_network_host(widget, event, controller, _multiplayer_mode_host_lan, widget_deleted)", "coop_host_online": "cooperative_network_host(widget, event, controller, _multiplayer_mode_host_internet, widget_deleted)", "coop_join_lan": "cooperative_network_join(widget, controller, _multiplayer_mode_lan, widget_deleted)", "coop_join_online": "cooperative_network_join(widget, controller, _multiplayer_mode_server_browser, widget_deleted)", "coop_join_invite": "cooperative_network_join(widget, controller, _multiplayer_mode_direct_link, widget_deleted)", } for name, call in routes.items(): require(re.search(r'if \(!strcmp\(widget->name, "' + name + r'"\)\)\s*return ' + re.escape(call) + r';', route), "Missing LAN/Internet campaign dispatch: " + name) require("cooperative_session_cleanup();" in route and '"network_screen"' in route, "Leaving the campaign network chooser must clean up its pending session.") single_player = c_function(menu, "cooperative_single_player") for integration in ("player_spawn_count = 1", "player_ui_reset_single_player_local_player_controllers()", "lobby_join_reset()", "player_ui_local_player_left_multiplayer_game(controller)"): require(integration in single_player, "Missing one-player campaign reset: " + integration) host = c_function(menu, "cooperative_network_host") require('mode == _multiplayer_mode_host_internet && !config_boolean("network.online")' in host and "multiplayer_host_mode(widget, event, controller, mode, TRUE, widget_deleted)" in host and "cooperative_map_opening = TRUE" in host and "cooperative_map_opening = FALSE" in host, "Campaign hosts must distinguish offline LAN from Internet and open campaign maps.") join = c_function(menu, "cooperative_network_join") require('mode != _multiplayer_mode_lan && !config_boolean("network.online")' in join and "cooperative_network_game = TRUE" in join and "multiplayer.mode = mode" in join, "Campaign joins must distinguish LAN, Internet browser and invite modes.") handler = c_function(menu, "pc_menu_event_function_invoke") require(re.search(r'else if \(!strcmp\(name, "port coop begin"\)\)\s*\{\s*return cooperative_begin\(', handler), "Campaign dispatch must work in both flat and VR builds.") require(re.search(r'else if \(!strcmp\(name, "port coop player 2 list initialize"\)[^{]+\{\s*return campaign_fail\(\);', handler) and "player_spawn_count = 2;" not in menu and "vr_cooperative_begin" not in menu, "Stale split-screen campaign routes must be rejected.") for helper in ("lobby_add_player", "lobby_join_start", "lobby_player_choose", "preview_add"): body = c_function(menu, helper) require("lobby_campaign_single_player()" in body and "return campaign_local_player_block();" in body, "Campaign must block a second local player: " + helper) require("lobby_local_player_count() > 1" in c_function(menu, "map_list_choose") and "cooperative_single_player()" in c_function(menu, "multiplayer_game_player"), "Ordinary map/browser entry points must also use one local campaign player.") require('!strcmp(name, "gamespy back handler") && cooperative_network_game &&' in handler and "!global_network_game_server_get()" in handler, "Browser cancellation must preserve a host when returning from Server Setup.") asset = source / "port/assets/menus/ce/main_menu.multiplayer_type_select.coop.xml" nodes = {node.attrib["name"].rsplit("/", 1)[-1]: node for node in ET.parse(asset).getroot()} require("player_2_profile_screen" not in nodes and "player_2_profile_list" not in nodes, "The campaign menu must not open a second-player profile screen.") for event in ("a", "start"): item = nodes["multiplayer_type_coop_item"].find('on[@event="' + event + '"]') require(item is not None and item.get("run") == "port coop begin" and item.get("open", "").endswith("coop/network_screen"), "The campaign button must open the LAN/Internet chooser.") captions = {"coop_host_lan": "HOST LOCAL (LAN)", "coop_join_lan": "JOIN LOCAL (LAN)", "coop_host_online": "HOST ONLINE (INTERNET)", "coop_join_online": "JOIN ONLINE (INTERNET)", "coop_join_invite": "JOIN INVITE LINK"} for name, caption in captions.items(): require(name in nodes and nodes[name].get("text") == caption, "Missing campaign menu choice: " + caption) for event in ("a", "start"): item = nodes[name].find('on[@event="' + event + '"]') require(item is not None and item.attrib == {"event": event, "run": "port coop begin"}, "Campaign choices must dispatch their named route without a static widget fallback.") generator = read("tools/port_settings.py") generated_coop = generator.split("def _coop() -> list:", 1)[1].split("def _item_options_extras", 1)[0] require(all(name in generated_coop for name in captions) and "network_screen" in generated_coop and "player_2_profile_screen" not in generated_coop, "The menu generator must retain the network-only campaign choices.") for script in ("tools/test_vr_sun_glow.py", "tools/test_online_coop_menu.py"): require((source / script).is_file(), "Missing production C regression: " + script) def check_nominal_projectile_reticle(source): read = lambda path: (source / path).read_text(encoding="utf-8") weapons = read("source/items/weapons.c") firing = c_function(weapons, "trigger_create_projectiles") require(re.search(r"trigger_adjust_projectile_ray\([^;]+&error,\s*FALSE,\s*NULL\)", firing), "Real shots must retain the shared firing calculation with target recording.") preview = c_function(weapons, "weapon_preview_projectile_ray") require(re.search(r"trigger_adjust_projectile_ray\([^;]+&error,\s*TRUE,\s*pose\)", preview), "The reticle must use the same pre-spread calculation in preview mode.") require(not re.search(r"\b(?:random|projectile_new|weapon_trigger_fire)\w*\s*\(", preview), "Reticle preview must not fire or consume shot randomness.") assist = read("source/game/aim_assist.c") require("player_aim_projectile_internal(player_index, position, direction, FALSE, pose)" in c_function(assist, "player_preview_projectile_aim"), "Reticle preview must suppress player targeting metadata writes.") require("player_aim_projectile_internal(player_index, position, direction, TRUE, NULL)" in c_function(assist, "player_aim_projectile"), "Actual shots must preserve player targeting metadata writes.") renderer = read("port/linux/game/vr_render.c") crosshairs = c_function(renderer, "vr_render_crosshairs") point = c_function(renderer, "vr_render_projectile_point") require("vr_render_projectile_point(" in crosshairs and "vr_render_projectile_ray(" in point and "_collision_test_for_projectiles_flags" in point, "The rendered reticle must trace the game's nominal projectile ray.") require("MAXIMUM_COLLISION_USER_STACK_DEPTH - 2" in point, "The reticle must reserve capacity for UI, aim-assist and nested line-of-sight contexts.") require("weapon_preview_projectile_ray(weapon_index, 0, player_index" in c_function(renderer, "vr_render_projectile_ray"), "The reticle must query the equipped primary weapon's actual firing ray.") require("crosshair_inverse_distance" not in crosshairs and "CROSSHAIR_SMOOTHING" not in crosshairs, "Reticle depth must not lag behind its current collision hit.") require((source / "tools/test_vr_projectile_reticle.py").is_file(), "The production projectile-reticle regression is missing.") pose = c_function(renderer, "vr_render_pointing_pose") for guard in ("local_player_get_player_index(0)", "_object_type_biped", "parent_object_index != NONE", "gunner_object_index != NONE", "_director_perspective_first_person", "director_inhibited_facing(0)", "aiming_velocity_maximum != 0.0f", "aiming_acceleration_maximum != 0.0f"): require(guard in pose, "Missing guarded render-rate preview state: " + guard) for integration in ("view->hand_forward", "player_control_get_facing_direction(0", "DEGREES_TO_RADIANS(85.0f)", "unit_clip_to_aiming_bounds(", "render_interpolation_camera(0, observer_get_camera(0))"): require(integration in pose, "Missing current-frame preview input: " + integration) require("hand_position" not in pose and "camera_effects" not in pose, "Preview origin must not add hand translation or apply camera effects again.") require("vr_render_pointing_pose(player_index, unit, &vr_render.view, vr_render.base_yaw, pose)" in c_function(renderer, "vr_render_projectile_pose"), "The equipped reticle must retain the current render pose and yaw.") unarmed = c_function(renderer, "vr_render_unarmed_ray") require("unit_get_weapon_count(unit_index) != 0" in unarmed and "_object_dead_bit" in unarmed and "unit->unit.player_index != player_index" in unarmed and "unit_inventory_get_weapon" in unarmed and "vr_render_pointing_pose(" in unarmed, "An unarmed reticle must be limited to a living owned player without an equipped weapon.") ray = c_function(renderer, "vr_render_projectile_ray") require("vr_render_projectile_pose(player_index, unit, &pose) ? &pose : NULL" in ray, "The current-frame pose is not integrated into the primary reticle preview.") units = read("source/units/units.c") require("adjust_origin, use_aiming_vector, NULL, NULL" in c_function(units, "unit_adjust_projectile_ray"), "Actual unit shots must not use presentation overrides.") require("unit_adjust_projectile_ray_internal(" in c_function(units, "unit_preview_projectile_ray"), "Unit preview must share the original camera/muzzle projection math.") def check_unarmed_flashlight(source): read = lambda path: (source / path).read_text(encoding="utf-8") renderer = read("port/linux/game/vr_render.c") pose = c_function(renderer, "vr_render_player_flashlight_pose") for guard in ("!vr_render.active", "!vr_render.weapon_camera_set", "!vr_render.weapon_camera_valid", "render.local_player_index != 0", "director_camera_scripted && *director_camera_scripted", "!halo_vr_aiming()", "!halo_vr_gaze_tracking_valid()", "vr_render_unarmed_ray("): require(guard in pose, "Missing unarmed flashlight render-pose guard: " + guard) require("vr_render.weapon_camera.position" in pose and "vr_render.weapon_camera.up" in pose and "dot_product3d(" in pose and "normalize3d(" in pose and "halo_vr_view(" not in pose, "The flashlight must use the cached rendered hand pose with a perpendicular up vector.") lights = read("source/objects/object_lights.c") override = c_function(lights, "light_vr_unarmed_flashlight_pose") for guard in ("#ifdef HALO_VR", "light->parent_light_index == NONE", "light->object_index != NONE", "_point_light_dynamic_bit", "_light_definition_is_first_person_flashlight_bit", "vr_render_player_flashlight_pose("): require(guard in override, "Missing local flashlight light-definition guard: " + guard) bounds = c_function(lights, "light_compute_render_bounding_sphere") require("struct light_datum effective = *light_get(light_index)" in bounds and "&effective.position" in bounds and "&effective.forward" in bounds and "light_compute_bounding_sphere_from_basis(&effective" in bounds, "Render flashlight bounds must use a copy of the simulation light.") simulation = c_function(lights, "light_compute_bounding_sphere") reconnect = c_function(lights, "light_reconnect_to_map") require("light_compute_bounding_sphere_from_basis(light_get(light_index)" in simulation and "light_compute_render_bounding_sphere(" not in reconnect and "light_vr_unarmed_flashlight_pose(" not in reconnect, "Flashlight presentation must not move the simulation light's BSP partition.") include = c_function(lights, "lights_vr_include_unarmed_flashlights") require("light_unmarked(light_index)" in include and "object_get_function_value(" in include and "intensity <= 0.0f" in include and "slot < MAXIMUM_RENDERED_LIGHTS" in include and "MAXIMUM_RENDERED_LIGHTS - 1" in include, "Adding a local flashlight outside stale visibility must respect scene-list capacity.") preprocess = c_function(lights, "lights_preprocess_scene") require(0 <= preprocess.find("structure_visibility_find_objects(") < preprocess.find("lights_vr_include_unarmed_flashlights()") < preprocess.find("light_marker_end()"), "The current flashlight must be included before finishing scene-light visibility.") require("light_vr_unarmed_flashlight_pose(light, &light_parameters.position" in preprocess and "first_person_weapon_center_flashlight(" in preprocess, "The local unarmed override must be integrated without replacing armed flashlight markers.") for name in ("light_get_bounding_sphere", "lights_render_diffuse", "lights_render_specular"): require("light_compute_render_bounding_sphere(" in c_function(lights, name), "Flashlight visibility and drawing must share rendered bounds: " + name) for name in ("lights_render_diffuse", "lights_render_specular"): require("light_render_skips_clusters(light)" in c_function(lights, name), "The unarmed beam must not use stale tick cluster restrictions: " + name) require((source / "tools/test_vr_flashlight.py").is_file(), "Missing production C flashlight regression.") def check_tutorial(source): read = lambda path: (source / path).read_text(encoding="utf-8") player = read("source/game/player_control.c") require("halo_vr_turn_control(" in c_function(player, "handle_one_player_input") and "halo_vr_tutorial_reset(" in c_function(player, "player_control_action_test_reset") and "halo_vr_tutorial_turn(" in c_function(player, "player_control_action_test_note"), "The script action tests must share actual VR turns and reset their head anchor.") buttons = c_function(player, "player_control_action_test_check_reset_input_blob") require("halo_vr_running()" in buttons and "halo_vr_active" not in buttons and "vr_controls" in buttons and "_unit_control_use_equipment_bit, FALSE" in c_function(player, "player_control_action_test_check_reset_input_blob"), "A consumed VR tutorial Back button must suppress its mapped melee action.") vr = read("port/linux/src/vr.c") c_function(vr, "halo_vr_running") require("int halo_vr_running(void);" in read("port/linux/src/vr.h"), "The tutorial VR-running query must use the real engine API.") units = c_function(read("source/units/units.c"), "unit_can_see_point") require("vr_render_player_gaze(" in units and "local_player_index == 0" in units and "_object_dead_bit" in units, "Headset gaze must be limited to the living local primary player.") hs = read("source/hs/hs_library_external.c") panel = c_function(hs, "hs_vr_calibration_panel") require("degrees != 5.f" in panel and "levels\\\\a10\\\\a10" in panel and "_object_type_scenery" in panel, "Hand-reticle acceptance must retain the original named a10 panel gate.") require("hs_vr_calibration_panel(" in c_function(hs, "hs_unit_can_see_object") and "vr_render_player_pointing_ray(" in c_function(hs, "hs_unit_can_see_object"), "The unarmed presentation ray must be integrated into the panel check.") locate = c_function(read("port/linux/arm64/host_vr.c"), "host_vr_locate") require(0 <= locate.find("views->head.valid = 0") < locate.find("if (!vr.frame_begun)") and "XR_VIEW_STATE_POSITION_VALID_BIT" in locate and "XR_SPACE_LOCATION_POSITION_VALID_BIT" in locate, "Fresh full-pose queries must invalidate stale data before any failure return.") for script in ("tools/test_vr_tutorial.py", "tools/test_vr_tracking.py", "tools/test_vr_tutorial_buttons.py"): require((source / script).is_file(), "Missing tutorial production C regression: " + script) GRAPH_SMOKE = r''' import io from pathlib import Path from types import SimpleNamespace import sys import re sys.path.insert(0, str(Path.cwd())) from tools import linux_arm64_build as build from tools.ninja_syntax import Writer headers = Path(".offline-check-headers") (headers / "GLES3").mkdir(parents=True) (headers / "GLES3/gl32.h").write_text("/* graph check only */") build.GL_HEADERS = headers build.is_linux_arm64 = lambda: True build.fetch_third_party = lambda directory: None sln = SimpleNamespace(port_release=True, port_vr=True, linux_arm64_cc="clang", port_pgo="off", port_lto="off") output = io.StringIO() build.generate_linux_arm64_build(Writer(output), sln) graph = output.getvalue() # pathlib follows the checker host even though this is a Linux build graph. # Normalize only the inspected text; retain the actual generated graph below. inspected_graph = re.sub(r"\$\r?\n[ \t]*", "", graph).replace("\\", "/") for required in ("port/linux/game/network_coop.c", "port/linux/game/coop_scripts.c", "port/linux/game/coop_enemies.c", "port/linux/src/vr.c", "port/linux/arm64/host_vr.c", "-DHALO_VR=1", "-iquote port/linux/game", "build/linux_arm64/brokers.txt", "port/assets/network/brokers.txt"): if required not in inspected_graph: raise RuntimeError("The ARM64 graph omitted " + required) if "-DHALO_GLES=1" in graph: raise RuntimeError("The VR guest graph selected GLES instead of desktop OpenGL") Path(".offline-check-build.ninja").write_text(graph, encoding="utf-8") print("ARM64/OpenXR guest, host, co-op and broker graph generated without downloads or compilation.") ''' def project_source(source, destination): archive = git(source, "archive", "--format=tar", SOURCE_COMMIT).stdout require(len(archive) <= 256 * 1024 * 1024, "The source archive exceeds the offline check limit.") with tarfile.open(fileobj=io.BytesIO(archive), mode="r:") as entries: for entry in entries: name = PurePosixPath(entry.name) require(not name.is_absolute() and ".." not in name.parts and (entry.isdir() or entry.isfile()), "The source projection contains an unexpected path or link.") target = destination.joinpath(*name.parts) if entry.isdir(): target.mkdir(parents=True, exist_ok=True) else: target.parent.mkdir(parents=True, exist_ok=True) with entries.extractfile(entry) as incoming, target.open("wb") as outgoing: outgoing.write(incoming.read()) def check_source(source: Path): source = source.resolve() require(git(source, "rev-parse", "HEAD").stdout.decode().strip() == SOURCE_COMMIT, "Use the exact pinned experimental engine commit; a moving branch is not accepted.") patch = ROOT / "resources/frame-controls.patch" changed = patch_files(patch.read_text(encoding="utf-8")) dirty = git(source, "diff", "--name-only", "HEAD").stdout.decode().splitlines() untracked = git(source, "ls-files", "--others", "--exclude-standard").stdout.decode().splitlines() require(set(dirty + untracked) <= changed, "The engine checkout has changes outside the reviewed local patch.") vanilla = git(source, "apply", "--check", str(patch), check=False).returncode == 0 patched = git(source, "apply", "--reverse", "--check", str(patch), check=False).returncode == 0 require(vanilla or patched, "The local patch neither applies cleanly nor exactly reverses in this checkout.") require(not vanilla or not dirty + untracked, "A vanilla source input must have no local engine changes.") # Always test a clean canonical projection, so a patched input is not trusted # merely because its HEAD still names the selected revision. (ROOT / "build").mkdir(exist_ok=True) with tempfile.TemporaryDirectory(prefix="experimental-engine-check-", dir=ROOT / "build") as temporary: projection = Path(temporary) project_source(source, projection) check_upstream(projection) subprocess.run(["git", "init", "--quiet"], cwd=projection, check=True, capture_output=True, timeout=30) for arguments in (("--check",), ()): result = subprocess.run(["git", "apply", *arguments, str(patch)], cwd=projection, capture_output=True, timeout=30) require(result.returncode == 0, "The local patch does not apply to the canonical pinned engine source.") if patched: for path in changed: require((source / path).read_bytes().replace(b"\r\n", b"\n") == (projection / path).read_bytes().replace(b"\r\n", b"\n"), "The patched input contains an extra change beyond the shipped local patch: " + path) check_controls(projection) check_stereo_glow_and_online_menu(projection) check_nominal_projectile_reticle(projection) check_tutorial(projection) check_unarmed_flashlight(projection) result = subprocess.run([sys.executable, "-I", "-c", GRAPH_SMOKE], cwd=projection, capture_output=True, text=True, timeout=30) require(result.returncode == 0, "Offline ARM64 configuration generation failed: " + result.stderr[-2000:]) return {"ok": True, "sourceCommit": SOURCE_COMMIT, "workingTree": "vanilla" if vanilla else "patched", "protocolVersion": 17, "rendererTwoPassBinding": True, "campaignCoopIntegration": True, "headDirectedMovementIntegration": True, "xboxControlsPreserved": True, "stereoSunGlowIntegration": True, "boundedSunGlowStrength": True, "vrInternetCampaignMenu": True, "nominalProjectileReticleIntegration": True, "renderRateUnarmedFlashlightIntegration": True, "renderRateProjectilePreviewIntegration": True, "networkCampaignOnlyIntegration": True, "vrTutorialIntegration": True, "freshFullPoseTrackingIntegration": True, "offlineArm64NinjaGraph": True, "arm64LinkedBuild": False, "hardwareValidated": False} def main(): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("source", type=Path, help="Local exact-pinned vanilla or fully patched engine checkout") arguments = parser.parse_args() try: result = check_source(arguments.source) except (EngineCheckError, OSError, subprocess.SubprocessError) as error: print(json.dumps({"ok": False, "error": str(error)})) return 1 print(json.dumps(result, indent=2)) return 0 if __name__ == "__main__": raise SystemExit(main())