A bad package station kind excludes the package; AetherDb opens a catalog with a kindless station hull, names it, and station-kinds hull sets it; the raw opener is gone
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
37a8110907
commit
d2f4b2c347
5 changed files with 59 additions and 19 deletions
|
|
@ -14,10 +14,12 @@ public static class AetheriaStores
|
|||
// Attaches (hydrates) the catalog read-only unless catalogWritable, then the run and player stores when given.
|
||||
// Throws when a [CultGlobal] type routed to the catalog has no record. The one exemption is a writable catalog with
|
||||
// no records at all, a seed store being authored from nothing; a populated catalog is held to it however it opens.
|
||||
// A catalog's station hull with no Kind is refused, unless the caller gives kindlessStation: the tool that repairs a catalog
|
||||
// is told the hull's name and goes on.
|
||||
// The registry is the process-wide one unless the caller scopes it (the test host does, so its own document types never
|
||||
// become catalog globals); the global check above is judged against whichever registry opened the cache.
|
||||
public static CultCache Open(string catalogPath, string runPath = null, string playerPath = null, bool catalogWritable = false,
|
||||
CultDocumentRegistry registry = null)
|
||||
CultDocumentRegistry registry = null, Action<string> kindlessStation = null)
|
||||
{
|
||||
// A read-only open of an absent catalog would hydrate an empty game; a shipped player missing its catalog stops here.
|
||||
if (!catalogWritable && !File.Exists(catalogPath))
|
||||
|
|
@ -42,7 +44,7 @@ public static class AetheriaStores
|
|||
foreach (var data in cache.GetAll<ConsumableItemData>()) CultRecordRefs.Validate(data);
|
||||
VerseGrammarValidation.ValidateAll(cache.GetAll<VerseVerb>());
|
||||
ValidateBins(cache);
|
||||
StationKinds.Validate(cache);
|
||||
StationKinds.Validate(cache, kindlessStation);
|
||||
return cache;
|
||||
}
|
||||
catch
|
||||
|
|
|
|||
|
|
@ -108,6 +108,7 @@ public static class ShipModCatalog
|
|||
Exclude(directory, $"hull name '{hull.Name}' collides with an existing catalog item.");
|
||||
if (shipIds.Contains(ship.Id))
|
||||
Exclude(directory, $"ship ID collides with an existing catalog ship authoring record.");
|
||||
if (StationKinds.HullKindFault(cache, hull) is { } kindFault) Exclude(directory, kindFault);
|
||||
foreach (var product in package.Products)
|
||||
{
|
||||
if (!factionKeys.Contains(product.Manufacturer.Key))
|
||||
|
|
|
|||
|
|
@ -3,25 +3,27 @@ using System.IO;
|
|||
using GameCult.Caching;
|
||||
|
||||
// Opens GameData/Aetheria.cc through AetheriaStores, the game's own store composition. The catalog attaches read-only
|
||||
// unless a command writes it, so a command that only reads cannot write.
|
||||
// unless a command writes it, so a command that only reads cannot write. A station hull with no kind does not lock the
|
||||
// catalog: it is named to warn (stderr by default) and the commands that repair it stay reachable.
|
||||
public sealed class AetherDb
|
||||
{
|
||||
public string Root { get; }
|
||||
public CultCache Cache { get; }
|
||||
|
||||
private AetherDb(string root, bool catalogWritable, bool withRun)
|
||||
private AetherDb(string root, bool catalogWritable, bool withRun, Action<string> warn)
|
||||
{
|
||||
Root = root;
|
||||
var gameData = Path.Combine(root, "GameData");
|
||||
Cache = AetheriaStores.Open(
|
||||
Path.Combine(gameData, "Aetheria.cc"),
|
||||
runPath: withRun ? Path.Combine(gameData, "run.cc") : null,
|
||||
catalogWritable: catalogWritable);
|
||||
catalogWritable: catalogWritable,
|
||||
kindlessStation: warn ?? Console.Error.WriteLine);
|
||||
}
|
||||
|
||||
// With no root given, walks up from the working directory to the game folder: the first folder whose GameData holds Aetheria.cc
|
||||
public static AetherDb Open(bool catalogWritable = false, bool withRun = false, string root = null) =>
|
||||
new AetherDb(root ?? FindRoot(), catalogWritable, withRun);
|
||||
public static AetherDb Open(bool catalogWritable = false, bool withRun = false, string root = null, Action<string> warn = null) =>
|
||||
new AetherDb(root ?? FindRoot(), catalogWritable, withRun, warn);
|
||||
|
||||
public static string FindRoot()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -61,10 +61,23 @@ public static class Program
|
|||
case "place-names":
|
||||
Console.OutputEncoding = new System.Text.UTF8Encoding(false);
|
||||
return PlaceNamesCommand.Run(args.Skip(1).ToArray());
|
||||
case "station-kinds": return StationKindsCatalog.Run(StationKindsCatalog.OpenCatalog(args.Contains("apply")), args.Contains("apply"), Console.Out);
|
||||
case "station-kinds":
|
||||
{
|
||||
var apply = args.Contains("apply");
|
||||
using var db = AetherDb.Open(catalogWritable: apply);
|
||||
int code;
|
||||
if (args.Length > 1 && args[1] == "hull")
|
||||
{
|
||||
if (args.Length != (apply ? 5 : 4)) { Console.WriteLine("station-kinds hull \"<hull name>\" <kind> [apply]"); return 1; }
|
||||
code = StationKindsCatalog.SetHullKind(db.Cache, args[2], args[3], apply, Console.Out);
|
||||
}
|
||||
else code = StationKindsCatalog.Run(db.Cache, apply, Console.Out);
|
||||
if (apply) db.Cache.FlushAsync().GetAwaiter().GetResult();
|
||||
return code;
|
||||
}
|
||||
case "doctrine-catalog": return DoctrineCatalog.Run(AetherDb.Open(catalogWritable: args.Contains("apply")).Cache, args.Contains("apply"), Console.Out);
|
||||
default:
|
||||
Console.WriteLine("commands: census, factions, station-fit, hardpoint-fit, loadout [seed], save, settings, settings-dump, dangling [clear <Type.Member>]... [apply], shield-migrate [apply], brownout-migrate [apply], roles-migrate [apply], firing-arc-migrate [apply], range-falloff [apply] [--effective <fraction>] [--end <multiplier>], targeting-catalog [apply], targeting-catalog-6c [apply], targeting-catalog-6d [apply], ship-authoring create|inspect|validate|compose, field-kinds [apply], mine-launcher [apply], smoke-hull [apply], smoke-rounds [apply], roles-backfill [apply], pd-gear [apply], consumables-first-set [apply], launch-suppressor [apply], pirates-faction [apply], verse-grammar [apply], generic-designs [apply], sensor-stat-set [apply], inspection-scanner [apply], minor-power-factions [apply], place-names [apply], product-lines weapons|minor-powers|gear|tiers [apply], doctrine-catalog [apply], assembly-from-maker [apply], station-kinds [apply]");
|
||||
Console.WriteLine("commands: census, factions, station-fit, hardpoint-fit, loadout [seed], save, settings, settings-dump, dangling [clear <Type.Member>]... [apply], shield-migrate [apply], brownout-migrate [apply], roles-migrate [apply], firing-arc-migrate [apply], range-falloff [apply] [--effective <fraction>] [--end <multiplier>], targeting-catalog [apply], targeting-catalog-6c [apply], targeting-catalog-6d [apply], ship-authoring create|inspect|validate|compose, field-kinds [apply], mine-launcher [apply], smoke-hull [apply], smoke-rounds [apply], roles-backfill [apply], pd-gear [apply], consumables-first-set [apply], launch-suppressor [apply], pirates-faction [apply], verse-grammar [apply], generic-designs [apply], sensor-stat-set [apply], inspection-scanner [apply], minor-power-factions [apply], place-names [apply], product-lines weapons|minor-powers|gear|tiers [apply], doctrine-catalog [apply], assembly-from-maker [apply], station-kinds [hull \"<name>\" <kind>] [apply]");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ using System;
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using GameCult.Caching;
|
||||
using GameCult.Caching.MessagePack;
|
||||
|
||||
// Authors the catalog's one list of station kinds (aetheria-living-world, cut station-kind-datum) and gives Zenith, the
|
||||
// one station hull in the repo, its ruled kind (ruling zenith-is-a-trade-hub: provisional, one edit here to change).
|
||||
|
|
@ -34,15 +33,6 @@ public static class StationKindsCatalog
|
|||
("waystation", "Waystation", "Fuel, repair and medicine along a corridor."),
|
||||
};
|
||||
|
||||
// The catalog opened without AetheriaStores.Open's checks: the catalog this command first runs on holds a station hull with no
|
||||
// kind, which StationKinds.Validate refuses, and this command is what gives it one.
|
||||
public static CultCache OpenCatalog(bool apply, string catalogPath = null)
|
||||
{
|
||||
var cache = new CultCache();
|
||||
cache.AddBackingStore(new SingleFileMessagePackBackingStore(catalogPath ?? Path.Combine(AetherDb.FindRoot(), "GameData", "Aetheria.cc"), !apply), AetheriaStores.CatalogTypes);
|
||||
return cache;
|
||||
}
|
||||
|
||||
public static int Run(CultCache cache, bool apply, TextWriter output)
|
||||
{
|
||||
var held = cache.GetAll<StationKindData>().Select(kind => cache.RefOf(kind).Key.Value).ToHashSet(StringComparer.Ordinal);
|
||||
|
|
@ -55,6 +45,9 @@ public static class StationKindsCatalog
|
|||
output.WriteLine(zenith == null ? "Zenith: no such hull, kind not set"
|
||||
: setZenith ? $"Zenith: kind {ZenithKind}" : "Zenith: already has a kind, kept");
|
||||
|
||||
foreach (var hull in cache.GetAll<HullData>().Where(h => h.HullType == HullType.Station && !h.Kind.IsSet() && h != zenith).OrderBy(h => h.Name, StringComparer.Ordinal))
|
||||
output.WriteLine($"Station hull '{hull.Name}' has no kind; set one with: station-kinds hull \"{hull.Name}\" <kind> apply");
|
||||
|
||||
if (!apply)
|
||||
{
|
||||
output.WriteLine("\nDry run. Pass \"apply\" to author them.");
|
||||
|
|
@ -79,4 +72,33 @@ public static class StationKindsCatalog
|
|||
output.WriteLine($"\nAuthored {missing.Length} station kinds{(setZenith ? " and Zenith's kind" : "")} in Aetheria.cc");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Gives a station hull its kind: the way to repair a hull the catalog holds without one. Dry run unless apply.
|
||||
public static int SetHullKind(CultCache cache, string hullName, string kindId, bool apply, TextWriter output)
|
||||
{
|
||||
var hull = cache.GetAll<HullData>().FirstOrDefault(h => h.HullType == HullType.Station && h.Name == hullName);
|
||||
if (hull == null)
|
||||
{
|
||||
output.WriteLine($"The catalog holds no station hull named '{hullName}'.");
|
||||
return 1;
|
||||
}
|
||||
var kind = new CultRecordRef<StationKindData>(StationKindData.KeyOf(kindId));
|
||||
if (cache.Get(kind) == null)
|
||||
{
|
||||
output.WriteLine($"The catalog holds no station kind '{kindId}'.");
|
||||
return 1;
|
||||
}
|
||||
output.WriteLine($"{hull.Name}: kind {kindId}");
|
||||
if (!apply)
|
||||
{
|
||||
output.WriteLine("
|
||||
Dry run. Pass \"apply\" to set it.");
|
||||
return 0;
|
||||
}
|
||||
hull.Kind = kind;
|
||||
cache.Upsert(hull);
|
||||
output.WriteLine($"
|
||||
Set {hull.Name}'s kind in Aetheria.cc");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue