diff --git a/.voidbot/state/aetheria.cc b/.voidbot/state/aetheria.cc index 02cfebc0e..2242c66e0 100644 Binary files a/.voidbot/state/aetheria.cc and b/.voidbot/state/aetheria.cc differ diff --git a/Assets/Scripts/Gameplay/ActionGameManager.cs b/Assets/Scripts/Gameplay/ActionGameManager.cs index 8f6d5b37f..86f17256f 100644 --- a/Assets/Scripts/Gameplay/ActionGameManager.cs +++ b/Assets/Scripts/Gameplay/ActionGameManager.cs @@ -73,15 +73,27 @@ public class ActionGameManager : MonoBehaviour } } + public static void CommitRuntimeInputBindingOverride(string actionName, int bindingIndex, string inputSystemPath) + { + RuntimePlayerSettings.InputSettings.SetBindingOverride(actionName, bindingIndex, inputSystemPath); + QueueRuntimePlayerSettingsCommit(); + } + + public static void CommitRuntimeActionBarInput(string inputSystemPath, bool enabled) + { + RuntimePlayerSettings.InputSettings.SetActionBarInputEnabled(inputSystemPath, enabled); + QueueRuntimePlayerSettingsCommit(); + } + private static RuntimePlayerSettings CreateDefaultRuntimePlayerSettings() { var settings = new RuntimePlayerSettings(); settings.Name = Environment.UserName; - settings.InputSettings.ActionBarInputs.Add("/leftShift"); - settings.InputSettings.ActionBarInputs.Add("/leftButton"); - settings.InputSettings.ActionBarInputs.Add("/rightButton"); - settings.InputSettings.ActionBarInputs.Add("/middleButton"); - for (int i = 1; i < 6; i++) settings.InputSettings.ActionBarInputs.Add($"/{i}"); + settings.InputSettings.SetActionBarInputEnabled("/leftShift", true); + settings.InputSettings.SetActionBarInputEnabled("/leftButton", true); + settings.InputSettings.SetActionBarInputEnabled("/rightButton", true); + settings.InputSettings.SetActionBarInputEnabled("/middleButton", true); + for (int i = 1; i < 6; i++) settings.InputSettings.SetActionBarInputEnabled($"/{i}", true); return settings; } @@ -138,14 +150,14 @@ public class ActionGameManager : MonoBehaviour if (string.IsNullOrWhiteSpace(binding.ActionName) || string.IsNullOrWhiteSpace(binding.BindingPath) || binding.BindingIndex < 0) continue; - settings.InputSettings.InputActionMap[(binding.ActionName, binding.BindingIndex)] = binding.BindingPath; + settings.InputSettings.SetBindingOverride(binding.ActionName, binding.BindingIndex, binding.BindingPath); } settings.InputSettings.ActionBarInputs.Clear(); foreach (var input in stored.ActionBarInputs) { - if (!string.IsNullOrWhiteSpace(input) && !settings.InputSettings.ActionBarInputs.Contains(input)) - settings.InputSettings.ActionBarInputs.Add(input); + if (!string.IsNullOrWhiteSpace(input)) + settings.InputSettings.SetActionBarInputEnabled(input, true); } } diff --git a/Assets/Scripts/ServerShared/RuntimePlayerSettings.cs b/Assets/Scripts/ServerShared/RuntimePlayerSettings.cs index 59a4c4d5a..2b9158f9a 100644 --- a/Assets/Scripts/ServerShared/RuntimePlayerSettings.cs +++ b/Assets/Scripts/ServerShared/RuntimePlayerSettings.cs @@ -63,6 +63,35 @@ public class RuntimePlayerInputSettings { public Dictionary<(string action, int binding), string> InputActionMap = new Dictionary<(string action, int binding), string>(); public List ActionBarInputs = new List(); + + public void SetBindingOverride(string action, int binding, string inputSystemPath) + { + if (string.IsNullOrWhiteSpace(action)) + throw new ArgumentException("Action name is required.", nameof(action)); + if (binding < 0) + throw new ArgumentOutOfRangeException(nameof(binding)); + if (string.IsNullOrWhiteSpace(inputSystemPath)) + throw new ArgumentException("Input path is required.", nameof(inputSystemPath)); + + InputActionMap[(action, binding)] = inputSystemPath; + } + + public void SetActionBarInputEnabled(string inputSystemPath, bool enabled) + { + if (string.IsNullOrWhiteSpace(inputSystemPath)) + throw new ArgumentException("Input path is required.", nameof(inputSystemPath)); + + var index = ActionBarInputs.IndexOf(inputSystemPath); + if (enabled) + { + if (index < 0) + ActionBarInputs.Add(inputSystemPath); + } + else if (index >= 0) + { + ActionBarInputs.RemoveAt(index); + } + } } public enum Quality diff --git a/Assets/Scripts/UI/InputScreen/InputDisplayLayout.cs b/Assets/Scripts/UI/InputScreen/InputDisplayLayout.cs index 6ade0bf6a..0ee2c0891 100644 --- a/Assets/Scripts/UI/InputScreen/InputDisplayLayout.cs +++ b/Assets/Scripts/UI/InputScreen/InputDisplayLayout.cs @@ -415,10 +415,9 @@ public class InputDisplayLayout : MonoBehaviour private void CommitBindingOverride(InputAction action, InputBinding binding, string inputSystemPath) { - binding.overridePath = inputSystemPath; - action.ApplyBindingOverride(binding); - ActionGameManager.RuntimePlayerSettings.InputSettings.InputActionMap[(action.name, action.GetBindingIndex(binding))] = inputSystemPath; - QueueRuntimeInputSettingsCommit(); + var bindingIndex = action.GetBindingIndex(binding); + action.ApplyBindingOverride(bindingIndex, inputSystemPath); + ActionGameManager.CommitRuntimeInputBindingOverride(action.name, bindingIndex, inputSystemPath); } private void OnEnable() @@ -463,17 +462,10 @@ public class InputDisplayLayout : MonoBehaviour .Subscribe(data => { buttonMapping.IsActionBarButton = !buttonMapping.IsActionBarButton; + ActionGameManager.CommitRuntimeActionBarInput(buttonMapping.Button.InputSystemPath, buttonMapping.IsActionBarButton); if (buttonMapping.IsActionBarButton) - { - ActionGameManager.RuntimePlayerSettings.InputSettings.ActionBarInputs.Add(buttonMapping.Button.InputSystemPath); foreach(var overlap in OverlappingLabels(buttonMapping.ButtonRect)) PlaceLabel(overlap); - } - else - { - ActionGameManager.RuntimePlayerSettings.InputSettings.ActionBarInputs.Remove(buttonMapping.Button.InputSystemPath); - } - QueueRuntimeInputSettingsCommit(); AssignColor(buttonMapping); }); diff --git a/docs/aetheria-perfect-machine-map.md b/docs/aetheria-perfect-machine-map.md index 5217dcd1a..daade6200 100644 --- a/docs/aetheria-perfect-machine-map.md +++ b/docs/aetheria-perfect-machine-map.md @@ -72,12 +72,13 @@ legacy catalog cache, and legacy UI paths should be migration-only or deleted. name, tutorial flag, story-file hash cursors, gameplay formatting, graphics preferences, input binding overrides, and action-bar inputs. Unity's menu and input screens mutate `RuntimePlayerSettings` in memory and queue typed Verse - commits through the shared player-settings commit primitive; the in-memory + commits through shared player-settings commit primitives; the in-memory projection is not portable state authority. Aetheria has its own remapping UI that calls Unity's InputSystem at the binding/action layer; Unity's generated `AetheriaInput` class is the edge consumer of typed binding overrides, not - the durable owner. Binding drag/drop and action-bar remapping both queue the - same typed player-settings commit after mutating the runtime projection. The + the durable owner. Binding drag/drop and action-bar remapping both route + through named runtime input commit methods instead of writing the input + collections directly. The action bar also uses typed runtime catalog category rows to reject non-consumable inventory drops and creates consumable bindings around typed catalog rows. Missing typed rows are rejected for consumable binding instead @@ -1088,9 +1089,9 @@ First Aetheria surfaces to publish: `RuntimePlayerSettings`; `AetheriaPlayerSettings` remains the typed Verse state document owner, while Unity only keeps a session projection and queues typed player-settings commits. - - Done: route input-screen binding/action-bar edits through the typed - player-settings commit primitive now that the Unity state package is live; - the stale runtime-only keyboard layout warning is gone. + - Done: route input-screen binding/action-bar edits through named typed + player-settings commit methods on the Unity runtime boundary, so the UI is + no longer a direct writer of input binding or action-bar collections. - Remaining: delete or quarantine old cache abstractions that no longer protect an invariant once catalog migration has a typed reader. @@ -1148,8 +1149,8 @@ First Aetheria surfaces to publish: - Unity runtime settings projection is now `RuntimePlayerSettings`; live Unity source has no standalone `PlayerSettings` class/property/method symbols. - Input binding and action-bar edits now queue the same typed player-settings - commit path as menu settings changes; the old `SaveLayout` runtime-only - warning is gone. The generated `AetheriaInput` class still calls Unity's + commit path as menu settings changes through named runtime input commit + methods; the old `SaveLayout` runtime-only warning is gone. The generated `AetheriaInput` class still calls Unity's `InputActionAsset.FromJson`, but that JSON belongs to Unity's generated input action lowering under Aetheria's remapping system. It is not durable Aetheria state and does not own remapping authority.