Refactor runtime bootstrap and ship control flows
This commit is contained in:
7
apps/backend/Shared/Contracts/VersionInfo.cs
Normal file
7
apps/backend/Shared/Contracts/VersionInfo.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace SpaceGame.Api.Shared.Contracts;
|
||||
|
||||
public sealed record VersionInfoSnapshot(
|
||||
string Version,
|
||||
string Environment,
|
||||
string? CommitSha,
|
||||
DateTimeOffset StartedAtUtc);
|
||||
29
apps/backend/Shared/Runtime/AppVersionService.cs
Normal file
29
apps/backend/Shared/Runtime/AppVersionService.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using System.Reflection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
namespace SpaceGame.Api.Shared.Runtime;
|
||||
|
||||
public sealed class AppVersionService
|
||||
{
|
||||
private readonly VersionInfoSnapshot _snapshot;
|
||||
|
||||
public AppVersionService(IHostEnvironment environment)
|
||||
{
|
||||
var assembly = Assembly.GetEntryAssembly() ?? Assembly.GetExecutingAssembly();
|
||||
var informationalVersion = assembly
|
||||
.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?
|
||||
.InformationalVersion;
|
||||
var assemblyVersion = assembly.GetName().Version?.ToString() ?? "0.0.0";
|
||||
var version = string.IsNullOrWhiteSpace(informationalVersion) ? assemblyVersion : informationalVersion;
|
||||
var commitSha = Environment.GetEnvironmentVariable("SPACEGAME_COMMIT_SHA")
|
||||
?? Environment.GetEnvironmentVariable("GIT_COMMIT_SHA");
|
||||
|
||||
_snapshot = new VersionInfoSnapshot(
|
||||
version,
|
||||
environment.EnvironmentName,
|
||||
string.IsNullOrWhiteSpace(commitSha) ? null : commitSha,
|
||||
DateTimeOffset.UtcNow);
|
||||
}
|
||||
|
||||
public VersionInfoSnapshot GetSnapshot() => _snapshot;
|
||||
}
|
||||
69
apps/backend/Shared/Runtime/KnownShipTaxonomy.cs
Normal file
69
apps/backend/Shared/Runtime/KnownShipTaxonomy.cs
Normal file
@@ -0,0 +1,69 @@
|
||||
namespace SpaceGame.Api.Shared.Runtime;
|
||||
|
||||
internal static class KnownShipTypes
|
||||
{
|
||||
internal const string Resupplier = "resupplier";
|
||||
internal const string Miner = "miner";
|
||||
internal const string Carrier = "carrier";
|
||||
internal const string Fighter = "fighter";
|
||||
internal const string HeavyFighter = "heavyfighter";
|
||||
internal const string Destroyer = "destroyer";
|
||||
internal const string LargeMiner = "largeminer";
|
||||
internal const string Freighter = "freighter";
|
||||
internal const string Bomber = "bomber";
|
||||
internal const string Scavenger = "scavenger";
|
||||
internal const string Frigate = "frigate";
|
||||
internal const string Transporter = "transporter";
|
||||
internal const string Interceptor = "interceptor";
|
||||
internal const string Scout = "scout";
|
||||
internal const string Courier = "courier";
|
||||
internal const string Builder = "builder";
|
||||
internal const string Corvette = "corvette";
|
||||
internal const string Police = "police";
|
||||
internal const string Battleship = "battleship";
|
||||
internal const string Gunboat = "gunboat";
|
||||
internal const string Tug = "tug";
|
||||
internal const string Compactor = "compactor";
|
||||
}
|
||||
|
||||
internal static class ShipTaxonomyExtensions
|
||||
{
|
||||
internal static string ToDataValue(this ShipPurpose purpose) =>
|
||||
purpose switch
|
||||
{
|
||||
ShipPurpose.Auxiliary => "auxiliary",
|
||||
ShipPurpose.Build => "build",
|
||||
ShipPurpose.Fight => "fight",
|
||||
ShipPurpose.Mine => "mine",
|
||||
ShipPurpose.Trade => "trade",
|
||||
_ => purpose.ToString(),
|
||||
};
|
||||
|
||||
internal static string ToDataValue(this ShipType type) =>
|
||||
type switch
|
||||
{
|
||||
ShipType.Resupplier => "resupplier",
|
||||
ShipType.Miner => "miner",
|
||||
ShipType.Carrier => "carrier",
|
||||
ShipType.Fighter => "fighter",
|
||||
ShipType.HeavyFighter => "heavyfighter",
|
||||
ShipType.Destroyer => "destroyer",
|
||||
ShipType.LargeMiner => "largeminer",
|
||||
ShipType.Freighter => "freighter",
|
||||
ShipType.Bomber => "bomber",
|
||||
ShipType.Scavenger => "scavenger",
|
||||
ShipType.Frigate => "frigate",
|
||||
ShipType.Transporter => "transporter",
|
||||
ShipType.Interceptor => "interceptor",
|
||||
ShipType.Scout => "scout",
|
||||
ShipType.Courier => "courier",
|
||||
ShipType.Builder => "builder",
|
||||
ShipType.Corvette => "corvette",
|
||||
ShipType.Police => "police",
|
||||
ShipType.Battleship => "battleship",
|
||||
ShipType.Gunboat => "gunboat",
|
||||
ShipType.Tug => "tug",
|
||||
ShipType.Compactor => "compactor",
|
||||
_ => type.ToString(),
|
||||
};
|
||||
}
|
||||
121
apps/backend/Shared/Runtime/ShipAutomationCatalog.cs
Normal file
121
apps/backend/Shared/Runtime/ShipAutomationCatalog.cs
Normal file
@@ -0,0 +1,121 @@
|
||||
namespace SpaceGame.Api.Shared.Runtime;
|
||||
|
||||
public enum ShipAutomationSupportStatus
|
||||
{
|
||||
Supported,
|
||||
PartiallySupported,
|
||||
NotSupported,
|
||||
InternalOnly,
|
||||
}
|
||||
|
||||
public sealed record ShipBehaviorDefinition(
|
||||
string Id,
|
||||
string Label,
|
||||
string Category,
|
||||
ShipAutomationSupportStatus SupportStatus,
|
||||
string Notes);
|
||||
|
||||
public sealed record ShipOrderDefinition(
|
||||
string Id,
|
||||
string Label,
|
||||
string Category,
|
||||
ShipAutomationSupportStatus SupportStatus,
|
||||
string Notes);
|
||||
|
||||
public static class ShipBehaviorKinds
|
||||
{
|
||||
public const string Patrol = "patrol";
|
||||
public const string Police = "police";
|
||||
public const string ProtectPosition = "protect-position";
|
||||
public const string ProtectShip = "protect-ship";
|
||||
public const string ProtectStation = "protect-station";
|
||||
|
||||
public const string LocalAutoMine = "local-auto-mine";
|
||||
public const string AdvancedAutoMine = "advanced-auto-mine";
|
||||
public const string ExpertAutoMine = "expert-auto-mine";
|
||||
|
||||
public const string DockAndWait = "dock-and-wait";
|
||||
public const string FlyAndWait = "fly-and-wait";
|
||||
public const string FlyToObject = "fly-to-object";
|
||||
public const string FollowShip = "follow-ship";
|
||||
public const string HoldPosition = "hold-position";
|
||||
|
||||
public const string AutoSalvage = "auto-salvage";
|
||||
|
||||
public const string LocalAutoTrade = "local-auto-trade";
|
||||
public const string AdvancedAutoTrade = "advanced-auto-trade";
|
||||
public const string FillShortages = "fill-shortages";
|
||||
public const string FindBuildTasks = "find-build-tasks";
|
||||
public const string RevisitKnownStations = "revisit-known-stations";
|
||||
public const string SupplyFleet = "supply-fleet";
|
||||
|
||||
public const string RepeatOrders = "repeat-orders";
|
||||
|
||||
public const string AttackTarget = "attack-target";
|
||||
public const string ConstructStation = "construct-station";
|
||||
public const string Idle = "idle";
|
||||
}
|
||||
|
||||
public static class ShipAutomationCatalog
|
||||
{
|
||||
public static readonly IReadOnlyList<ShipBehaviorDefinition> Behaviors =
|
||||
[
|
||||
new(ShipBehaviorKinds.Patrol, "Patrol", "Combat", ShipAutomationSupportStatus.Supported, "Queue-backed behavior synthesizing attack or fly-and-wait orders from the active patrol context."),
|
||||
new(ShipBehaviorKinds.Police, "Police", "Combat", ShipAutomationSupportStatus.Supported, "Queue-backed behavior synthesizing attack or follow-ship inspection orders from the active policing context."),
|
||||
new(ShipBehaviorKinds.ProtectPosition, "Protect Position", "Combat", ShipAutomationSupportStatus.Supported, "Queue-backed behavior synthesizing attack or fly-and-wait orders from the defended position context."),
|
||||
new(ShipBehaviorKinds.ProtectShip, "Protect Ship", "Combat", ShipAutomationSupportStatus.Supported, "Queue-backed behavior synthesizing attack or follow-ship escort orders from the guarded ship context."),
|
||||
new(ShipBehaviorKinds.ProtectStation, "Protect Station", "Combat", ShipAutomationSupportStatus.Supported, "Queue-backed behavior synthesizing attack or fly-and-wait guard orders from the defended station context."),
|
||||
|
||||
new(ShipBehaviorKinds.LocalAutoMine, "Local AutoMine", "Mining", ShipAutomationSupportStatus.PartiallySupported, "Queue-backed for solo mining; broader order-generation model still in progress."),
|
||||
new(ShipBehaviorKinds.AdvancedAutoMine, "Advanced AutoMine", "Mining", ShipAutomationSupportStatus.Supported, "Queue-backed behavior synthesizing an internal mine-and-deliver run order from the current mining opportunity."),
|
||||
new(ShipBehaviorKinds.ExpertAutoMine, "Expert AutoMine", "Mining", ShipAutomationSupportStatus.Supported, "Queue-backed behavior synthesizing an internal mine-and-deliver run order from the current mining opportunity."),
|
||||
|
||||
new(ShipBehaviorKinds.DockAndWait, "Dock And Wait", "Navigation", ShipAutomationSupportStatus.Supported, "Queue-backed behavior using the same building-block order as the direct order."),
|
||||
new(ShipBehaviorKinds.FlyAndWait, "Fly And Wait", "Navigation", ShipAutomationSupportStatus.Supported, "Queue-backed behavior using the same building-block order as the direct order."),
|
||||
new(ShipBehaviorKinds.FlyToObject, "Fly To Object", "Navigation", ShipAutomationSupportStatus.Supported, "Queue-backed behavior using the same building-block order as the direct order."),
|
||||
new(ShipBehaviorKinds.FollowShip, "Follow Ship", "Navigation", ShipAutomationSupportStatus.Supported, "Queue-backed behavior using the same building-block order as the direct order."),
|
||||
new(ShipBehaviorKinds.HoldPosition, "Hold Position", "Navigation", ShipAutomationSupportStatus.Supported, "Default baseline behavior; queue-backed behavior order is active."),
|
||||
|
||||
new(ShipBehaviorKinds.AutoSalvage, "AutoSalvage", "Salvage", ShipAutomationSupportStatus.Supported, "Queue-backed behavior synthesizing an internal salvage run order for wreck recovery."),
|
||||
|
||||
new(ShipBehaviorKinds.LocalAutoTrade, "Local AutoTrade", "Trade", ShipAutomationSupportStatus.Supported, "Queue-backed behavior synthesizing trade-route or dock-and-wait orders from the current market context."),
|
||||
new(ShipBehaviorKinds.AdvancedAutoTrade, "Advanced AutoTrade", "Trade", ShipAutomationSupportStatus.Supported, "Queue-backed behavior synthesizing trade-route orders from the current market context."),
|
||||
new(ShipBehaviorKinds.FillShortages, "Fill Shortages", "Trade", ShipAutomationSupportStatus.Supported, "Queue-backed behavior synthesizing trade-route orders from the current market context."),
|
||||
new(ShipBehaviorKinds.FindBuildTasks, "Find Build Tasks", "Trade", ShipAutomationSupportStatus.Supported, "Queue-backed behavior synthesizing construction-support trade routes from the current market context."),
|
||||
new(ShipBehaviorKinds.RevisitKnownStations, "Revisit Known Stations", "Trade", ShipAutomationSupportStatus.Supported, "Queue-backed behavior synthesizing trade-route or dock-and-wait orders from known-station context."),
|
||||
new(ShipBehaviorKinds.SupplyFleet, "Supply Fleet", "Trade", ShipAutomationSupportStatus.Supported, "Queue-backed behavior synthesizing an internal fleet supply run order."),
|
||||
|
||||
new(ShipBehaviorKinds.RepeatOrders, "Repeat Orders", "Advanced", ShipAutomationSupportStatus.Supported, "Queue-backed behavior generating the current repeat-order template at the bottom of the stack."),
|
||||
|
||||
new(ShipBehaviorKinds.AttackTarget, "Attack Target", "Internal", ShipAutomationSupportStatus.InternalOnly, "Internal gameplay behavior used by current combat/control systems, not an X4 exposed default behavior."),
|
||||
new(ShipBehaviorKinds.ConstructStation, "Construct Station", "Internal", ShipAutomationSupportStatus.InternalOnly, "Internal gameplay behavior used by construction ships."),
|
||||
new(ShipBehaviorKinds.Idle, "Idle", "Internal", ShipAutomationSupportStatus.InternalOnly, "Legacy fallback/internal placeholder; not intended as an exposed player behavior."),
|
||||
];
|
||||
|
||||
public static readonly IReadOnlyList<ShipOrderDefinition> Orders =
|
||||
[
|
||||
new(ShipOrderKinds.DockAndWait, "Dock And Wait", "Navigation", ShipAutomationSupportStatus.PartiallySupported, "Direct order supported in backend."),
|
||||
new(ShipOrderKinds.FlyAndWait, "Fly To And Wait", "Navigation", ShipAutomationSupportStatus.PartiallySupported, "Direct order supported in backend."),
|
||||
new(ShipOrderKinds.FlyToObject, "Fly To Object", "Navigation", ShipAutomationSupportStatus.PartiallySupported, "Direct order supported in backend."),
|
||||
new(ShipOrderKinds.FollowShip, "Follow Ship", "Navigation", ShipAutomationSupportStatus.PartiallySupported, "Direct order supported in backend."),
|
||||
new(ShipOrderKinds.HoldPosition, "Hold Position", "Navigation", ShipAutomationSupportStatus.Supported, "Direct order supported in backend."),
|
||||
new(ShipOrderKinds.Move, "Move", "Navigation", ShipAutomationSupportStatus.PartiallySupported, "Low-level direct movement order; viewer may present richer labels such as Fly To And Wait instead."),
|
||||
|
||||
new(ShipOrderKinds.AttackTarget, "Attack Target", "Combat", ShipAutomationSupportStatus.PartiallySupported, "Direct order supported in backend."),
|
||||
|
||||
new(ShipOrderKinds.MineAndDeliver, "Mine Resource", "Mining", ShipAutomationSupportStatus.Supported, "Direct order mines the requested ware in the requested system until cargo is full."),
|
||||
|
||||
new(ShipOrderKinds.TradeRoute, "Trade Route", "Trade", ShipAutomationSupportStatus.PartiallySupported, "Direct order supported in backend."),
|
||||
|
||||
new(ShipOrderKinds.BuildAtSite, "Build At Site", "Construction", ShipAutomationSupportStatus.PartiallySupported, "Direct order supported in backend."),
|
||||
|
||||
new(ShipOrderKinds.RepeatOrders, "Repeat Orders", "Advanced", ShipAutomationSupportStatus.PartiallySupported, "Represented today as a behavior plus templates, not a normal one-shot direct order."),
|
||||
|
||||
new(ShipOrderKinds.MineLocal, "Mine Local", "Internal", ShipAutomationSupportStatus.InternalOnly, "Internal queue-backed behavior order for Local AutoMine."),
|
||||
new(ShipOrderKinds.MineAndDeliverRun, "Mine And Deliver Run", "Internal", ShipAutomationSupportStatus.InternalOnly, "Internal queue-backed behavior order for Advanced/Expert AutoMine."),
|
||||
new(ShipOrderKinds.SellMinedCargo, "Sell Mined Cargo", "Internal", ShipAutomationSupportStatus.InternalOnly, "Internal queue-backed behavior order for Local AutoMine."),
|
||||
new(ShipOrderKinds.SupplyFleetRun, "Supply Fleet Run", "Internal", ShipAutomationSupportStatus.InternalOnly, "Internal queue-backed behavior order for Supply Fleet."),
|
||||
new(ShipOrderKinds.SalvageRun, "Salvage Run", "Internal", ShipAutomationSupportStatus.InternalOnly, "Internal queue-backed behavior order for AutoSalvage."),
|
||||
new(ShipOrderKinds.Flee, "Flee", "Internal", ShipAutomationSupportStatus.InternalOnly, "Internal emergency order."),
|
||||
];
|
||||
}
|
||||
@@ -28,6 +28,13 @@ public enum OrderStatus
|
||||
Interrupted,
|
||||
}
|
||||
|
||||
public enum ShipOrderSourceKind
|
||||
{
|
||||
Player,
|
||||
Behavior,
|
||||
Commander,
|
||||
}
|
||||
|
||||
public enum AiPlanStatus
|
||||
{
|
||||
Planned,
|
||||
@@ -166,6 +173,11 @@ public static class ShipOrderKinds
|
||||
public const string BuildAtSite = "build-at-site";
|
||||
public const string AttackTarget = "attack-target";
|
||||
public const string HoldPosition = "hold-position";
|
||||
public const string MineLocal = "mine-local";
|
||||
public const string MineAndDeliverRun = "mine-and-deliver-run";
|
||||
public const string SellMinedCargo = "sell-mined-cargo";
|
||||
public const string SupplyFleetRun = "supply-fleet-run";
|
||||
public const string SalvageRun = "salvage-run";
|
||||
public const string RepeatOrders = "repeat-orders";
|
||||
public const string Flee = "flee";
|
||||
}
|
||||
@@ -329,6 +341,14 @@ public static class SimulationEnumMappings
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(kind), kind, null),
|
||||
};
|
||||
|
||||
public static string ToContractValue(this ShipOrderSourceKind kind) => kind switch
|
||||
{
|
||||
ShipOrderSourceKind.Player => "player",
|
||||
ShipOrderSourceKind.Behavior => "behavior",
|
||||
ShipOrderSourceKind.Commander => "commander",
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(kind), kind, null),
|
||||
};
|
||||
|
||||
public static string ToContractValue(this ShipState state) => state switch
|
||||
{
|
||||
ShipState.Idle => "idle",
|
||||
|
||||
@@ -3,8 +3,56 @@ namespace SpaceGame.Api.Shared.Runtime;
|
||||
|
||||
internal static class SimulationRuntimeSupport
|
||||
{
|
||||
internal static bool HasShipCapabilities(ShipDefinition definition, params string[] capabilities) =>
|
||||
capabilities.All(cap => definition.Capabilities.Contains(cap, StringComparer.Ordinal));
|
||||
internal static bool CanWarp(ShipDefinition definition) =>
|
||||
definition.Engines.Count > 0;
|
||||
|
||||
internal static bool CanFtl(ShipDefinition definition) =>
|
||||
definition.Engines.Count > 0;
|
||||
|
||||
internal static bool IsMiningShip(ShipDefinition definition) =>
|
||||
definition.Type is ShipType.Miner or ShipType.LargeMiner;
|
||||
|
||||
internal static bool IsTransportShip(ShipDefinition definition) =>
|
||||
definition.Type is ShipType.Freighter or ShipType.Transporter or ShipType.Courier or ShipType.Resupplier;
|
||||
|
||||
internal static bool IsConstructionShip(ShipDefinition definition) =>
|
||||
definition.Type == ShipType.Builder;
|
||||
|
||||
internal static bool IsMilitaryShip(ShipDefinition definition) =>
|
||||
definition.Type is ShipType.Fighter
|
||||
or ShipType.HeavyFighter
|
||||
or ShipType.Destroyer
|
||||
or ShipType.Bomber
|
||||
or ShipType.Frigate
|
||||
or ShipType.Interceptor
|
||||
or ShipType.Corvette
|
||||
or ShipType.Battleship
|
||||
or ShipType.Gunboat;
|
||||
|
||||
internal static string? GetShipCategory(ShipDefinition definition)
|
||||
{
|
||||
if (IsMilitaryShip(definition))
|
||||
{
|
||||
return "military";
|
||||
}
|
||||
|
||||
if (IsConstructionShip(definition))
|
||||
{
|
||||
return "construction";
|
||||
}
|
||||
|
||||
if (IsTransportShip(definition))
|
||||
{
|
||||
return "transport";
|
||||
}
|
||||
|
||||
if (IsMiningShip(definition))
|
||||
{
|
||||
return "mining";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
internal static int CountStationModules(StationRuntime station, ModuleType moduleType) =>
|
||||
station.Modules.Count(module => module.ModuleType == moduleType);
|
||||
@@ -131,13 +179,13 @@ internal static class SimulationRuntimeSupport
|
||||
modules.All(moduleId => station.Modules.Any(candidate => string.Equals(candidate.ModuleId, moduleId, StringComparison.Ordinal)));
|
||||
|
||||
internal static bool CanExtractNode(ShipRuntime ship, ResourceNodeRuntime node, SimulationWorld world) =>
|
||||
HasShipCapabilities(ship.Definition, "mining")
|
||||
IsMiningShip(ship.Definition)
|
||||
&& world.ItemDefinitions.TryGetValue(node.ItemId, out var item)
|
||||
&& item.CargoKind is not null
|
||||
&& item.CargoKind == ship.Definition.CargoKind;
|
||||
&& ship.Definition.SupportsCargoKind(item.CargoKind.Value);
|
||||
|
||||
internal static bool CanBuildClaimBeacon(ShipRuntime ship) =>
|
||||
string.Equals(ship.Definition.Kind, "military", StringComparison.Ordinal);
|
||||
IsMilitaryShip(ship.Definition);
|
||||
|
||||
internal static float ComputeWorkforceRatio(float population, float workforceRequired)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user