diff --git a/packages/cli/lib/behavior/advance.dart b/packages/cli/lib/behavior/advance.dart index 2bf6125f..65197d09 100644 --- a/packages/cli/lib/behavior/advance.dart +++ b/packages/cli/lib/behavior/advance.dart @@ -73,6 +73,7 @@ Future advanceShipBehavior( final navResult = await continueNavigationIfNeeded( api, ship, + state, caches.ships, caches.systems, centralCommand, @@ -94,6 +95,13 @@ Future advanceShipBehavior( ship, getNow: getNow, ); + if (state.isComplete) { + // If the behavior is complete, clear it. + centralCommand.completeBehavior(ship.shipSymbol); + } else { + // Otherwise update the behavior state. + centralCommand.setBehavior(ship.shipSymbol, state); + } return waitUntil; } on JobException catch (error) { centralCommand.disableBehaviorForShip( diff --git a/packages/cli/lib/behavior/buy_ship.dart b/packages/cli/lib/behavior/buy_ship.dart index 164c0983..c9cb9edf 100644 --- a/packages/cli/lib/behavior/buy_ship.dart +++ b/packages/cli/lib/behavior/buy_ship.dart @@ -265,6 +265,7 @@ Future advanceBuyShip( return beingNewRouteAndLog( api, ship, + state, caches.ships, caches.systems, caches.routePlanner, diff --git a/packages/cli/lib/behavior/central_command.dart b/packages/cli/lib/behavior/central_command.dart index 2cad70b0..533b50f4 100644 --- a/packages/cli/lib/behavior/central_command.dart +++ b/packages/cli/lib/behavior/central_command.dart @@ -383,22 +383,6 @@ class CentralCommand { return _behaviorCache.deleteBehavior(shipSymbol); } - /// Returns any mount that's been queued for adding to this ship. - ShipMountSymbolEnum? getMountToAdd(ShipSymbol shipSymbol) { - final state = _behaviorCache.getBehavior(shipSymbol); - return state?.mountToAdd; - } - - /// Saves the given mount to be added to the ship. - void claimMount(ShipSymbol shipSymbol, ShipMountSymbolEnum mountSymbol) { - final state = _behaviorCache.getBehavior(shipSymbol); - if (state == null) { - logger.warn('No state for $shipSymbol'); - return; - } - state.mountToAdd = mountSymbol; - } - /// Returns the delivery ship bringing the mounts. Ship? getDeliveryShip(ShipSymbol shipSymbol, TradeSymbol item) { final deliveryShip = _shipCache.ships.first; @@ -455,62 +439,6 @@ class CentralCommand { return available.difference(claimed); } - /// Sets the deliver state for the given ship. - void setBuyJob(Ship ship, BuyJob buyJob) { - final shipSymbol = ship.shipSymbol; - final state = _behaviorCache.getBehavior(shipSymbol); - if (state == null) { - shipWarn(ship, 'No state for $shipSymbol'); - return; - } - state.buyJob = buyJob; - } - - /// Returns the DeliverState for the given ship. - BuyJob? getBuyJob(Ship ship) { - final shipSymbol = ship.shipSymbol; - final state = _behaviorCache.getBehavior(shipSymbol); - return state?.buyJob; - } - - /// Sets the deliver state for the given ship. - void setDeliverJob(Ship ship, DeliverJob deliverJob) { - final shipSymbol = ship.shipSymbol; - final state = _behaviorCache.getBehavior(shipSymbol); - if (state == null) { - shipWarn(ship, 'No state for $shipSymbol'); - return; - } - state.deliverJob = deliverJob; - } - - /// Returns the DeliverState for the given ship. - DeliverJob? getDeliverJob(Ship ship) { - final shipSymbol = ship.shipSymbol; - final state = _behaviorCache.getBehavior(shipSymbol); - return state?.deliverJob; - } - - /// Set the [RoutePlan] for the ship. - void setRoutePlan(Ship ship, RoutePlan routePlan) { - final state = _behaviorCache.getBehavior(ship.shipSymbol)! - ..routePlan = routePlan; - _behaviorCache.setBehavior(ship.shipSymbol, state); - } - - /// Get the current [RoutePlan] for the given ship. - RoutePlan? currentRoutePlan(Ship ship) { - final state = _behaviorCache.getBehavior(ship.shipSymbol); - return state?.routePlan; - } - - /// The [ship] has reached its destination. - void reachedEndOfRoutePlan(Ship ship) { - final state = _behaviorCache.getBehavior(ship.shipSymbol)! - ..routePlan = null; - _behaviorCache.setBehavior(ship.shipSymbol, state); - } - /// Record the given [transactions] for the current deal for [ship]. void recordDealTransactions( Ship ship, diff --git a/packages/cli/lib/behavior/change_mounts.dart b/packages/cli/lib/behavior/change_mounts.dart index c635cf50..6c34cc6d 100644 --- a/packages/cli/lib/behavior/change_mounts.dart +++ b/packages/cli/lib/behavior/change_mounts.dart @@ -38,7 +38,7 @@ Future advanceChangeMounts( Ship ship, { DateTime Function() getNow = defaultGetNow, }) async { - final toMount = centralCommand.getMountToAdd(ship.shipSymbol); + final toMount = state.mountToAdd; // Re-validate every loop in case resuming from error. final template = assertNotNull( @@ -170,12 +170,13 @@ Future advanceChangeMounts( const Duration(minutes: 10), ); shipInfo(ship, 'Claiming mount: $toClaim.'); - centralCommand.claimMount(ship.shipSymbol, toClaim); + state.mountToAdd = toClaim; // Go to the shipyard. final waitUntil = beingNewRouteAndLog( api, ship, + state, caches.ships, caches.systems, caches.routePlanner, diff --git a/packages/cli/lib/behavior/deliver.dart b/packages/cli/lib/behavior/deliver.dart index e3ee433b..d83cf1bf 100644 --- a/packages/cli/lib/behavior/deliver.dart +++ b/packages/cli/lib/behavior/deliver.dart @@ -187,6 +187,7 @@ Future doBuyJob( centralCommand, caches, ship, + state, maybeMarket, buyJob.tradeSymbol, ); @@ -199,6 +200,7 @@ Future doBuyJob( final waitUntil = await beingNewRouteAndLog( api, ship, + state, caches.ships, caches.systems, caches.routePlanner, @@ -309,6 +311,7 @@ Future doDeliverJob( final waitUntil = await beingNewRouteAndLog( api, ship, + state, caches.ships, caches.systems, caches.routePlanner, @@ -346,7 +349,7 @@ Future doInitJob( const Duration(minutes: 20), ); - centralCommand.setBuyJob(ship, buyJob); + state.buyJob = buyJob; final hqSystem = caches.agent.headquartersSymbol.systemSymbol; final hqWaypoints = await caches.waypoints.waypointsInSystem(hqSystem); @@ -356,7 +359,7 @@ Future doInitJob( tradeSymbol: buyJob.tradeSymbol, waypointSymbol: shipyard.waypointSymbol, ); - centralCommand.setDeliverJob(ship, deliverJob); + state.deliverJob = deliverJob; return JobResult.complete(); } diff --git a/packages/cli/lib/behavior/explorer.dart b/packages/cli/lib/behavior/explorer.dart index 213e5186..8eea79d6 100644 --- a/packages/cli/lib/behavior/explorer.dart +++ b/packages/cli/lib/behavior/explorer.dart @@ -222,6 +222,7 @@ Future routeForEmergencyFuelingIfNeeded( CentralCommand centralCommand, Waypoint waypoint, Ship ship, + BehaviorState state, ) async { if (ship.fuelPercentage > 0.4) { return null; @@ -250,6 +251,7 @@ Future routeForEmergencyFuelingIfNeeded( final waitUntil = await beingNewRouteAndLog( api, ship, + state, caches.ships, caches.systems, caches.routePlanner, @@ -314,6 +316,7 @@ Future advanceExplorer( centralCommand, waypoint, ship, + state, ); if (refuelWaitTime != null) { return refuelWaitTime; @@ -350,6 +353,7 @@ Future advanceExplorer( return beingNewRouteAndLog( api, ship, + state, caches.ships, caches.systems, caches.routePlanner, diff --git a/packages/cli/lib/behavior/miner.dart b/packages/cli/lib/behavior/miner.dart index 4919f6d8..a1d608cd 100644 --- a/packages/cli/lib/behavior/miner.dart +++ b/packages/cli/lib/behavior/miner.dart @@ -206,8 +206,8 @@ class MineJob { // - Navigate to mine // - Survey if needed // - Mine +// - Handle cargo (navigate to market, sell, jettison, etc) // - Navigate to market -// - Sell /// Apply the miner behavior to the ship. Future advanceMiner( @@ -272,6 +272,7 @@ Future advanceMiner( return beingNewRouteAndLog( api, ship, + state, caches.ships, caches.systems, caches.routePlanner, @@ -289,6 +290,7 @@ Future advanceMiner( return beingNewRouteAndLog( api, ship, + state, caches.ships, caches.systems, caches.routePlanner, @@ -324,6 +326,8 @@ Future advanceMiner( if (ship.isCommand) { centralCommand.completeBehavior(ship.shipSymbol); } + // We wait the full cooldown because our next action will be either + // surveying or mining, both of which require the reactor cooldown. return response.cooldown.expiration; } diff --git a/packages/cli/lib/behavior/surveyor.dart b/packages/cli/lib/behavior/surveyor.dart index ac283cf8..274fde22 100644 --- a/packages/cli/lib/behavior/surveyor.dart +++ b/packages/cli/lib/behavior/surveyor.dart @@ -25,6 +25,7 @@ Future advanceSurveyor( return beingNewRouteAndLog( api, ship, + state, caches.ships, caches.systems, caches.routePlanner, diff --git a/packages/cli/lib/behavior/trader.dart b/packages/cli/lib/behavior/trader.dart index 36758dc4..39a78a32 100644 --- a/packages/cli/lib/behavior/trader.dart +++ b/packages/cli/lib/behavior/trader.dart @@ -129,6 +129,7 @@ Future _handleAtSourceWithDeal( CentralCommand centralCommand, Caches caches, Ship ship, + BehaviorState state, Market currentMarket, CostedDeal costedDeal, ) async { @@ -192,6 +193,7 @@ Future _handleAtSourceWithDeal( return beingNewRouteAndLog( api, ship, + state, caches.ships, caches.systems, caches.routePlanner, @@ -344,6 +346,7 @@ Future _handleOffCourseWithDeal( CentralCommand centralCommand, Caches caches, Ship ship, + BehaviorState state, CostedDeal costedDeal, ) { final haveDealCargo = ship.cargo.countUnits(costedDeal.tradeSymbol) > 0; @@ -352,6 +355,7 @@ Future _handleOffCourseWithDeal( return beingNewRouteAndLog( api, ship, + state, caches.ships, caches.systems, caches.routePlanner, @@ -365,6 +369,7 @@ Future _handleOffCourseWithDeal( return beingNewRouteAndLog( api, ship, + state, caches.ships, caches.systems, caches.routePlanner, @@ -380,6 +385,7 @@ Future _handleAtDestinationWithDeal( CentralCommand centralCommand, Caches caches, Ship ship, + BehaviorState state, Market currentMarket, CostedDeal costedDeal, ) { @@ -390,6 +396,7 @@ Future _handleAtDestinationWithDeal( return beingNewRouteAndLog( api, ship, + state, caches.ships, caches.systems, caches.routePlanner, @@ -425,6 +432,7 @@ Future _handleDeal( Caches caches, CostedDeal costedDeal, Ship ship, + BehaviorState state, Market? currentMarket, ) async { // If we're at the source buy the cargo. @@ -440,6 +448,7 @@ Future _handleDeal( centralCommand, caches, ship, + state, currentMarket, costedDeal, ); @@ -457,6 +466,7 @@ Future _handleDeal( centralCommand, caches, ship, + state, currentMarket, costedDeal, ); @@ -466,6 +476,7 @@ Future _handleDeal( centralCommand, caches, ship, + state, costedDeal, ); } @@ -536,6 +547,7 @@ Future _navigateToBetterTradeLocation( MarketPrices marketPrices, ShipCache shipCache, Ship ship, + BehaviorState state, String why, ) async { shipWarn(ship, why); @@ -556,6 +568,7 @@ Future _navigateToBetterTradeLocation( final waitUntil = await beingNewRouteAndLog( api, ship, + state, shipCache, systemsCache, routePlanner, @@ -572,6 +585,7 @@ Future handleUnwantedCargoIfNeeded( CentralCommand centralCommand, Caches caches, Ship ship, + BehaviorState state, Market? currentMarket, TradeSymbol? wantedTradeSymbol, ) async { @@ -629,6 +643,7 @@ Future handleUnwantedCargoIfNeeded( final waitUntil = await beingRouteAndLog( api, ship, + state, caches.ships, caches.systems, centralCommand, @@ -693,6 +708,7 @@ Future advanceTrader( centralCommand, caches, ship, + state, currentMarket, pastDeal?.tradeSymbol, ); @@ -709,6 +725,7 @@ Future advanceTrader( caches, pastDeal, ship, + state, currentMarket, ); return waitUntil; @@ -739,6 +756,7 @@ Future advanceTrader( caches.marketPrices, caches.ships, ship, + state, why, ); return waitUntil; @@ -768,6 +786,7 @@ Future advanceTrader( caches, newDeal, ship, + state, currentMarket, ); return waitUntil; diff --git a/packages/cli/lib/nav/navigation.dart b/packages/cli/lib/nav/navigation.dart index 10b1b5dd..8bedd89d 100644 --- a/packages/cli/lib/nav/navigation.dart +++ b/packages/cli/lib/nav/navigation.dart @@ -14,6 +14,7 @@ import 'package:types/types.dart'; Future beingNewRouteAndLog( Api api, Ship ship, + BehaviorState state, ShipCache shipCache, SystemsCache systemsCache, RoutePlanner routePlanner, @@ -43,6 +44,7 @@ Future beingNewRouteAndLog( final waitTime = await beingRouteAndLog( api, ship, + state, shipCache, systemsCache, centralCommand, @@ -57,6 +59,7 @@ Future beingNewRouteAndLog( Future beingRouteAndLog( Api api, Ship ship, + BehaviorState state, ShipCache shipCache, SystemsCache systemsCache, CentralCommand centralCommand, @@ -67,12 +70,13 @@ Future beingRouteAndLog( return null; } - centralCommand.setRoutePlan(ship, routePlan); + state.routePlan = routePlan; // TODO(eseidel): Should this buy fuel first if we need it? shipInfo(ship, 'Beginning route to ${routePlan.endSymbol}'); final navResult = await continueNavigationIfNeeded( api, ship, + state, shipCache, systemsCache, centralCommand, @@ -150,6 +154,7 @@ void _verifyJumpTime( Future continueNavigationIfNeeded( Api api, Ship ship, + BehaviorState state, ShipCache shipCache, SystemsCache systemsCache, CentralCommand centralCommand, { @@ -173,21 +178,21 @@ Future continueNavigationIfNeeded( // has arrived before we got a chance to update it here. ship.nav.status = ShipNavStatus.IN_ORBIT; } - final routePlan = centralCommand.currentRoutePlan(ship); + final routePlan = state.routePlan; if (routePlan == null) { // We don't have a routePlan, so we can't navigate. return NavResult._continueAction(); } if (routePlan.actions.isEmpty) { shipWarn(ship, "Route plan is empty, assuming we're already there."); - centralCommand.reachedEndOfRoutePlan(ship); + state.routePlan = null; return NavResult._continueAction(); } // We've reached the routePlan, so we can stop navigating. if (ship.waypointSymbol == routePlan.endSymbol) { // Remove the destination from the ship's state or it will try to come back. - centralCommand.reachedEndOfRoutePlan(ship); + state.routePlan = null; return NavResult._continueAction(); } final action = routePlan.nextActionFrom(ship.waypointSymbol); diff --git a/packages/cli/test/behavior/central_command_test.dart b/packages/cli/test/behavior/central_command_test.dart index 24947ffe..c9ab4731 100644 --- a/packages/cli/test/behavior/central_command_test.dart +++ b/packages/cli/test/behavior/central_command_test.dart @@ -132,33 +132,27 @@ void main() { when(() => shipA.symbol).thenReturn(aSymbol.symbol); when(() => shipNavA.systemSymbol).thenReturn('S-A'); when(() => shipA.nav).thenReturn(shipNavA); - centralCommand.setBehavior( - aSymbol, - BehaviorState(aSymbol, Behavior.explorer), - ); + final stateA = BehaviorState(aSymbol, Behavior.explorer); + centralCommand.setBehavior(aSymbol, stateA); final saa = WaypointSymbol.fromString('S-A-A'); final saw = WaypointSymbol.fromString('S-A-W'); - centralCommand.setRoutePlan(shipA, fakeJump(saa, saw)); + stateA.routePlan = fakeJump(saa, saw); final shipB = _MockShip(); final shipBSymbol = ShipSymbol.fromString('X-B'); when(() => shipB.symbol).thenReturn(shipBSymbol.symbol); final shipNavB = _MockShipNav(); when(() => shipNavB.systemSymbol).thenReturn('S-C'); when(() => shipB.nav).thenReturn(shipNavB); - centralCommand.setBehavior( - shipBSymbol, - BehaviorState(shipBSymbol, Behavior.explorer), - ); + final stateB = BehaviorState(shipBSymbol, Behavior.explorer); + centralCommand.setBehavior(shipBSymbol, stateB); final sbw = WaypointSymbol.fromString('S-B-W'); - centralCommand.setRoutePlan(shipB, fakeJump(saa, sbw)); - expect(centralCommand.currentRoutePlan(shipB)!.endSymbol, sbw); + stateB.routePlan = fakeJump(saa, sbw); when(() => shipCache.ship(shipBSymbol)).thenReturn(shipB); final otherSystems = centralCommand.otherExplorerSystems(aSymbol).toList(); expect(otherSystems, [sbw.systemSymbol]); // From destination - centralCommand.reachedEndOfRoutePlan(shipB); - expect(centralCommand.currentRoutePlan(shipB), isNull); + stateB.routePlan = null; final otherSystems2 = centralCommand.otherExplorerSystems(aSymbol).toList(); expect( otherSystems2, diff --git a/packages/cli/test/nav/navigation_test.dart b/packages/cli/test/nav/navigation_test.dart index e7fc45fc..2e2d0737 100644 --- a/packages/cli/test/nav/navigation_test.dart +++ b/packages/cli/test/nav/navigation_test.dart @@ -38,8 +38,8 @@ void main() { final centralCommand = _MockCentralCommand(); /// The behavior doesn't matter, just needs to have a null destination. - when(() => centralCommand.getBehavior(shipSymbol)) - .thenAnswer((_) => BehaviorState(shipSymbol, Behavior.idle)); + final state = BehaviorState(shipSymbol, Behavior.idle); + when(() => centralCommand.getBehavior(shipSymbol)).thenAnswer((_) => state); final now = DateTime(2021); DateTime getNow() => now; @@ -56,6 +56,7 @@ void main() { () => continueNavigationIfNeeded( api, ship, + state, shipCache, systemsCache, centralCommand, @@ -79,6 +80,7 @@ void main() { () => continueNavigationIfNeeded( api, ship, + state, shipCache, systemsCache, centralCommand, diff --git a/packages/types/lib/behavior.dart b/packages/types/lib/behavior.dart index efde5ae6..747db20b 100644 --- a/packages/types/lib/behavior.dart +++ b/packages/types/lib/behavior.dart @@ -51,7 +51,7 @@ class BehaviorState { this.buyJob, this.deliverJob, this.jobIndex = 0, - }); + }) : isComplete = false; /// Create a new behavior state from JSON. factory BehaviorState.fromJson(Map json) { @@ -109,6 +109,10 @@ class BehaviorState { /// Mount to add. ShipMountSymbolEnum? mountToAdd; + /// This behavior is complete. + /// Never written to disk (instead the behavior state is deleted). + bool isComplete; + /// Convert this to JSON. Map toJson() { return {