refactor(backend): simplify cargo kind loading

This commit is contained in:
2026-03-27 15:18:42 -04:00
parent e8fb033a01
commit 3237735b08
11 changed files with 35 additions and 82 deletions

View File

@@ -114,10 +114,8 @@ public sealed class ItemDefinition
public required string Id { get; set; } public required string Id { get; set; }
public required string Name { get; set; } public required string Name { get; set; }
public string Description { get; set; } = string.Empty; public string Description { get; set; } = string.Empty;
public string Type { get; set; } = "material";
public string CargoKind { get; set; } = string.Empty;
[JsonIgnore] [JsonIgnore]
public StorageKind? CargoStorageKind { get; set; } public StorageKind? CargoKind { get; set; }
public float Volume { get; set; } = 1f; public float Volume { get; set; } = 1f;
public int Version { get; set; } public int Version { get; set; }
public string FactoryName { get; set; } = string.Empty; public string FactoryName { get; set; } = string.Empty;
@@ -130,7 +128,8 @@ public sealed class ItemDefinition
[JsonPropertyName("transport")] [JsonPropertyName("transport")]
public string Transport public string Transport
{ {
set => CargoKind = value; get => CargoKind?.ToDataValue() ?? string.Empty;
set => CargoKind = value.ToNullableStorageKind();
} }
} }
@@ -338,9 +337,14 @@ public sealed class ShipDefinition
public float FtlSpeed { get; set; } public float FtlSpeed { get; set; }
public float SpoolTime { get; set; } public float SpoolTime { get; set; }
public float CargoCapacity { get; set; } public float CargoCapacity { get; set; }
public string? CargoKind { get; set; }
[JsonIgnore] [JsonIgnore]
public StorageKind? CargoStorageKind { get; set; } public StorageKind? CargoKind { get; set; }
[JsonPropertyName("cargoKind")]
public string? SerializedCargoKind
{
get => CargoKind?.ToDataValue();
set => CargoKind = value.ToNullableStorageKind();
}
public required string Color { get; set; } public required string Color { get; set; }
public required string HullColor { get; set; } public required string HullColor { get; set; }
public float Size { get; set; } public float Size { get; set; }

View File

@@ -14,7 +14,7 @@ internal static class ProductionGraphBuilder
ItemId = item.Id, ItemId = item.Id,
Name = item.Name, Name = item.Name,
Group = item.Group, Group = item.Group,
CargoKind = item.CargoKind, CargoKind = item.CargoKind?.ToDataValue() ?? string.Empty,
}, },
StringComparer.Ordinal); StringComparer.Ordinal);

View File

@@ -265,6 +265,9 @@ public static class SimulationEnumMappings
}; };
} }
public static StorageKind? ToNullableStorageKind(this string? value) =>
string.IsNullOrWhiteSpace(value) ? null : value.ToStorageKind();
public static string ToContractValue(this SpatialNodeKind kind) => kind switch public static string ToContractValue(this SpatialNodeKind kind) => kind switch
{ {
SpatialNodeKind.Star => "star", SpatialNodeKind.Star => "star",

View File

@@ -46,7 +46,7 @@ internal static class SimulationRuntimeSupport
} }
var remaining = station.Inventory var remaining = station.Inventory
.Where(entry => world.ItemDefinitions.TryGetValue(entry.Key, out var definition) && definition.CargoStorageKind == storageKind) .Where(entry => world.ItemDefinitions.TryGetValue(entry.Key, out var definition) && definition.CargoKind == storageKind)
.Sum(entry => entry.Value); .Sum(entry => entry.Value);
foreach (var (module, definition) in storageModules) foreach (var (module, definition) in storageModules)
@@ -114,8 +114,8 @@ internal static class SimulationRuntimeSupport
internal static bool CanExtractNode(ShipRuntime ship, ResourceNodeRuntime node, SimulationWorld world) => internal static bool CanExtractNode(ShipRuntime ship, ResourceNodeRuntime node, SimulationWorld world) =>
HasShipCapabilities(ship.Definition, "mining") HasShipCapabilities(ship.Definition, "mining")
&& world.ItemDefinitions.TryGetValue(node.ItemId, out var item) && world.ItemDefinitions.TryGetValue(node.ItemId, out var item)
&& item.CargoStorageKind is not null && item.CargoKind is not null
&& item.CargoStorageKind == ship.Definition.CargoStorageKind; && item.CargoKind == ship.Definition.CargoKind;
internal static bool CanBuildClaimBeacon(ShipRuntime ship) => internal static bool CanBuildClaimBeacon(ShipRuntime ship) =>
string.Equals(ship.Definition.Kind, "military", StringComparison.Ordinal); string.Equals(ship.Definition.Kind, "military", StringComparison.Ordinal);
@@ -176,7 +176,7 @@ internal static class SimulationRuntimeSupport
return 0f; return 0f;
} }
var storageKind = itemDefinition.CargoStorageKind; var storageKind = itemDefinition.CargoKind;
if (storageKind is null) if (storageKind is null)
{ {
return 0f; return 0f;
@@ -194,7 +194,7 @@ internal static class SimulationRuntimeSupport
} }
var used = station.Inventory var used = station.Inventory
.Where(entry => world.ItemDefinitions.TryGetValue(entry.Key, out var definition) && definition.CargoStorageKind == storageKind) .Where(entry => world.ItemDefinitions.TryGetValue(entry.Key, out var definition) && definition.CargoKind == storageKind)
.Sum(entry => entry.Value); .Sum(entry => entry.Value);
var accepted = MathF.Min(amount, MathF.Max(0f, capacity - used)); var accepted = MathF.Min(amount, MathF.Max(0f, capacity - used));
if (accepted <= 0.01f) if (accepted <= 0.01f)

View File

@@ -2619,7 +2619,7 @@ internal sealed class ShipAiService
{ {
if (world.ItemDefinitions.TryGetValue(itemId, out var itemDefinition)) if (world.ItemDefinitions.TryGetValue(itemId, out var itemDefinition))
{ {
var storageModule = GetStorageRequirement(world.ModuleDefinitions, itemDefinition.CargoStorageKind); var storageModule = GetStorageRequirement(world.ModuleDefinitions, itemDefinition.CargoKind);
if (storageModule is not null && !modules.Contains(storageModule, StringComparer.Ordinal)) if (storageModule is not null && !modules.Contains(storageModule, StringComparer.Ordinal))
{ {
modules.Add(storageModule); modules.Add(storageModule);

View File

@@ -801,7 +801,7 @@ internal sealed class SimulationProjectionService
.Select(storageKind => new StationStorageUsageSnapshot( .Select(storageKind => new StationStorageUsageSnapshot(
storageKind.ToDataValue(), storageKind.ToDataValue(),
station.Inventory station.Inventory
.Where(entry => world.ItemDefinitions.TryGetValue(entry.Key, out var definition) && definition.CargoStorageKind == storageKind) .Where(entry => world.ItemDefinitions.TryGetValue(entry.Key, out var definition) && definition.CargoKind == storageKind)
.Sum(entry => entry.Value), .Sum(entry => entry.Value),
GetStationStorageCapacity(world, station, storageKind))) GetStationStorageCapacity(world, station, storageKind)))
.Where(snapshot => snapshot.Capacity > 0.01f) .Where(snapshot => snapshot.Capacity > 0.01f)

View File

@@ -176,7 +176,7 @@ internal sealed class InfrastructureSimulationService
} }
var used = station.Inventory var used = station.Inventory
.Where(entry => world.ItemDefinitions.TryGetValue(entry.Key, out var def) && def.CargoStorageKind == storageKind) .Where(entry => world.ItemDefinitions.TryGetValue(entry.Key, out var def) && def.CargoKind == storageKind)
.Sum(entry => entry.Value); .Sum(entry => entry.Value);
if (used / capacity >= 0.65f) if (used / capacity >= 0.65f)
{ {
@@ -195,7 +195,7 @@ internal sealed class InfrastructureSimulationService
continue; continue;
} }
if (GetStorageRequirement(world.ModuleDefinitions, itemDefinition.CargoStorageKind) is { } storageModuleId) if (GetStorageRequirement(world.ModuleDefinitions, itemDefinition.CargoKind) is { } storageModuleId)
{ {
yield return storageModuleId; yield return storageModuleId;
} }
@@ -210,7 +210,7 @@ internal sealed class InfrastructureSimulationService
continue; continue;
} }
if (GetStorageRequirement(world.ModuleDefinitions, itemDefinition.CargoStorageKind) is { } storageModuleId) if (GetStorageRequirement(world.ModuleDefinitions, itemDefinition.CargoKind) is { } storageModuleId)
{ {
yield return storageModuleId; yield return storageModuleId;
} }
@@ -325,7 +325,7 @@ internal sealed class InfrastructureSimulationService
var capacity = GetStationStorageCapacity(world, station, storageKind); var capacity = GetStationStorageCapacity(world, station, storageKind);
var used = station.Inventory var used = station.Inventory
.Where(entry => world.ItemDefinitions.TryGetValue(entry.Key, out var def) && def.CargoStorageKind == storageKind) .Where(entry => world.ItemDefinitions.TryGetValue(entry.Key, out var def) && def.CargoKind == storageKind)
.Sum(entry => entry.Value); .Sum(entry => entry.Value);
var utilization = capacity <= 0.01f ? 0f : used / capacity; var utilization = capacity <= 0.01f ? 0f : used / capacity;
@@ -689,12 +689,12 @@ internal sealed class InfrastructureSimulationService
return recipe.Inputs.Any(input => return recipe.Inputs.Any(input =>
world.ItemDefinitions.TryGetValue(input.ItemId, out var itemDefinition) world.ItemDefinitions.TryGetValue(input.ItemId, out var itemDefinition)
&& itemDefinition.CargoStorageKind == storageKind); && itemDefinition.CargoKind == storageKind);
} }
private static bool CommodityUsesStorageClass(SimulationWorld world, string commodityId, StorageKind storageKind) => private static bool CommodityUsesStorageClass(SimulationWorld world, string commodityId, StorageKind storageKind) =>
world.ItemDefinitions.TryGetValue(commodityId, out var itemDefinition) world.ItemDefinitions.TryGetValue(commodityId, out var itemDefinition)
&& itemDefinition.CargoStorageKind == storageKind; && itemDefinition.CargoKind == storageKind;
private static bool CanStationAcceptStationOutputSoon(SimulationWorld world, StationRuntime station, string itemId, float amount) private static bool CanStationAcceptStationOutputSoon(SimulationWorld world, StationRuntime station, string itemId, float amount)
{ {
@@ -703,7 +703,7 @@ internal sealed class InfrastructureSimulationService
return false; return false;
} }
if (itemDefinition.CargoStorageKind is not { } storageKind) if (itemDefinition.CargoKind is not { } storageKind)
{ {
return false; return false;
} }
@@ -715,7 +715,7 @@ internal sealed class InfrastructureSimulationService
} }
var used = station.Inventory var used = station.Inventory
.Where(entry => world.ItemDefinitions.TryGetValue(entry.Key, out var definition) && definition.CargoStorageKind == storageKind) .Where(entry => world.ItemDefinitions.TryGetValue(entry.Key, out var definition) && definition.CargoKind == storageKind)
.Sum(entry => entry.Value); .Sum(entry => entry.Value);
return used + amount <= capacity * 0.95f; return used + amount <= capacity * 0.95f;
} }

View File

@@ -408,7 +408,7 @@ internal sealed class StationSimulationService
return false; return false;
} }
var storageKind = itemDefinition.CargoStorageKind; var storageKind = itemDefinition.CargoKind;
if (storageKind is null) if (storageKind is null)
{ {
return false; return false;
@@ -426,7 +426,7 @@ internal sealed class StationSimulationService
} }
var used = station.Inventory var used = station.Inventory
.Where(entry => world.ItemDefinitions.TryGetValue(entry.Key, out var definition) && definition.CargoStorageKind == storageKind) .Where(entry => world.ItemDefinitions.TryGetValue(entry.Key, out var definition) && definition.CargoKind == storageKind)
.Sum(entry => entry.Value); .Sum(entry => entry.Value);
return used + amount <= capacity + 0.001f; return used + amount <= capacity + 0.001f;
} }

View File

