feat(ui): AI-Enhanced Dialogue toggle + hardware detection (#646, D-138)
Implements the full AI-Enhanced Dialogue feature for Sprint 26: - HardwareDetector autoload (extends Node): three-layer detection — Layer 1 RAM classification (pass/marginal/fail, thresholds 2GB/1.6GB), Layer 2 TPT benchmark cache (green/yellow/red, thresholds 6/3 t/s), Layer 3 degradation monitoring (>40% drop → yellow). load_ai_pref() restores toggle state from user://settings.cfg on startup. - Settings dialog AI section: toggle, colored status dot, status message label, testable API (get_ai_dialogue_label_text, set_ai_dialogue_ hardware_status, is_ai_dialogue_toggle_enabled). Only RAM "fail" greys out toggle — player always overrides yellow/red recommendations (D-138). - GameState.ai_enhanced_dialogue_enabled (default true, opt-out model). GameState.settings_response (v20 one-shot settings dump from server). apply_snapshot() hydrates ai_enhanced_dialogue_enabled from full dump. - Protocol v19→v20, settings_response decoding in decode_snapshot(). Protocol converted to extends Node autoload (enables test has_method). encode_change_settings() helper for test inspection. - InputMapper: CHANGE_SETTINGS, REQUEST_ALL_SETTINGS, DELETE_SETTING. SimBridge: wire mappings for all three. RequestAllSettings queued after handshake to hydrate client state from server SQLite on connect. settings_response carry-forward in receive_bytes(). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
class_name Protocol
|
||||
extends Node
|
||||
## MessagePack codec for the Rust↔Godot wire protocol (D-020).
|
||||
##
|
||||
## Encodes/decodes ObserverSnapshot and PlayerInput to match
|
||||
@@ -11,8 +11,8 @@ class_name Protocol
|
||||
|
||||
## Protocol version — must match server PROTOCOL_VERSION in bridge/types.rs.
|
||||
## Reject snapshots where version != this value.
|
||||
## v19: adds character_archetype field to StartupMessage (#588, #587).
|
||||
const PROTOCOL_VERSION: int = 19
|
||||
## v20: adds settings_response field to ObserverSnapshot (#627, D-138).
|
||||
const PROTOCOL_VERSION: int = 20
|
||||
|
||||
|
||||
# -- Decode: bytes from server → GDScript types --------------------------------
|
||||
@@ -270,6 +270,32 @@ static func decode_snapshot(bytes: PackedByteArray) -> Variant:
|
||||
"triangle_id": int(raw_ev["triangle_id"]),
|
||||
})
|
||||
|
||||
# v20: settings_response (#627, D-138) — server ack after ChangeSettings / full settings dump
|
||||
# after RequestAllSettings. kind = "full"|"ack". "full": settings array [{key, value}].
|
||||
# "ack": {success: bool, key: String, error: String|null}.
|
||||
var settings_response: Variant = null
|
||||
var raw_sr: Variant = raw.get("settings_response")
|
||||
if raw_sr is Dictionary:
|
||||
var sr_kind: String = str(raw_sr.get("kind", ""))
|
||||
if sr_kind == "full":
|
||||
var sr_settings: Array = []
|
||||
var raw_sr_settings: Variant = raw_sr.get("settings")
|
||||
if raw_sr_settings is Array:
|
||||
for raw_s in raw_sr_settings:
|
||||
if raw_s is Dictionary and raw_s.has("key"):
|
||||
sr_settings.append({
|
||||
"key": str(raw_s["key"]),
|
||||
"value": raw_s.get("value"),
|
||||
})
|
||||
settings_response = {"kind": "full", "settings": sr_settings}
|
||||
elif sr_kind == "ack":
|
||||
settings_response = {
|
||||
"kind": "ack",
|
||||
"success": bool(raw_sr.get("success", false)),
|
||||
"key": str(raw_sr.get("key", "")),
|
||||
"error": raw_sr.get("error"),
|
||||
}
|
||||
|
||||
# v19: current_ticker (#592) — scrolling news headline when in The Last Shift zone.
|
||||
# {id: String, text: String, category: String} or null when player outside bar zone.
|
||||
var current_ticker: Variant = null
|
||||
@@ -362,6 +388,7 @@ static func decode_snapshot(bytes: PackedByteArray) -> Variant:
|
||||
"zone_id": zone_id,
|
||||
"triangle_crisis_events": triangle_crisis_events,
|
||||
"current_ticker": current_ticker,
|
||||
"settings_response": settings_response,
|
||||
}
|
||||
|
||||
|
||||
@@ -548,6 +575,24 @@ static func _encode_action(action_name: String, action_data: Variant) -> Variant
|
||||
return action_name
|
||||
|
||||
|
||||
## Encode a ChangeSettings action for the AI-Enhanced Dialogue toggle (#646, D-138).
|
||||
## Returns a MessagePack-encoded Vec<PlayerInput> in buffer-entry format
|
||||
## (action_name + action_data), suitable for inspection in tests and for
|
||||
## queuing via SimBridge._outbound_buffer.
|
||||
## The server receives this as a PlayerAction::ChangeSettings variant.
|
||||
static func encode_change_settings(enabled: bool) -> PackedByteArray:
|
||||
var entries: Array = [{
|
||||
"tick": 0,
|
||||
"action_name": "ChangeSettings",
|
||||
"action_data": {"ai_enhanced_dialogue": enabled},
|
||||
}]
|
||||
var result = Messagepack.encode(entries)
|
||||
if result.status != null:
|
||||
push_error("Protocol: encode_change_settings failed: %s" % result.status)
|
||||
return PackedByteArray()
|
||||
return result.value
|
||||
|
||||
|
||||
## Decode a PlayerInput from MessagePack bytes (used in tests / echo scenarios).
|
||||
## Returns { "tick": int, "action": { "variant": String, "data": Variant } } or null.
|
||||
static func decode_player_input(bytes: PackedByteArray) -> Variant:
|
||||
|
||||
Reference in New Issue
Block a user