Bugfix: don't allow other players to move warships (#879)

## Description:
The MoveWarshipExecution now verifies that the player who requested to
move owns the warship. This prevents players from moving other players'
warships.

## Please complete the following:

- [x] I have added screenshots for all UI updates
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
- [x] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors

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

evan
This commit is contained in:
evanpelle
2025-05-25 13:24:04 -07:00
committed by evanpelle
parent 27792428a0
commit 182a42e0db
3 changed files with 66 additions and 21 deletions
+1 -1
View File
@@ -63,7 +63,7 @@ export class Executor {
case "cancel_boat":
return new BoatRetreatExecution(playerID, intent.unitID);
case "move_warship":
return new MoveWarshipExecution(intent.unitId, intent.tile);
return new MoveWarshipExecution(player, intent.unitId, intent.tile);
case "spawn":
return new SpawnExecution(
player.info(),
+16 -19
View File
@@ -1,36 +1,33 @@
import { Execution, Game } from "../game/Game";
const cancelDelay = 2;
import { Execution, Game, Player, UnitType } from "../game/Game";
import { TileRef } from "../game/GameMap";
export class MoveWarshipExecution implements Execution {
private active = true;
private mg: Game | null = null;
constructor(
public readonly unitId: number,
public readonly position: number,
private readonly owner: Player,
private readonly unitId: number,
private readonly position: TileRef,
) {}
init(mg: Game, ticks: number): void {
this.mg = mg;
}
tick(ticks: number): void {
if (this.mg === null) {
throw new Error("Not initialized");
}
const warship = this.mg.units().find((u) => u.id() === this.unitId);
const warship = this.owner
.units(UnitType.Warship)
.find((u) => u.id() === this.unitId);
if (!warship) {
console.log("MoveWarshipExecution: warship is already dead");
console.warn("MoveWarshipExecution: warship not found");
return;
}
if (!warship.isActive()) {
console.warn("MoveWarshipExecution: warship is not active");
return;
}
warship.setPatrolTile(this.position);
warship.setTargetTile(undefined);
this.active = false;
}
tick(ticks: number): void {}
isActive(): boolean {
return this.active;
return false;
}
activeDuringSpawnPhase(): boolean {
+49 -1
View File
@@ -175,7 +175,7 @@ describe("Warship", () => {
game.addExecution(new WarshipExecution(warship));
game.addExecution(
new MoveWarshipExecution(warship.id(), game.ref(coastX + 5, 15)),
new MoveWarshipExecution(player1, warship.id(), game.ref(coastX + 5, 15)),
);
executeTicks(game, 10);
@@ -211,4 +211,52 @@ describe("Warship", () => {
// Trade ship should not be captured
expect(tradeShip.owner().id()).toBe(player2.id());
});
test("MoveWarshipExecution fails if player is not the owner", async () => {
const originalPatrolTile = game.ref(coastX + 1, 10);
const warship = player1.buildUnit(
UnitType.Warship,
game.ref(coastX + 1, 5),
{
patrolTile: originalPatrolTile,
},
);
new MoveWarshipExecution(
player2,
warship.id(),
game.ref(coastX + 5, 15),
).init(game, 0);
expect(warship.patrolTile()).toBe(originalPatrolTile);
});
test("MoveWarshipExecution fails if warship is not active", async () => {
const originalPatrolTile = game.ref(coastX + 1, 10);
const warship = player1.buildUnit(
UnitType.Warship,
game.ref(coastX + 1, 5),
{
patrolTile: originalPatrolTile,
},
);
warship.delete();
new MoveWarshipExecution(
player1,
warship.id(),
game.ref(coastX + 5, 15),
).init(game, 0);
expect(warship.patrolTile()).toBe(originalPatrolTile);
});
test("MoveWarshipExecution fails gracefully if warship not found", async () => {
const exec = new MoveWarshipExecution(
player1,
123,
game.ref(coastX + 5, 15),
);
// Verify that no error is thrown.
exec.init(game, 0);
expect(exec.isActive()).toBe(false);
});
});