@@ -16,8 +16,8 @@ internal sealed class DataCatalogLoader(string dataRoot)
var authoredSystems = Read<List<SolarSystemDefinition>>("systems.json"); var authoredSystems = Read<List<SolarSystemDefinition>>("systems.json");
var scenario = Read<ScenarioDefinition>("scenario.json"); var scenario = Read<ScenarioDefinition>("scenario.json");
var modules = NormalizeModules(Read<List<ModuleDefinition>>("modules.json")); var modules = NormalizeModules(Read<List<ModuleDefinition>>("modules.json"));
var ships = NormalizeShips(Read<List<ShipDefinition>>("ships.json")); var ships = Read<List<ShipDefinition>>("ships.json");
var items = NormalizeItems(Read<List<ItemDefinition>>("items.json")); var items = Read<List<ItemDefinition>>("items.json");
var balance = Read<BalanceDefinition>("balance.json"); var balance = Read<BalanceDefinition>("balance.json");
var recipes = BuildRecipes(items, ships, modules); var recipes = BuildRecipes(items, ships, modules);
var moduleRecipes = BuildModuleRecipes(modules); var moduleRecipes = BuildModuleRecipes(modules);
@@ -255,60 +255,6 @@ internal sealed class DataCatalogLoader(string dataRoot)
_ => 60, _ => 60,
}; };
private static List<ItemDefinition> NormalizeItems(List<ItemDefinition> items)
{
foreach (var item in items)
{
if (string.IsNullOrWhiteSpace(item.Type))
{
item.Type = string.IsNullOrWhiteSpace(item.Group) ? "material" : item.Group;
}
if (string.IsNullOrWhiteSpace(item.CargoKind))
{
item.CargoStorageKind = null;
}
else
{
try
{
item.CargoStorageKind = item.CargoKind.ToStorageKind();
item.CargoKind = item.CargoStorageKind.Value.ToDataValue();
}
catch (ArgumentOutOfRangeException exception)
{
throw new InvalidOperationException($"Item '{item.Id}' has unsupported cargo kind '{item.CargoKind}'.", exception);
}
}
}
return items;
}
private static List<ShipDefinition> NormalizeShips(List<ShipDefinition> ships)
{
foreach (var ship in ships)
{
if (string.IsNullOrWhiteSpace(ship.CargoKind))
{
ship.CargoStorageKind = null;
continue;
}
try
{
ship.CargoStorageKind = ship.CargoKind.ToStorageKind();
ship.CargoKind = ship.CargoStorageKind.Value.ToDataValue();
}
catch (ArgumentOutOfRangeException exception)
{
throw new InvalidOperationException($"Ship '{ship.Id}' has unsupported cargo kind '{ship.CargoKind}'.", exception);
}
}
return ships;
}
private static List<ModuleDefinition> NormalizeModules(List<ModuleDefinition> modules) private static List<ModuleDefinition> NormalizeModules(List<ModuleDefinition> modules)
{ {
for (var index = 0; index < modules.Count; index += 1) for (var index = 0; index < modules.Count; index += 1)

View File

@@ -218,7 +218,7 @@ internal sealed class WorldBuilder(
continue; continue;
} }
if (SpaceGame.Api.Shared.Runtime.SimulationRuntimeSupport.GetStorageRequirement(moduleDefinitions, itemDefinition.CargoStorageKind) is { } storageModuleId) if (SpaceGame.Api.Shared.Runtime.SimulationRuntimeSupport.GetStorageRequirement(moduleDefinitions, itemDefinition.CargoKind) is { } storageModuleId)
{ {
yield return storageModuleId; yield return storageModuleId;
} }

View File

@@ -241,7 +241,7 @@ internal sealed class WorldSeedingService
continue; continue;
} }
if (SpaceGame.Api.Shared.Runtime.SimulationRuntimeSupport.GetStorageRequirement(world.ModuleDefinitions, itemDefinition.CargoStorageKind) is { } storageModuleId) if (SpaceGame.Api.Shared.Runtime.SimulationRuntimeSupport.GetStorageRequirement(world.ModuleDefinitions, itemDefinition.CargoKind) is { } storageModuleId)
{ {
yield return storageModuleId; yield return storageModuleId;
} }