mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 13:50:43 +00:00
Validate spawn tile (#1512)
## Description: Enforce valid tile during spawn, to prevent the game from crashing for all players. ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] 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 - [ ] I have read and accepted the CLA aggreement (only required once).
This commit is contained in:
@@ -385,7 +385,7 @@ export class ClientGameRunner {
|
||||
!this.gameView.hasOwner(tile) &&
|
||||
this.gameView.inSpawnPhase()
|
||||
) {
|
||||
this.eventBus.emit(new SendSpawnIntentEvent(cell));
|
||||
this.eventBus.emit(new SendSpawnIntentEvent(tile));
|
||||
return;
|
||||
}
|
||||
if (this.gameView.inSpawnPhase()) {
|
||||
|
||||
@@ -2,7 +2,6 @@ import { z } from "zod/v4";
|
||||
import { EventBus, GameEvent } from "../core/EventBus";
|
||||
import {
|
||||
AllPlayers,
|
||||
Cell,
|
||||
GameType,
|
||||
Gold,
|
||||
PlayerID,
|
||||
@@ -68,7 +67,7 @@ export class SendAllianceExtensionIntentEvent implements GameEvent {
|
||||
}
|
||||
|
||||
export class SendSpawnIntentEvent implements GameEvent {
|
||||
constructor(public readonly cell: Cell) {}
|
||||
constructor(public readonly tile: TileRef) {}
|
||||
}
|
||||
|
||||
export class SendAttackIntentEvent implements GameEvent {
|
||||
@@ -90,7 +89,7 @@ export class SendBoatAttackIntentEvent implements GameEvent {
|
||||
export class BuildUnitIntentEvent implements GameEvent {
|
||||
constructor(
|
||||
public readonly unit: UnitType,
|
||||
public readonly cell: Cell,
|
||||
public readonly tile: TileRef,
|
||||
) {}
|
||||
}
|
||||
|
||||
@@ -438,8 +437,7 @@ export class Transport {
|
||||
pattern: this.lobbyConfig.pattern,
|
||||
name: this.lobbyConfig.playerName,
|
||||
playerType: PlayerType.Human,
|
||||
x: event.cell.x,
|
||||
y: event.cell.y,
|
||||
tile: event.tile,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -540,8 +538,7 @@ export class Transport {
|
||||
type: "build_unit",
|
||||
clientID: this.lobbyConfig.clientID,
|
||||
unit: event.unit,
|
||||
x: event.cell.x,
|
||||
y: event.cell.y,
|
||||
tile: event.tile,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ import { translateText } from "../../../client/Utils";
|
||||
import { EventBus } from "../../../core/EventBus";
|
||||
import {
|
||||
BuildableUnit,
|
||||
Cell,
|
||||
Gold,
|
||||
PlayerActions,
|
||||
UnitType,
|
||||
@@ -396,12 +395,7 @@ export class BuildMenu extends LitElement implements Layer {
|
||||
),
|
||||
);
|
||||
} else if (buildableUnit.canBuild) {
|
||||
this.eventBus.emit(
|
||||
new BuildUnitIntentEvent(
|
||||
buildableUnit.type,
|
||||
new Cell(this.game.x(tile), this.game.y(tile)),
|
||||
),
|
||||
);
|
||||
this.eventBus.emit(new BuildUnitIntentEvent(buildableUnit.type, tile));
|
||||
}
|
||||
this.hideMenu();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { EventBus } from "../../../core/EventBus";
|
||||
import { Cell, PlayerActions, PlayerID } from "../../../core/game/Game";
|
||||
import { PlayerActions, PlayerID } from "../../../core/game/Game";
|
||||
import { TileRef } from "../../../core/game/GameMap";
|
||||
import { PlayerView } from "../../../core/game/GameView";
|
||||
import {
|
||||
@@ -61,8 +61,9 @@ export class PlayerActionHandler {
|
||||
): Promise<TileRef | false> {
|
||||
return await player.bestTransportShipSpawn(tile);
|
||||
}
|
||||
handleSpawn(spawnCell: Cell) {
|
||||
this.eventBus.emit(new SendSpawnIntentEvent(spawnCell));
|
||||
|
||||
handleSpawn(tile: TileRef) {
|
||||
this.eventBus.emit(new SendSpawnIntentEvent(tile));
|
||||
}
|
||||
|
||||
handleAllianceRequest(player: PlayerView, recipient: PlayerView) {
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
import { Config } from "../../../core/configuration/Config";
|
||||
import {
|
||||
AllPlayers,
|
||||
Cell,
|
||||
PlayerActions,
|
||||
UnitType,
|
||||
} from "../../../core/game/Game";
|
||||
import { AllPlayers, PlayerActions, UnitType } from "../../../core/game/Game";
|
||||
import { TileRef } from "../../../core/game/GameMap";
|
||||
import { GameView, PlayerView } from "../../../core/game/GameView";
|
||||
import { flattenedEmojiTable } from "../../../core/Util";
|
||||
@@ -427,11 +422,7 @@ export const centerButtonElement: CenterButtonElement = {
|
||||
},
|
||||
action: (params: MenuElementParams) => {
|
||||
if (params.game.inSpawnPhase()) {
|
||||
const cell = new Cell(
|
||||
params.game.x(params.tile),
|
||||
params.game.y(params.tile),
|
||||
);
|
||||
params.playerActionHandler.handleSpawn(cell);
|
||||
params.playerActionHandler.handleSpawn(params.tile);
|
||||
} else {
|
||||
params.playerActionHandler.handleAttack(
|
||||
params.myPlayer,
|
||||
|
||||
+2
-4
@@ -255,8 +255,7 @@ export const SpawnIntentSchema = BaseIntentSchema.extend({
|
||||
flag: FlagSchema,
|
||||
pattern: PatternSchema,
|
||||
playerType: PlayerTypeSchema,
|
||||
x: z.number(),
|
||||
y: z.number(),
|
||||
tile: z.number(),
|
||||
});
|
||||
|
||||
export const BoatAttackIntentSchema = BaseIntentSchema.extend({
|
||||
@@ -320,8 +319,7 @@ export const TargetTroopRatioIntentSchema = BaseIntentSchema.extend({
|
||||
export const BuildUnitIntentSchema = BaseIntentSchema.extend({
|
||||
type: z.literal("build_unit"),
|
||||
unit: z.enum(UnitType),
|
||||
x: z.number(),
|
||||
y: z.number(),
|
||||
tile: z.number(),
|
||||
});
|
||||
|
||||
export const UpgradeStructureIntentSchema = BaseIntentSchema.extend({
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import {
|
||||
Cell,
|
||||
Execution,
|
||||
Game,
|
||||
Gold,
|
||||
@@ -27,12 +26,11 @@ export class ConstructionExecution implements Execution {
|
||||
private ticksUntilComplete: Tick;
|
||||
|
||||
private cost: Gold;
|
||||
private tile: TileRef;
|
||||
|
||||
constructor(
|
||||
private player: Player,
|
||||
private constructionType: UnitType,
|
||||
private tileOrCell: TileRef | Cell,
|
||||
private tile: TileRef,
|
||||
) {}
|
||||
|
||||
init(mg: Game, ticks: number): void {
|
||||
@@ -46,17 +44,10 @@ export class ConstructionExecution implements Execution {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.tileOrCell instanceof Cell) {
|
||||
if (!this.mg.isValidCoord(this.tileOrCell.x, this.tileOrCell.y)) {
|
||||
console.warn(
|
||||
`cannot build construction invalid coordinates ${this.tileOrCell.x}, ${this.tileOrCell.y}`,
|
||||
);
|
||||
this.active = false;
|
||||
return;
|
||||
}
|
||||
this.tile = this.mg.ref(this.tileOrCell.x, this.tileOrCell.y);
|
||||
} else {
|
||||
this.tile = this.tileOrCell;
|
||||
if (!this.mg.isValidRef(this.tile)) {
|
||||
console.warn(`cannot build construction invalid tile ${this.tile}`);
|
||||
this.active = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Cell, Execution, Game } from "../game/Game";
|
||||
import { Execution, Game } from "../game/Game";
|
||||
import { PseudoRandom } from "../PseudoRandom";
|
||||
import { ClientID, GameID, Intent, Turn } from "../Schemas";
|
||||
import { simpleHash } from "../Util";
|
||||
@@ -67,10 +67,7 @@ export class Executor {
|
||||
case "move_warship":
|
||||
return new MoveWarshipExecution(player, intent.unitId, intent.tile);
|
||||
case "spawn":
|
||||
return new SpawnExecution(
|
||||
player.info(),
|
||||
this.mg.ref(intent.x, intent.y),
|
||||
);
|
||||
return new SpawnExecution(player.info(), intent.tile);
|
||||
case "boat":
|
||||
return new TransportShipExecution(
|
||||
player,
|
||||
@@ -106,11 +103,7 @@ export class Executor {
|
||||
case "embargo":
|
||||
return new EmbargoExecution(player, intent.targetID, intent.action);
|
||||
case "build_unit":
|
||||
return new ConstructionExecution(
|
||||
player,
|
||||
intent.unit,
|
||||
new Cell(intent.x, intent.y),
|
||||
);
|
||||
return new ConstructionExecution(player, intent.unit, intent.tile);
|
||||
case "allianceExtension": {
|
||||
return new AllianceExtensionExecution(player, intent.recipient);
|
||||
}
|
||||
|
||||
@@ -20,6 +20,11 @@ export class SpawnExecution implements Execution {
|
||||
tick(ticks: number) {
|
||||
this.active = false;
|
||||
|
||||
if (!this.mg.isValidRef(this.tile)) {
|
||||
console.warn(`SpawnExecution: tile ${this.tile} not valid`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.mg.inSpawnPhase()) {
|
||||
this.active = false;
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user