Skip to content

Reroute trade ships when destination port is no longer valid#3908

Open
lnoss wants to merge 4 commits into
openfrontio:mainfrom
lnoss:reroute-trade-ships
Open

Reroute trade ships when destination port is no longer valid#3908
lnoss wants to merge 4 commits into
openfrontio:mainfrom
lnoss:reroute-trade-ships

Conversation

@lnoss
Copy link
Copy Markdown

@lnoss lnoss commented May 12, 2026

Description:

The trade embargo system adds depth to the game's economy, but it also penalizes players who specifically choose their trading partners. A player might decide to stop trading with players for whatever reason, thereby negatively impacting them financially.

However, by ceasing trade with the other player, they lose their trade ships. This is very disadvantageous if you are a small or medium-sized player, since you are already in a potentially unfavorable situation (not many ports, eventually no train connections) and on top of that you have to wait for the game engine to spawn your new ships (in a spawn pool shared with every other players) that will have to travel a long way again before you can recover the lost money.

By rerouting ships to the nearest valid trading port, we avoid this unnecessary loss of gold and ensure that players who use embargos wisely aren't disrupted or slowed in their gameplay.

redirectownships.mp4

To prevent an unfair advantage, the converse is true: any ships that might be coming from the other player toward our port will also be redirected.

unownedshipsredirected.mp4

I couldn't decide whether to choose the nearest valid port in both general cases (embargo or invalid destination port), or the nearest valid port belonging to the same player (the ship could potentially be rerouted to a port owned by another player that is very close to the ship).

Please complete the following:

  • I have added screenshots for all UI updates
  • I process any text displayed to the user through translateText() and I've added it to the en.json file
  • I have added relevant tests to the test directory
  • I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced

Please put your Discord username so you can be contacted if a bug or regression is found:

DISCORD_USERNAME

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 12, 2026

Review Change Stack

Walkthrough

TradeShipExecution.tick records the ship's current tile early, replaces inline nearest-port selection with a private findNearestValidPort helper, and re-routes ships when destinations or ownership become invalid; ships are deleted if no valid port is found.

Changes

Trade Ship Re-routing Flow

Layer / File(s) Summary
Port selection helper extraction
src/core/execution/TradeShipExecution.ts
New private findNearestValidPort method filters candidate ports by active/non-deleting/non-under-construction and water-component reachability, then returns the closest port by Manhattan distance or null.
Trade ship re-routing and tile recording
src/core/execution/TradeShipExecution.ts
tick captures curTile earlier. When a destination is invalid/untradeable or ownership/capture changes, it calls the helper to find a nearest valid port (other player or current owner as appropriate), updates _dstPort and target, touches the ship, clears motionPlanDst to force re-plan, or deletes the ship if no port exists.
Tests: halfPirate scenario and target assertions
tests/core/executions/TradeShipExecution.test.ts
Adds halfPirate and halfPiratePort, makes tradeShip.setTargetUnit record the chosen port for assertions, includes the new player in game.players(), and adds a test verifying retargeting when destination owner is embargoed.

Sequence Diagram(s)

sequenceDiagram
  participant Tick as TradeShipExecution.tick
  participant Finder as findNearestValidPort
  participant Game as Game.manhattanDist / players
  participant Ship as TradeShipUnit
  Tick->>Ship: read curTile, check dstPort and capture
  Tick->>Finder: request nearest valid port (curTile, player filter)
  Finder->>Game: enumerate players & ports, check water reachability
  Finder-->>Tick: nearest port or null
  alt found
    Tick->>Ship: setTargetUnit(dstPort), touch(), clear motionPlanDst
  else not found
    Tick->>Ship: delete()
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

⛵ A ship once pointed to a shore,
Its target lost, its plans no more.
A helper scans the tiles around,
Finds the closest friendly ground—
And sets the course where safety's found.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The pull request description clearly relates to the changeset, explaining the trade embargo rerouting feature and its motivation with specific examples.
Title check ✅ Passed The title accurately summarizes the main change: rerouting trade ships when destination ports become invalid, which is the core objective of the pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@CLAassistant
Copy link
Copy Markdown

