feat(client): T-1132 standalone Atlas companion shell + make atlas (D-254 SS3)
New atlas_standalone scene/script: attach-or-spawn boot (one REAL
connect_to_sim attempt at SR_PORT-or-9876 — a separate throwaway TCP
probe was proven by live run to kill a pre-accept-loop server via
broken handshake pipe; never abandon a connected socket), else spawn
--port 0 via new ServerProcess.start_with_pipe + LISTENING:{port}
stdout parse, retry against the resolved port. Reader role wired end
to end: protocol.encode_startup_message optional role param (empty
omits the wire key — byte-identical for all existing callers),
sim_bridge.connection_role suppresses the post-handshake
RequestAllSettings auto-send, hud_groups skips AutoPause/AutoResume
sends for readers (all three would otherwise burn Reader violation
strikes per the T-1130 matrix — endorsed by Oscar).
Generic implant host per D-254 SS3: the shell instantiates ALL
registered implant apps; implant_app_manifest gains
available_in_companion (opt-out, default true) and
implant_registry.instantiate_all a standalone filter param (default
preserves hud.gd behavior byte-identically). Boot order is
instantiate_all THEN open_app (reverse renders a permanently black
window — app_changed fires with no listener; matches hud.gd's order).
Owned-server lifecycle: _exit_tree stops a spawned child, attached
servers survive companion close. Known engine limitation documented:
raw SIGTERM bypasses all Godot notifications and orphans a spawned
server; WM close paths verified clean.
Live-verified: spawn-mode (301 systems rendered from systems.db over
the wire), attach-mode, two simultaneous readers, clean shutdown with
zero orphan processes. 14 new gdUnit tests (port/LISTENING parsing).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -11,3 +11,13 @@ extends Resource
|
||||
# KEY_M = 77, KEY_N = 78; -1 = no binding.
|
||||
@export var default_key: int = -1
|
||||
@export var preserves_state: bool = true
|
||||
## D-254 §3 (companion app amendment, 2026-07-17): opt-out flag for the
|
||||
## standalone Atlas companion (`make atlas`) — every registered app is hosted
|
||||
## in the companion by default; set false only for apps that structurally
|
||||
## require a playing character/gameplay context. The IN-GAME implant ignores
|
||||
## this field entirely (zero behavior change there) — it exists solely for
|
||||
## `atlas_standalone.gd`'s ImplantRegistry.instantiate_all(standalone=true)
|
||||
## filter. Naming note: Jeroen's original suggested name was
|
||||
## `availableInAtlasApp`; renamed to snake_case + "companion" (not "atlas")
|
||||
## because the Atlas is itself one of the hosted apps, not the host.
|
||||
@export var available_in_companion: bool = true
|
||||
|
||||
@@ -27,9 +27,15 @@ func get_resolved_mode(app_path: String) -> int:
|
||||
|
||||
## Instantiate all registered apps whose manifest declares a scene_path and add
|
||||
## them as children of parent. Manifests without scene_path are metadata-only
|
||||
## and are silently skipped. Called from hud.gd._ready() — not from _ready() here.
|
||||
func instantiate_all(parent: Node) -> void:
|
||||
## and are silently skipped. Called from hud.gd._ready() in the normal game
|
||||
## (standalone=false, default — every app is hosted, the flag below is
|
||||
## irrelevant) and from atlas_standalone.gd (standalone=true, D-254 §3
|
||||
## amendment) — in the companion, manifests with available_in_companion=false
|
||||
## are skipped entirely (not instantiated, so never reachable by app-path).
|
||||
func instantiate_all(parent: Node, standalone: bool = false) -> void:
|
||||
for m in get_manifests():
|
||||
if standalone and not m.available_in_companion:
|
||||
continue
|
||||
var scene_path: String = m.scene_path
|
||||
if scene_path.is_empty():
|
||||
continue
|
||||
|
||||
Reference in New Issue
Block a user