From 2e622ead78c967eccf344b7c2cfe29b25b2296f2 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Mon, 6 Jul 2026 22:06:33 +0100 Subject: [PATCH] Guard daemon playable run generation --- Aetheria.State.Verify/Program.cs | 62 +++++++++++++++++++ .../Scripts/ServerShared/LoadoutGenerator.cs | 13 ---- 2 files changed, 62 insertions(+), 13 deletions(-) diff --git a/Aetheria.State.Verify/Program.cs b/Aetheria.State.Verify/Program.cs index 12faf767b..9681b5430 100644 --- a/Aetheria.State.Verify/Program.cs +++ b/Aetheria.State.Verify/Program.cs @@ -18,6 +18,7 @@ RequireTypedPendingCommitKeys(root); RequireTypedRuntimeFactionKeys(root); RequireTypedGalaxyFactionRelationships(root); RequireRuntimeCatalogKeyOnlyLookups(root); +RequireDaemonPlayableRunGenerationAuthority(root); RequireTypedBehaviorBodyKeys(root); RequireTypedOrbitTaskKeys(root); RequireTypedAgentTaskKeys(root); @@ -1210,6 +1211,67 @@ static void RequireRuntimeCatalogKeyOnlyLookups(string root) } } +static void RequireDaemonPlayableRunGenerationAuthority(string root) +{ + var daemonProgramPath = Path.Combine(root, "Aetheria.State.Daemon", "Program.cs"); + var daemonZoneGeneratorPath = Path.Combine(root, "Aetheria.State.Daemon", "AetheriaDaemonZoneGenerator.cs"); + var sharedLoadoutGeneratorPath = Path.Combine(root, "Assets", "Scripts", "ServerShared", "LoadoutGenerator.cs"); + + var daemonProgram = File.Exists(daemonProgramPath) + ? File.ReadAllText(daemonProgramPath) + : throw new InvalidOperationException("Cannot verify daemon playable-run generation authority; daemon Program.cs is missing."); + var daemonZoneGenerator = File.Exists(daemonZoneGeneratorPath) + ? File.ReadAllText(daemonZoneGeneratorPath) + : throw new InvalidOperationException("Cannot verify daemon playable-run generation authority; daemon zone generator is missing."); + var sharedLoadoutGenerator = File.Exists(sharedLoadoutGeneratorPath) + ? File.ReadAllText(sharedLoadoutGeneratorPath) + : throw new InvalidOperationException("Cannot verify daemon playable-run generation authority; legacy Unity LoadoutGenerator.cs is missing."); + + var requiredDaemonSymbols = new[] + { + "await AetheriaDaemonZoneGenerator.WritePlayableRunAsync(", + "internal static class AetheriaDaemonZoneGenerator", + "private sealed class DaemonLoadoutGenerator", + "var loadouts = new DaemonLoadoutGenerator(catalog);", + "GenerateEntities(loadouts)" + }; + + var daemonText = daemonProgram + "\n" + daemonZoneGenerator; + var missingDaemonSymbols = requiredDaemonSymbols + .Where(symbol => !daemonText.Contains(symbol, StringComparison.Ordinal)) + .ToArray(); + if (missingDaemonSymbols.Length > 0) + { + throw new InvalidOperationException( + "Playable run and loadout generation must be daemon-owned, not recovered through Unity ServerShared generators: " + + string.Join(", ", missingDaemonSymbols)); + } + + var forbiddenDaemonSymbols = new[] + { + "new LoadoutGenerator(", + "ZoneGenerator.Generate(", + "Assets.Scripts.ServerShared", + "ServerShared.LoadoutGenerator" + }; + var survivingDaemonSymbols = forbiddenDaemonSymbols + .Where(symbol => daemonText.Contains(symbol, StringComparison.Ordinal)) + .ToArray(); + if (survivingDaemonSymbols.Length > 0) + { + throw new InvalidOperationException( + "Daemon generation must not call the legacy Unity ServerShared loadout/zone generators: " + + string.Join(", ", survivingDaemonSymbols)); + } + + if (sharedLoadoutGenerator.Contains("// public LoadoutGenerator", StringComparison.Ordinal) || + sharedLoadoutGenerator.Contains("// ref Random random", StringComparison.Ordinal)) + { + throw new InvalidOperationException( + "Legacy Unity LoadoutGenerator must not carry commented constructor scars; Git history owns removed APIs."); + } +} + static void RequireTypedBehaviorBodyKeys(string root) { var checkedFiles = new[] diff --git a/Assets/Scripts/ServerShared/LoadoutGenerator.cs b/Assets/Scripts/ServerShared/LoadoutGenerator.cs index 903e71c13..c4862d667 100644 --- a/Assets/Scripts/ServerShared/LoadoutGenerator.cs +++ b/Assets/Scripts/ServerShared/LoadoutGenerator.cs @@ -40,19 +40,6 @@ public class LoadoutGenerator Faction = faction; PriceExponent = priceExponent; } - // - // public LoadoutGenerator( - // ref Random random, - // ItemManager itemManager, - // Faction faction, - // float priceExponent) - // { - // Random = random; - // ItemManager = itemManager; - // Faction = faction; - // PriceExponent = priceExponent; - // } - public EntityConstructionBlueprint GenerateShipLoadout(Predicate hullFilter = null) { var hullRow = RandomHull(HullType.Ship, hullFilter);