Lower player name through Eve settings surface

This commit is contained in:
Meta Vens 2026-06-16 19:45:39 +01:00
commit 65a5a56635
8 changed files with 111 additions and 38 deletions

View file

@ -486,7 +486,7 @@ Console.WriteLine("Package serializer boundary: MessagePack symbols remain in na
Console.WriteLine("Eve runtime bootstrap: operations surface mounts through UI Toolkit presenter");
Console.WriteLine("Renderer-local console authority: deleted; UI commands flow through Eve command documents");
Console.WriteLine("Renderer-local debug panels: obsolete uGUI field tester authority is deleted");
Console.WriteLine("Main-menu settings authority: gameplay and graphics settings return through typed player-settings commits");
Console.WriteLine("Main-menu settings authority: player name, gameplay, and graphics settings return through typed player-settings commits");
Console.WriteLine("Main-menu Continue authority: Continue selects typed run state instead of a null button");
Console.WriteLine("PropertiesPanel inspector authority: dead generic reflection inspector path is deleted");
Console.WriteLine("Runtime simulation tuning authority: UI writes flow through gameplay checkpoint commits");
@ -1980,7 +1980,6 @@ static void RequireMainMenuSettingsCommit(string root)
var requiredMainMenuAuthoritySymbols = new[]
{
"ActionGameManager.CommitRuntimePlayerName",
"ActionGameManager.CommitRuntimePlayerSettingsCommand"
};
@ -1997,6 +1996,7 @@ static void RequireMainMenuSettingsCommit(string root)
var requiredSharedCommands = new[]
{
"SetPlayerName",
"CycleTemperatureUnit",
"DecrementSignificantDigits",
"IncrementSignificantDigits",
@ -2018,6 +2018,8 @@ static void RequireMainMenuSettingsCommit(string root)
var requiredSurfaceBuilderSymbols = new[]
{
"AetheriaRuntimePlayerSettingsSurfaceBuilder",
"AetheriaRuntimePlayerSettingsCommands.SetPlayerName",
"\"control.text\"",
"AetheriaRuntimePlayerSettingsCommands.CycleTemperatureUnit",
"AetheriaRuntimePlayerSettingsCommands.DecrementSignificantDigits",
"AetheriaRuntimePlayerSettingsCommands.IncrementSignificantDigits",
@ -2040,8 +2042,7 @@ static void RequireMainMenuSettingsCommit(string root)
{
"AetheriaRuntimePlayerSettingsSurfaceBuilder.Build",
"new EveUiToolkitSurfaceLowerer",
"ActionGameManager.CommitRuntimePlayerSettingsCommand",
"new TextField(\"Name\")"
"ActionGameManager.CommitRuntimePlayerSettingsCommand(request.Command, request.Payload)"
};
var missingMainMenuSymbols = requiredMainMenuSymbols
@ -2055,6 +2056,23 @@ static void RequireMainMenuSettingsCommit(string root)
string.Join(", ", missingMainMenuSymbols));
}
var forbiddenMainMenuSymbols = new[]
{
"new TextField(\"Name\")",
"ActionGameManager.CommitRuntimePlayerName(evt.newValue)"
};
var mainMenuHits = forbiddenMainMenuSymbols
.Where(symbol => source.Contains(symbol, StringComparison.Ordinal))
.ToArray();
if (mainMenuHits.Length > 0)
{
throw new InvalidOperationException(
"MainMenu should not keep renderer-local player-name input authority outside the shared Eve surface: " +
string.Join(", ", mainMenuHits));
}
var forbiddenUiFieldSymbols = new[]
{
"AddField(\"Temperature Unit\"",
@ -2141,7 +2159,9 @@ static void RequirePlayerSettingsEveSurface(string root)
if (!bridge.Contains("AppliedPlayerSettingsCommands", StringComparison.Ordinal) ||
!bridge.Contains("ApplyPlayerSettingsCommandAsync", StringComparison.Ordinal) ||
!bridge.Contains("PutPlayerSettingsSurfaceAsync", StringComparison.Ordinal))
!bridge.Contains("PutPlayerSettingsSurfaceAsync", StringComparison.Ordinal) ||
!bridge.Contains("SetPlayerName", StringComparison.Ordinal) ||
!bridge.Contains("command.Payload.TryGetValue(\"value\"", StringComparison.Ordinal))
{
throw new InvalidOperationException(
"Eve command bridge no longer owns the typed player-settings surface mutation path.");
@ -2162,13 +2182,16 @@ static void RequirePlayerSettingsEveSurface(string root)
}
if (!sharedCommands.Contains("SurfaceId = \"aetheria.player_settings\"", StringComparison.Ordinal) ||
!sharedCommands.Contains("public static bool IsKnown", StringComparison.Ordinal))
!sharedCommands.Contains("public static bool IsKnown", StringComparison.Ordinal) ||
!sharedCommands.Contains("SetPlayerName", StringComparison.Ordinal))
{
throw new InvalidOperationException(
"Shared player-settings Eve command contract is missing the surface id or command registry helper.");
}
if (!surfaceBuilder.Contains("public static class AetheriaRuntimePlayerSettingsSurfaceBuilder", StringComparison.Ordinal))
if (!surfaceBuilder.Contains("public static class AetheriaRuntimePlayerSettingsSurfaceBuilder", StringComparison.Ordinal) ||
!surfaceBuilder.Contains("\"control.text\"", StringComparison.Ordinal) ||
!surfaceBuilder.Contains("SetPlayerName", StringComparison.Ordinal))
{
throw new InvalidOperationException(
"Shared player-settings Eve surface builder no longer owns the portable settings surface contract.");

View file

@ -68,6 +68,7 @@ public static class AetheriaEveCommandBridge
report.AppliedOperationsRefreshes++;
break;
case GameCult.Aetheria.State.Unity.AetheriaRuntimePlayerSettingsCommands.Refresh:
case GameCult.Aetheria.State.Unity.AetheriaRuntimePlayerSettingsCommands.SetPlayerName:
case GameCult.Aetheria.State.Unity.AetheriaRuntimePlayerSettingsCommands.CycleTemperatureUnit:
case GameCult.Aetheria.State.Unity.AetheriaRuntimePlayerSettingsCommands.DecrementSignificantDigits:
case GameCult.Aetheria.State.Unity.AetheriaRuntimePlayerSettingsCommands.IncrementSignificantDigits:
@ -127,6 +128,12 @@ public static class AetheriaEveCommandBridge
switch (command.Command)
{
case GameCult.Aetheria.State.Unity.AetheriaRuntimePlayerSettingsCommands.SetPlayerName:
settings.PlayerName = command.Payload.TryGetValue("value", out var playerName)
? playerName ?? ""
: "";
persistSettings = true;
break;
case GameCult.Aetheria.State.Unity.AetheriaRuntimePlayerSettingsCommands.CycleTemperatureUnit:
settings.Gameplay.TemperatureUnit = Cycle(
settings.Gameplay.TemperatureUnit,

View file

@ -116,10 +116,22 @@ public class ActionGameManager : MonoBehaviour
QueueRuntimePlayerSettingsCommit();
}
public static bool CommitRuntimePlayerSettingsCommand(string command)
public static bool CommitRuntimePlayerSettingsCommand(
string command,
IReadOnlyDictionary<string, string> payload = null)
{
switch (command)
{
case AetheriaRuntimePlayerSettingsCommands.SetPlayerName:
if (payload != null && payload.TryGetValue("value", out var playerName))
{
CommitRuntimePlayerName(playerName);
}
else
{
CommitRuntimePlayerName("");
}
return true;
case AetheriaRuntimePlayerSettingsCommands.CycleTemperatureUnit:
CommitRuntimeTemperatureUnit(
RuntimePlayerSettings.GameplaySettings.TemperatureUnit switch

View file

@ -338,37 +338,12 @@ public class MainMenu : MonoBehaviour
shell.style.backgroundColor = new Color(0.08f, 0.1f, 0.14f, 0.96f);
root.Add(shell);
var title = new Label("Player Settings");
title.style.unityFontStyleAndWeight = FontStyle.Bold;
title.style.fontSize = 24;
title.style.marginBottom = 6;
shell.Add(title);
var subtitle = new Label("Shared Eve surface lowered locally while gameplay keeps commit authority.");
subtitle.style.marginBottom = 12;
shell.Add(subtitle);
var nameField = new TextField("Name")
{
value = ActionGameManager.RuntimePlayerSettings.Name
};
nameField.style.marginBottom = 14;
nameField.RegisterValueChangedCallback(evt =>
{
if (string.Equals(evt.newValue, ActionGameManager.RuntimePlayerSettings.Name, StringComparison.Ordinal))
return;
ActionGameManager.CommitRuntimePlayerName(evt.newValue);
RenderPlayerSettingsSurface();
});
shell.Add(nameField);
var lowerer = new EveUiToolkitSurfaceLowerer();
var surface = lowerer.Lower(
ToEveSurfaceDocument(BuildPlayerSettingsSurfaceDefinition()),
request =>
{
if (!ActionGameManager.CommitRuntimePlayerSettingsCommand(request.Command))
if (!ActionGameManager.CommitRuntimePlayerSettingsCommand(request.Command, request.Payload))
{
Debug.LogWarning($"Unknown player-settings command: {request.Command}");
return;

View file

@ -4,6 +4,7 @@ namespace GameCult.Aetheria.State.Unity
{
public const string SurfaceId = "aetheria.player_settings";
public const string Refresh = "aetheria.player_settings.refresh";
public const string SetPlayerName = "aetheria.player_settings.player_name.set";
public const string CycleTemperatureUnit = "aetheria.player_settings.gameplay.temperature_unit.cycle";
public const string DecrementSignificantDigits = "aetheria.player_settings.gameplay.significant_digits.decrement";
public const string IncrementSignificantDigits = "aetheria.player_settings.gameplay.significant_digits.increment";
@ -13,6 +14,7 @@ namespace GameCult.Aetheria.State.Unity
public static bool IsKnown(string command)
{
return command == Refresh ||
command == SetPlayerName ||
command == CycleTemperatureUnit ||
command == DecrementSignificantDigits ||
command == IncrementSignificantDigits ||

View file

@ -181,9 +181,13 @@ namespace GameCult.Aetheria.State.Unity
"aetheria.playerSettings.summary",
"card",
new[] { ("title", "Player Settings") },
TextInput(
"playerSettings.summary.playerName",
"Name",
settings.PlayerName,
AetheriaRuntimePlayerSettingsCommands.SetPlayerName),
Row(
"playerSettings.summary.values",
("playerName", settings.PlayerName),
("tutorialPassed", settings.TutorialPassed ? "Yes" : "No"),
("activeRun", settings.ActiveRunKey)),
Text(
@ -242,6 +246,10 @@ namespace GameCult.Aetheria.State.Unity
AetheriaRuntimePlayerSettingsCommands.Refresh,
"Refresh",
"cultmesh"),
new AetheriaRuntimeSurfaceCommandTemplate(
AetheriaRuntimePlayerSettingsCommands.SetPlayerName,
"Set Player Name",
"cultmesh"),
new AetheriaRuntimeSurfaceCommandTemplate(
AetheriaRuntimePlayerSettingsCommands.CycleTemperatureUnit,
"Cycle Temperature Unit",
@ -280,6 +288,11 @@ namespace GameCult.Aetheria.State.Unity
return Node(id, "control.button", new[] { ("label", label), ("command", command) });
}
private static AetheriaRuntimeSurfaceComponent TextInput(string id, string label, string value, string command)
{
return Node(id, "control.text", new[] { ("label", label), ("value", value), ("command", command) });
}
private static AetheriaRuntimeSurfaceComponent ButtonRow(
string id,
params AetheriaRuntimeSurfaceComponent[] children)

View file

@ -113,6 +113,26 @@ namespace GameCult.Eve.UnityUIToolkit
var command = component.GetProp("command", component.GetProp("action", "invoke"));
return new Button(() => EmitCommand(document, component, command, commandSink)) { text = label };
}
case "control.text":
{
var label = component.GetProp("label", component.GetProp("title", "Value"));
var command = component.GetProp("command", component.GetProp("action", "set"));
var value = component.GetProp("value");
var field = new TextField(label);
field.SetValueWithoutNotify(value);
field.RegisterValueChangedCallback(evt =>
{
if (string.Equals(evt.newValue, value, StringComparison.Ordinal))
return;
var payload = new Dictionary<string, string>(component.Props, StringComparer.Ordinal)
{
["value"] = evt.newValue ?? ""
};
EmitCommand(document, component, command, payload, commandSink);
});
return field;
}
default:
{
var element = new VisualElement();
@ -130,6 +150,21 @@ namespace GameCult.Eve.UnityUIToolkit
EveSurfaceComponent component,
string command,
Action<EveSurfaceCommandRequest>? commandSink)
{
EmitCommand(
document,
component,
command,
new Dictionary<string, string>(component.Props, StringComparer.Ordinal),
commandSink);
}
private static void EmitCommand(
EveSurfaceDocument document,
EveSurfaceComponent component,
string command,
IReadOnlyDictionary<string, string> payload,
Action<EveSurfaceCommandRequest>? commandSink)
{
if (commandSink == null || string.IsNullOrWhiteSpace(command))
return;
@ -138,7 +173,7 @@ namespace GameCult.Eve.UnityUIToolkit
document.ProviderId,
document.Surface.Id,
command,
new Dictionary<string, string>(component.Props, StringComparer.Ordinal),
payload,
DateTimeOffset.UtcNow,
"unity-uitoolkit"));
}
@ -205,6 +240,8 @@ namespace GameCult.Eve.UnityUIToolkit
return "text";
if (kind.StartsWith("control.button.", StringComparison.Ordinal))
return "control.button";
if (kind.StartsWith("control.text.", StringComparison.Ordinal))
return "control.text";
return kind;
}

View file

@ -1515,8 +1515,12 @@ First Aetheria surfaces to publish:
- Done: replace the main-menu gameplay/graphics settings screen with a
locally lowered UI Toolkit projection of the shared
`aetheria.player_settings` Eve surface contract. The menu shell still owns
entry/exit flow and player-name input, but gameplay now owns surface
command semantics through `ActionGameManager.CommitRuntimePlayerSettingsCommand`.
entry/exit flow, but gameplay now owns surface command semantics through
`ActionGameManager.CommitRuntimePlayerSettingsCommand`.
- Done: move the main-menu player-name field into the shared
`aetheria.player_settings` Eve surface contract. `MainMenu` no longer owns
a special-case text field; UI Toolkit lowers the shared Eve text control
and gameplay still owns the typed player-settings commit.
- Replace the next concrete uGUI screen with an Eve-owned surface.
- Move the staged packages into the Eve repo once its worktree is clean, then
import them back into Aetheria from Eve instead of carrying a local copy.