CLAassistant commented May 12, 2026

CLA assistant check
All committers have signed the CLA.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/core/execution/TradeShipExecution.ts`:
- Around line 89-129: The PR modifies TradeShipExecution rerouting logic but
lacks tests; add deterministic unit tests covering the three behaviors: (1)
embargo/inactive destination reroute where wasCaptured is false — set up a trade
ship with an inactive or embargoed _dstPort and verify findNearestValidPort is
called, _dstPort is updated, tradeShip.setTargetUnit and touch are invoked, and
motionPlanDst is reset; (2) captured-ship reroute where wasCaptured is true —
simulate capture so tradeShipOwner !== dstPortOwner or _dstPort inactive, ensure
findNearestValidPort returns the owner’s port, _dstPort updates,
setTargetUnit/touch called and motionPlanDst cleared; and (3) no-valid-port
deletion behavior for both cases — have findNearestValidPort return null and
assert tradeShip.delete(false) is called and active becomes false; target tests
at TradeShipExecution and exercise findNearestValidPort, wasCaptured,
tradeShip.setTargetUnit, tradeShip.delete, and motionPlanDst effects to make
them deterministic.
- Around line 94-97: The reroute filter uses the wrong trade direction: change
the predicate passed to findNearestValidPort so it matches the reroute
precondition (use tradeShipOwner.canTrade(p) rather than
p.canTrade(tradeShipOwner)) and still exclude the original owner; e.g. call
findNearestValidPort(curTile, (p) => p !== this.origOwner &&
tradeShipOwner.canTrade(p)) so trade direction is consistent with the
precondition (referencing findNearestValidPort, tradeShipOwner, origOwner,
canTrade).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ddc62b0d-f89a-433e-8c8b-594c1b2e3fc9

📥 Commits

Reviewing files that changed from the base of the PR and between 990eba6 and af51b89.

📒 Files selected for processing (1)
  • src/core/execution/TradeShipExecution.ts

Comment thread src/core/execution/TradeShipExecution.ts
Comment thread src/core/execution/TradeShipExecution.ts
@github-project-automation github-project-automation Bot moved this from Triage to Development in OpenFront Release Management May 12, 2026
coderabbitai[bot]
coderabbitai Bot previously approved these changes May 12, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/core/executions/TradeShipExecution.test.ts`:
- Line 145: The test's spyOf on game.manhattanDist returns a constant 10, making
all ports equidistant and preventing verification of nearest-port selection;
change the mock to return different distances based on the input tile
coordinates (inspect the two tile args in the mock implementation and return
smaller values for the intended nearest port coordinates and larger values for
others) so that TradeShipExecution's rerouting logic is forced to pick the
correct nearest valid port; update the spyOn for manhattanDist used in
TradeShipExecution.test to a conditional implementation that distinguishes tile
pairs.
- Line 19: currentTarget is declared outside the test lifecycle and not reset,
allowing state to leak between tests; update the test suite's beforeEach to
explicitly set currentTarget = null so each test starts with a clean value
(locate the beforeEach in TradeShipExecution.test.ts and add the reset for the
currentTarget variable declared above it).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8fc3a03e-31b8-454f-8491-f6ce256fc729

📥 Commits

Reviewing files that changed from the base of the PR and between 58a7820 and add87f9.

📒 Files selected for processing (1)
  • tests/core/executions/TradeShipExecution.test.ts

Comment thread tests/core/executions/TradeShipExecution.test.ts
Comment thread tests/core/executions/TradeShipExecution.test.ts
@lnoss lnoss changed the title Reroute trade ships when original destination port is no longer valid Reroute trade ships when destination port is no longer valid May 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Development

Development

Successfully merging this pull request may close these issues.

2 participants