validate tile refs

This commit is contained in:
evanpelle
2025-06-20 10:02:20 -07:00
parent be8770d373
commit b782e5f084
10 changed files with 43 additions and 37 deletions
+4 -11
View File
@@ -13,7 +13,7 @@ import {
import { createGameRecord } from "../core/Util";
import { ServerConfig } from "../core/configuration/Config";
import { getConfig } from "../core/configuration/ConfigLoader";
import { Cell, UnitType } from "../core/game/Game";
import { UnitType } from "../core/game/Game";
import { TileRef } from "../core/game/GameMap";
import {
ErrorUpdate,
@@ -390,22 +390,15 @@ export class ClientGameRunner {
this.gameView.isLand(tile)
) {
this.myPlayer
.bestTransportShipSpawn(this.gameView.ref(cell.x, cell.y))
.bestTransportShipSpawn(tile)
.then((spawn: number | false) => {
if (this.myPlayer === null) throw new Error("not initialized");
let spawnCell: Cell | null = null;
if (spawn !== false) {
spawnCell = new Cell(
this.gameView.x(spawn),
this.gameView.y(spawn),
);
}
this.eventBus.emit(
new SendBoatAttackIntentEvent(
this.gameView.owner(tile).id(),
cell,
tile,
this.myPlayer.troops() * this.renderer.uiState.attackRatio,
spawnCell,
spawn !== false ? spawn : null,
),
);
});
+5 -6
View File
@@ -10,6 +10,7 @@ import {
Tick,
UnitType,
} from "../core/game/Game";
import { TileRef } from "../core/game/GameMap";
import { PlayerView } from "../core/game/GameView";
import {
AllPlayersStats,
@@ -69,9 +70,9 @@ export class SendAttackIntentEvent implements GameEvent {
export class SendBoatAttackIntentEvent implements GameEvent {
constructor(
public readonly targetID: PlayerID | null,
public readonly dst: Cell,
public readonly dst: TileRef,
public readonly troops: number,
public readonly src: Cell | null = null,
public readonly src: TileRef | null = null,
) {}
}
@@ -432,10 +433,8 @@ export class Transport {
clientID: this.lobbyConfig.clientID,
targetID: event.targetID,
troops: event.troops,
dstX: event.dst.x,
dstY: event.dst.y,
srcX: event.src?.x ?? null,
srcY: event.src?.y ?? null,
dst: event.dst,
src: event.src,
});
}
+5 -8
View File
@@ -287,6 +287,9 @@ export class RadialMenu implements Layer {
if (!this.isVisible || this.clickedCell === null) return;
const myPlayer = this.g.myPlayer();
if (myPlayer === null || !myPlayer.isAlive()) return;
if (!this.g.isValidCoord(this.clickedCell.x, this.clickedCell.y)) {
return;
}
const tile = this.g.ref(this.clickedCell.x, this.clickedCell.y);
if (this.originalTileOwner.isPlayer()) {
if (this.g.owner(tile) !== this.originalTileOwner) {
@@ -399,18 +402,12 @@ export class RadialMenu implements Layer {
// BestTransportShipSpawn is an expensive operation, so
// we calculate it here and send the spawn tile to other clients.
myPlayer.bestTransportShipSpawn(tile).then((spawn) => {
let spawnTile: Cell | null = null;
if (spawn !== false) {
spawnTile = new Cell(this.g.x(spawn), this.g.y(spawn));
}
if (this.clickedCell === null) return;
this.eventBus.emit(
new SendBoatAttackIntentEvent(
this.g.owner(tile).id(),
this.clickedCell,
tile,
this.uiState.attackRatio * myPlayer.troops(),
spawnTile,
spawn !== false ? spawn : null,
),
);
});
+2 -4
View File
@@ -198,10 +198,8 @@ export const BoatAttackIntentSchema = BaseIntentSchema.extend({
type: z.literal("boat"),
targetID: ID.nullable(),
troops: z.number(),
dstX: z.number(),
dstY: z.number(),
srcX: z.number().nullable(),
srcY: z.number().nullable(),
dst: z.number(),
src: z.number().nullable(),
});
export const AllianceRequestIntentSchema = BaseIntentSchema.extend({
+2 -7
View File
@@ -1,5 +1,4 @@
import { Execution, Game } from "../game/Game";
import { TileRef } from "../game/GameMap";
import { PseudoRandom } from "../PseudoRandom";
import { ClientID, GameID, Intent, Turn } from "../Schemas";
import { simpleHash } from "../Util";
@@ -70,16 +69,12 @@ export class Executor {
this.mg.ref(intent.x, intent.y),
);
case "boat":
let src: TileRef | null = null;
if (intent.srcX !== null && intent.srcY !== null) {
src = this.mg.ref(intent.srcX, intent.srcY);
}
return new TransportShipExecution(
playerID,
intent.targetID,
this.mg.ref(intent.dstX, intent.dstY),
intent.dst,
intent.troops,
src,
intent.src,
);
case "allianceRequest":
return new AllianceRequestExecution(playerID, intent.recipient);
@@ -9,6 +9,10 @@ export class MoveWarshipExecution implements Execution {
) {}
init(mg: Game, ticks: number): void {
if (!mg.isValidRef(this.position)) {
console.warn(`MoveWarshipExecution: position ${this.position} not valid`);
return;
}
const warship = this.owner
.units(UnitType.Warship)
.find((u) => u.id() === this.unitId);
@@ -60,6 +60,16 @@ export class TransportShipExecution implements Execution {
this.active = false;
return;
}
if (!mg.isValidRef(this.ref)) {
console.warn(`TransportShipExecution: ref ${this.ref} not valid`);
this.active = false;
return;
}
if (this.src !== null && !mg.isValidRef(this.src)) {
console.warn(`TransportShipExecution: src ${this.src} not valid`);
this.active = false;
return;
}
this.lastMove = ticks;
this.mg = mg;
+3
View File
@@ -687,6 +687,9 @@ export class GameImpl implements Game {
ref(x: number, y: number): TileRef {
return this._map.ref(x, y);
}
isValidRef(ref: TileRef): boolean {
return this._map.isValidRef(ref);
}
x(ref: TileRef): number {
return this._map.x(ref);
}
+5 -1
View File
@@ -5,7 +5,7 @@ export type TileUpdate = bigint;
export interface GameMap {
ref(x: number, y: number): TileRef;
isValidRef(ref: TileRef): boolean;
x(ref: TileRef): number;
y(ref: TileRef): number;
cell(ref: TileRef): Cell;
@@ -117,6 +117,10 @@ export class GameMapImpl implements GameMap {
return this.yToRef[y] + x;
}
isValidRef(ref: TileRef): boolean {
return this.isValidCoord(this.x(ref), this.y(ref));
}
x(ref: TileRef): number {
return this.refToX[ref];
}
+3
View File
@@ -481,6 +481,9 @@ export class GameView implements GameMap {
ref(x: number, y: number): TileRef {
return this._map.ref(x, y);
}
isValidRef(ref: TileRef): boolean {
return this._map.isValidRef(ref);
}
x(ref: TileRef): number {
return this._map.x(ref);
}