Enable the sort-keys eslint rule (#1746)

## Description:

Enable the `sort-keys` eslint rule.

Fixes #1629

## 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
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
- [ ] I have read and accepted the CLA agreement (only required once).
This commit is contained in:
Scott Anderson
2025-08-07 19:13:42 -04:00
committed by GitHub
parent 63cb51a4f9
commit b56e380107
48 changed files with 320 additions and 254 deletions
+1
View File
@@ -6,6 +6,7 @@ export const RefreshResponseSchema = z.object({
});
export type RefreshResponse = z.infer<typeof RefreshResponseSchema>;
/* eslint-disable sort-keys */
export const TokenPayloadSchema = z.object({
jti: z.string(),
sub: z
+4
View File
@@ -3,8 +3,10 @@ import { RequiredPatternSchema } from "./Schemas";
export const ProductSchema = z.object({
productId: z.string(),
/* eslint-disable sort-keys */
priceId: z.string(),
price: z.string(),
/* eslint-enable sort-keys */
});
const PatternSchema = z.object({
@@ -16,6 +18,7 @@ const PatternSchema = z.object({
// Schema for resources/cosmetics/cosmetics.json
export const CosmeticsSchema = z.object({
patterns: z.record(z.string(), PatternSchema),
/* eslint-disable sort-keys */
flag: z
.object({
layers: z.record(
@@ -35,6 +38,7 @@ export const CosmeticsSchema = z.object({
),
})
.optional(),
/* eslint-enable sort-keys */
});
export type Cosmetics = z.infer<typeof CosmeticsSchema>;
export type Pattern = z.infer<typeof PatternSchema>;
+3
View File
@@ -2,12 +2,14 @@ import { Cosmetics } from "./CosmeticSchemas";
const ANIMATION_DURATIONS: Record<string, number> = {
rainbow: 4000,
/* eslint-disable sort-keys */
"bright-rainbow": 4000,
"copper-glow": 3000,
"silver-glow": 3000,
"gold-glow": 3000,
neon: 3000,
lava: 6000,
/* eslint-enable sort-keys */
water: 6200,
};
@@ -28,6 +30,7 @@ export function renderPlayerFlag(
const code = flag.slice("!".length);
const layers = code.split("_").map((segment) => {
const [layerKey, colorKey] = segment.split("-");
// eslint-disable-next-line sort-keys
return { layerKey, colorKey };
});
+7 -7
View File
@@ -165,10 +165,10 @@ export class GameRunner {
updates[GameUpdateType.Tile] = [];
this.callBack({
tick: this.game.ticks(),
packedTileUpdates: new BigUint64Array(packedTileUpdates),
updates: updates,
playerNameViewData: this.playerViewData,
tick: this.game.ticks(),
updates: updates,
});
this.isExecuting = false;
}
@@ -181,21 +181,21 @@ export class GameRunner {
const player = this.game.player(playerID);
const tile = this.game.ref(x, y);
const actions = {
canAttack: player.canAttack(tile),
buildableUnits: player.buildableUnits(tile),
canAttack: player.canAttack(tile),
canSendEmojiAllPlayers: player.canSendEmoji(AllPlayers),
} as PlayerActions;
if (this.game.hasOwner(tile)) {
const other = this.game.owner(tile) as Player;
actions.interaction = {
sharedBorder: player.sharesBorderWith(other),
canSendEmoji: player.canSendEmoji(other),
canTarget: player.canTarget(other),
canSendAllianceRequest: player.canSendAllianceRequest(other),
canBreakAlliance: player.isAlliedWith(other),
canDonate: player.canDonate(other),
canEmbargo: !player.hasEmbargoAgainst(other),
canSendAllianceRequest: player.canSendAllianceRequest(other),
canSendEmoji: player.canSendEmoji(other),
canTarget: player.canTarget(other),
sharedBorder: player.sharesBorderWith(other),
};
const alliance = player.allianceWith(other as Player);
if (alliance) {
+60 -60
View File
@@ -144,17 +144,17 @@ const TeamCountConfigSchema = z.union([
export type TeamCountConfig = z.infer<typeof TeamCountConfigSchema>;
export const GameConfigSchema = z.object({
gameMap: z.enum(GameMapType),
difficulty: z.enum(Difficulty),
gameType: z.enum(GameType),
gameMode: z.enum(GameMode),
disableNPCs: z.boolean(),
bots: z.number().int().min(0).max(400),
difficulty: z.enum(Difficulty),
disableNPCs: z.boolean(),
disabledUnits: z.enum(UnitType).array().optional(),
gameMap: z.enum(GameMapType),
gameMode: z.enum(GameMode),
gameType: z.enum(GameType),
infiniteGold: z.boolean(),
infiniteTroops: z.boolean(),
instantBuild: z.boolean(),
maxPlayers: z.number().optional(),
disabledUnits: z.enum(UnitType).array().optional(),
playerTeams: TeamCountConfigSchema.optional(),
});
@@ -244,82 +244,82 @@ const BaseIntentSchema = z.object({
});
export const AllianceExtensionIntentSchema = BaseIntentSchema.extend({
type: z.literal("allianceExtension"),
recipient: ID,
type: z.literal("allianceExtension"),
});
export const AttackIntentSchema = BaseIntentSchema.extend({
type: z.literal("attack"),
targetID: ID.nullable(),
troops: z.number().nonnegative().nullable(),
type: z.literal("attack"),
});
export const SpawnIntentSchema = BaseIntentSchema.extend({
type: z.literal("spawn"),
name: UsernameSchema,
flag: FlagSchema,
name: UsernameSchema,
pattern: PatternSchema,
playerType: PlayerTypeSchema,
tile: z.number(),
type: z.literal("spawn"),
});
export const BoatAttackIntentSchema = BaseIntentSchema.extend({
type: z.literal("boat"),
targetID: ID.nullable(),
troops: z.number().nonnegative(),
dst: z.number(),
src: z.number().nullable(),
targetID: ID.nullable(),
troops: z.number().nonnegative(),
type: z.literal("boat"),
});
export const AllianceRequestIntentSchema = BaseIntentSchema.extend({
type: z.literal("allianceRequest"),
recipient: ID,
type: z.literal("allianceRequest"),
});
export const AllianceRequestReplyIntentSchema = BaseIntentSchema.extend({
type: z.literal("allianceRequestReply"),
requestor: ID, // The one who made the original alliance request
accept: z.boolean(),
requestor: ID, // The one who made the original alliance request
type: z.literal("allianceRequestReply"),
});
export const BreakAllianceIntentSchema = BaseIntentSchema.extend({
type: z.literal("breakAlliance"),
recipient: ID,
type: z.literal("breakAlliance"),
});
export const TargetPlayerIntentSchema = BaseIntentSchema.extend({
type: z.literal("targetPlayer"),
target: ID,
type: z.literal("targetPlayer"),
});
export const EmojiIntentSchema = BaseIntentSchema.extend({
type: z.literal("emoji"),
recipient: z.union([ID, z.literal(AllPlayers)]),
emoji: EmojiSchema,
recipient: z.union([ID, z.literal(AllPlayers)]),
type: z.literal("emoji"),
});
export const EmbargoIntentSchema = BaseIntentSchema.extend({
type: z.literal("embargo"),
targetID: ID,
action: z.union([z.literal("start"), z.literal("stop")]),
targetID: ID,
type: z.literal("embargo"),
});
export const DonateGoldIntentSchema = BaseIntentSchema.extend({
type: z.literal("donate_gold"),
recipient: ID,
gold: z.bigint().nullable(),
recipient: ID,
type: z.literal("donate_gold"),
});
export const DonateTroopIntentSchema = BaseIntentSchema.extend({
type: z.literal("donate_troops"),
recipient: ID,
troops: z.number().nullable(),
type: z.literal("donate_troops"),
});
export const BuildUnitIntentSchema = BaseIntentSchema.extend({
tile: z.number(),
type: z.literal("build_unit"),
unit: z.enum(UnitType),
tile: z.number(),
});
export const UpgradeStructureIntentSchema = BaseIntentSchema.extend({
@@ -329,8 +329,8 @@ export const UpgradeStructureIntentSchema = BaseIntentSchema.extend({
});
export const CancelAttackIntentSchema = BaseIntentSchema.extend({
type: z.literal("cancel_attack"),
attackID: z.string(),
type: z.literal("cancel_attack"),
});
export const CancelBoatIntentSchema = BaseIntentSchema.extend({
@@ -339,9 +339,9 @@ export const CancelBoatIntentSchema = BaseIntentSchema.extend({
});
export const MoveWarshipIntentSchema = BaseIntentSchema.extend({
tile: z.number(),
type: z.literal("move_warship"),
unitId: z.number(),
tile: z.number(),
});
export const DeleteUnitIntentSchema = BaseIntentSchema.extend({
@@ -350,20 +350,20 @@ export const DeleteUnitIntentSchema = BaseIntentSchema.extend({
});
export const QuickChatIntentSchema = BaseIntentSchema.extend({
type: z.literal("quick_chat"),
recipient: ID,
quickChatKey: QuickChatKeySchema,
recipient: ID,
target: ID.optional(),
type: z.literal("quick_chat"),
});
export const MarkDisconnectedIntentSchema = BaseIntentSchema.extend({
type: z.literal("mark_disconnected"),
isDisconnected: z.boolean(),
type: z.literal("mark_disconnected"),
});
export const KickPlayerIntentSchema = BaseIntentSchema.extend({
type: z.literal("kick_player"),
target: ID,
type: z.literal("kick_player"),
});
const IntentSchema = z.discriminatedUnion("type", [
@@ -395,22 +395,22 @@ const IntentSchema = z.discriminatedUnion("type", [
//
export const TurnSchema = z.object({
turnNumber: z.number(),
intents: IntentSchema.array(),
// The hash of the game state at the end of the turn.
hash: z.number().nullable().optional(),
intents: IntentSchema.array(),
turnNumber: z.number(),
});
export const PlayerSchema = z.object({
clientID: ID,
username: UsernameSchema,
flag: FlagSchema,
pattern: PatternSchema,
username: UsernameSchema,
});
export const GameStartInfoSchema = z.object({
gameID: ID,
config: GameConfigSchema,
gameID: ID,
players: PlayerSchema.array(),
});
@@ -427,8 +427,8 @@ export type Winner = z.infer<typeof WinnerSchema>;
//
export const ServerTurnMessageSchema = z.object({
type: z.literal("turn"),
turn: TurnSchema,
type: z.literal("turn"),
});
export const ServerPingMessageSchema = z.object({
@@ -436,30 +436,30 @@ export const ServerPingMessageSchema = z.object({
});
export const ServerPrestartMessageSchema = z.object({
type: z.literal("prestart"),
gameMap: z.nativeEnum(GameMapType),
type: z.literal("prestart"),
});
export const ServerStartGameMessageSchema = z.object({
type: z.literal("start"),
gameStartInfo: GameStartInfoSchema,
// Turns the client missed if they are late to the game.
turns: TurnSchema.array(),
gameStartInfo: GameStartInfoSchema,
type: z.literal("start"),
});
export const ServerDesyncSchema = z.object({
type: z.literal("desync"),
turn: z.number(),
correctHash: z.number().nullable(),
clientsWithCorrectHash: z.number(),
correctHash: z.number().nullable(),
totalActiveClients: z.number(),
turn: z.number(),
type: z.literal("desync"),
yourHash: z.number().optional(),
});
export const ServerErrorSchema = z.object({
type: z.literal("error"),
error: z.string(),
message: z.string().optional(),
type: z.literal("error"),
});
export const ServerMessageSchema = z.discriminatedUnion("type", [
@@ -476,21 +476,21 @@ export const ServerMessageSchema = z.discriminatedUnion("type", [
//
export const ClientSendWinnerSchema = z.object({
allPlayersStats: AllPlayersStatsSchema,
type: z.literal("winner"),
winner: WinnerSchema,
allPlayersStats: AllPlayersStatsSchema,
});
export const ClientHashSchema = z.object({
type: z.literal("hash"),
hash: z.number(),
turnNumber: z.number(),
type: z.literal("hash"),
});
export const ClientLogMessageSchema = z.object({
type: z.literal("log"),
severity: z.enum(LogSeverity),
log: ID,
severity: z.enum(LogSeverity),
type: z.literal("log"),
});
export const ClientPingMessageSchema = z.object({
@@ -498,20 +498,20 @@ export const ClientPingMessageSchema = z.object({
});
export const ClientIntentMessageSchema = z.object({
type: z.literal("intent"),
intent: IntentSchema,
type: z.literal("intent"),
});
// WARNING: never send this message to clients.
export const ClientJoinMessageSchema = z.object({
type: z.literal("join"),
clientID: ID,
token: TokenSchema, // WARNING: PII
flag: FlagSchema,
gameID: ID,
lastTurn: z.number(), // The last turn the client saw.
username: UsernameSchema,
flag: FlagSchema,
pattern: PatternSchema,
token: TokenSchema, // WARNING: PII
type: z.literal("join"),
username: UsernameSchema,
});
export const ClientMessageSchema = z.discriminatedUnion("type", [
@@ -534,11 +534,11 @@ export const PlayerRecordSchema = PlayerSchema.extend({
export type PlayerRecord = z.infer<typeof PlayerRecordSchema>;
export const GameEndInfoSchema = GameStartInfoSchema.extend({
duration: z.number().nonnegative(),
end: z.number(),
num_turns: z.number(),
players: PlayerRecordSchema.array(),
start: z.number(),
end: z.number(),
duration: z.number().nonnegative(),
num_turns: z.number(),
winner: WinnerSchema,
});
export type GameEndInfo = z.infer<typeof GameEndInfoSchema>;
@@ -546,11 +546,11 @@ export type GameEndInfo = z.infer<typeof GameEndInfoSchema>;
const GitCommitSchema = z.string().regex(/^[0-9a-fA-F]{40}$/);
export const AnalyticsRecordSchema = z.object({
info: GameEndInfoSchema,
version: z.literal("v0.0.2"),
gitCommit: GitCommitSchema,
subdomain: z.string(),
domain: z.string(),
gitCommit: GitCommitSchema,
info: GameEndInfoSchema,
subdomain: z.string(),
version: z.literal("v0.0.2"),
});
export type AnalyticsRecord = z.infer<typeof AnalyticsRecordSchema>;
+13 -12
View File
@@ -88,6 +88,7 @@ export function calculateBoundingBox(
maxY = Math.max(maxY, cell.y);
});
// eslint-disable-next-line sort-keys
return { min: new Cell(minX, minY), max: new Cell(maxX, maxY) };
}
@@ -143,10 +144,10 @@ export function sanitize(name: string): string {
export function onlyImages(html: string) {
return DOMPurify.sanitize(html, {
ALLOWED_TAGS: ["span", "img"],
ALLOWED_ATTR: ["src", "alt", "class", "style"],
ALLOWED_URI_REGEXP: /^https:\/\/cdn\.jsdelivr\.net\/gh\/twitter\/twemoji/,
ADD_ATTR: ["style"],
ALLOWED_ATTR: ["src", "alt", "class", "style"],
ALLOWED_TAGS: ["span", "img"],
ALLOWED_URI_REGEXP: /^https:\/\/cdn\.jsdelivr\.net\/gh\/twitter\/twemoji/,
});
}
@@ -171,21 +172,21 @@ export function createGameRecord(
(t) => t.intents.length !== 0 || t.hash !== undefined,
);
const record: GameRecord = {
domain,
gitCommit,
info: {
gameID,
config,
duration,
end,
gameID,
num_turns,
players,
start,
end,
duration,
num_turns,
winner,
},
version,
gitCommit,
subdomain,
domain,
turns,
version,
};
return record;
}
@@ -197,8 +198,8 @@ export function decompressGameRecord(gameRecord: GameRecord) {
while (lastTurnNum < turn.turnNumber - 1) {
lastTurnNum++;
turns.push({
turnNumber: lastTurnNum,
intents: [],
turnNumber: lastTurnNum,
});
}
turns.push(turn);
@@ -207,8 +208,8 @@ export function decompressGameRecord(gameRecord: GameRecord) {
const turnLength = turns.length;
for (let i = turnLength; i < gameRecord.info.num_turns; i++) {
turns.push({
turnNumber: i,
intents: [],
turnNumber: i,
});
}
gameRecord.turns = turns;
+1
View File
@@ -5,6 +5,7 @@ import lchPlugin from "colord/plugins/lch";
extend([lchPlugin]);
extend([labPlugin]);
/* eslint-disable sort-keys */
export const red = colord({ h: 0, s: 82, l: 56 });
export const blue = colord({ h: 224, s: 100, l: 58 });
export const teal = colord({ h: 172, s: 66, l: 50 });
+2
View File
@@ -367,6 +367,7 @@ export class DefaultConfig implements Config {
return 1_000_000;
}
/* eslint-disable sort-keys */
unitInfo(type: UnitType): UnitInfo {
switch (type) {
case UnitType.TransportShip:
@@ -488,6 +489,7 @@ export class DefaultConfig implements Config {
assertNever(type);
}
}
/* eslint-enable sort-keys */
private costWrapper(
type: UnitType,
+10
View File
@@ -17,6 +17,7 @@ export class PastelTheme implements Theme {
private teamColorAllocator = new ColorAllocator(humanColors, fallbackColors);
private nationColorAllocator = new ColorAllocator(nationColors, nationColors);
/* eslint-disable sort-keys */
private background = colord({ r: 60, g: 60, b: 60 });
private shore = colord({ r: 204, g: 203, b: 158 });
private falloutColors = [
@@ -34,6 +35,7 @@ export class PastelTheme implements Theme {
private _enemyColor = colord({ r: 255, g: 0, b: 0 });
private _spawnHighlightColor = colord({ r: 255, g: 213, b: 79 });
/* eslint-enable sort-keys */
teamColor(team: Team): Colord {
return this.teamColorAllocator.assignTeamColor(team);
@@ -59,20 +61,24 @@ export class PastelTheme implements Theme {
specialBuildingColor(player: PlayerView): Colord {
const tc = this.territoryColor(player).rgba;
/* eslint-disable sort-keys */
return colord({
r: Math.max(tc.r - 50, 0),
g: Math.max(tc.g - 50, 0),
b: Math.max(tc.b - 50, 0),
});
/* eslint-enable sort-keys */
}
railroadColor(player: PlayerView): Colord {
const tc = this.territoryColor(player).rgba;
/* eslint-disable sort-keys */
const color = colord({
r: Math.max(tc.r - 10, 0),
g: Math.max(tc.g - 10, 0),
b: Math.max(tc.b - 10, 0),
});
/* eslint-enable sort-keys */
return color;
}
@@ -81,16 +87,19 @@ export class PastelTheme implements Theme {
return this.borderColorCache.get(player.id())!;
}
const tc = this.territoryColor(player).rgba;
/* eslint-disable sort-keys */
const color = colord({
r: Math.max(tc.r - 40, 0),
g: Math.max(tc.g - 40, 0),
b: Math.max(tc.b - 40, 0),
});
/* eslint-enable sort-keys */
this.borderColorCache.set(player.id(), color);
return color;
}
/* eslint-disable sort-keys */
defendedBorderColors(player: PlayerView): { light: Colord; dark: Colord } {
return {
light: this.territoryColor(player).darken(0.2),
@@ -140,6 +149,7 @@ export class PastelTheme implements Theme {
});
}
}
/* eslint-enable sort-keys */
backgroundColor(): Colord {
return this.background;
@@ -17,6 +17,7 @@ export class PastelThemeDark implements Theme {
private teamColorAllocator = new ColorAllocator(humanColors, fallbackColors);
private nationColorAllocator = new ColorAllocator(nationColors, nationColors);
/* eslint-disable sort-keys */
private background = colord({ r: 0, g: 0, b: 0 });
private shore = colord({ r: 134, g: 133, b: 88 });
private falloutColors = [
@@ -34,6 +35,7 @@ export class PastelThemeDark implements Theme {
private _enemyColor = colord({ r: 255, g: 0, b: 0 });
private _spawnHighlightColor = colord({ r: 255, g: 213, b: 79 });
/* eslint-enable sort-keys */
teamColor(team: Team): Colord {
return this.teamColorAllocator.assignTeamColor(team);
@@ -57,6 +59,7 @@ export class PastelThemeDark implements Theme {
return player.type() === PlayerType.Human ? "#ffffff" : "#e6e6e6";
}
/* eslint-disable sort-keys */
specialBuildingColor(player: PlayerView): Colord {
const tc = this.territoryColor(player).rgba;
return colord({
@@ -142,6 +145,7 @@ export class PastelThemeDark implements Theme {
});
}
}
/* eslint-enable sort-keys */
backgroundColor(): Colord {
return this.background;
+2 -1
View File
@@ -184,9 +184,10 @@ export class NukeExecution implements Execution {
this.mg.config().defaultNukeTargetableRange() ** 2;
const allTiles: TileRef[] = this.pathFinder.allTiles();
for (const tile of allTiles) {
const targetable = this.isTargetable(target, tile, targetRangeSquared);
trajectoryTiles.push({
targetable,
tile,
targetable: this.isTargetable(target, tile, targetRangeSquared),
});
}
+3 -1
View File
@@ -18,6 +18,7 @@ export class RailroadExecution implements Execution {
return this.active;
}
/* eslint-disable sort-keys */
init(mg: Game, ticks: number): void {
this.mg = mg;
const tiles = this.railRoad.tiles;
@@ -48,6 +49,7 @@ export class RailroadExecution implements Execution {
: RailType.VERTICAL,
});
}
/* eslint-enable sort-keys */
private computeExtremityDirection(tile: TileRef, next: TileRef): RailType {
const x = this.mg.x(tile);
@@ -143,9 +145,9 @@ export class RailroadExecution implements Execution {
}
if (updatedRailTiles) {
this.mg.addUpdate({
type: GameUpdateType.RailroadEvent,
isActive: true,
railTiles: updatedRailTiles,
type: GameUpdateType.RailroadEvent,
});
}
}
@@ -98,6 +98,7 @@ class SAMTargetingSystem {
}
const interceptionTile = this.computeInterceptionTile(nuke.unit);
if (interceptionTile !== undefined) {
// eslint-disable-next-line sort-keys
targets.push({ unit: nuke.unit, tile: interceptionTile });
} else {
// Store unreachable nukes in order to prevent useless interception computation
+1 -1
View File
@@ -43,8 +43,8 @@ export class TradeShipExecution implements Execution {
return;
}
this.tradeShip = this.origOwner.buildUnit(UnitType.TradeShip, spawn, {
targetUnit: this._dstPort,
lastSetSafeFromPirates: ticks,
targetUnit: this._dstPort,
});
this.mg.stats().boatSendTrade(this.origOwner, this._dstPort.owner());
}
+1 -1
View File
@@ -119,8 +119,8 @@ export class TrainExecution implements Execution {
for (let i = 0; i < this.numCars; i++) {
this.cars.push(
this.player.buildUnit(UnitType.Train, tile, {
trainType: TrainType.Carriage,
loaded: this.hasCargo,
trainType: TrainType.Carriage,
}),
);
}
+1 -1
View File
@@ -113,7 +113,7 @@ export class WarshipExecution implements Execution {
continue;
}
}
potentialTargets.push({ unit: unit, distSquared });
potentialTargets.push({ distSquared, unit });
}
return potentialTargets.sort((a, b) => {
+3 -3
View File
@@ -31,10 +31,10 @@ export class AllianceRequestImpl implements AllianceRequest {
toUpdate(): AllianceRequestUpdate {
return {
type: GameUpdateType.AllianceRequest,
requestorID: this.requestor_.smallID(),
recipientID: this.recipient_.smallID(),
createdAt: this.tickCreated,
recipientID: this.recipient_.smallID(),
requestorID: this.requestor_.smallID(),
type: GameUpdateType.AllianceRequest,
};
}
}
+7 -7
View File
@@ -37,6 +37,13 @@ export class BinaryLoaderGameMapLoader implements GameMapLoader {
const fileName = key?.toLowerCase();
const mapData = {
manifest: this.createLazyLoader(() =>
(
import(
`../../../resources/maps/${fileName}/manifest.json`
) as Promise<NationMapModule>
).then((m) => m.default),
),
mapBin: this.createLazyLoader(() =>
(
import(
@@ -51,13 +58,6 @@ export class BinaryLoaderGameMapLoader implements GameMapLoader {
) as Promise<BinModule>
).then((m) => this.toUInt8Array(m.default)),
),
manifest: this.createLazyLoader(() =>
(
import(
`../../../resources/maps/${fileName}/manifest.json`
) as Promise<NationMapModule>
).then((m) => m.default),
),
webpPath: this.createLazyLoader(() =>
(
import(
+1 -1
View File
@@ -27,10 +27,10 @@ export class FetchGameMapLoader implements GameMapLoader {
}
const mapData = {
manifest: () => this.loadJsonFromUrl(this.url(fileName, "manifest.json")),
mapBin: () => this.loadBinaryFromUrl(this.url(fileName, "map.bin")),
miniMapBin: () =>
this.loadBinaryFromUrl(this.url(fileName, "mini_map.bin")),
manifest: () => this.loadJsonFromUrl(this.url(fileName, "manifest.json")),
webpPath: async () => this.url(fileName, "thumbnail.webp"),
} satisfies MapData;
+3
View File
@@ -44,6 +44,7 @@ export const Duos = "Duos" as const;
export const Trios = "Trios" as const;
export const Quads = "Quads" as const;
/* eslint-disable sort-keys */
export const ColoredTeams: Record<string, Team> = {
Red: "Red",
Blue: "Blue",
@@ -54,6 +55,7 @@ export const ColoredTeams: Record<string, Team> = {
Green: "Green",
Bot: "Bot",
} as const;
/* eslint-enable sort-keys */
export enum GameMapType {
World = "World",
@@ -118,6 +120,7 @@ export const mapCategories: Record<string, GameMapType[]> = {
GameMapType.Italia,
GameMapType.Yenisei,
],
// eslint-disable-next-line sort-keys
fantasy: [
GameMapType.Pangaea,
GameMapType.Pluto,
+27 -27
View File
@@ -292,9 +292,9 @@ export class GameImpl implements Game {
recipient.endTemporaryEmbargo(requestor.id());
this.addUpdate({
type: GameUpdateType.AllianceRequestReply,
request: request.toUpdate(),
accepted: true,
request: request.toUpdate(),
type: GameUpdateType.AllianceRequestReply,
});
}
@@ -306,9 +306,9 @@ export class GameImpl implements Game {
request,
);
this.addUpdate({
type: GameUpdateType.AllianceRequestReply,
request: request.toUpdate(),
accepted: false,
request: request.toUpdate(),
type: GameUpdateType.AllianceRequestReply,
});
}
@@ -358,9 +358,9 @@ export class GameImpl implements Game {
}
if (this.ticks() % 10 === 0) {
this.addUpdate({
type: GameUpdateType.Hash,
tick: this.ticks(),
hash: this.hash(),
tick: this.ticks(),
type: GameUpdateType.Hash,
});
}
this._ticks++;
@@ -573,9 +573,9 @@ export class GameImpl implements Game {
target(targeter: Player, target: Player) {
this.addUpdate({
type: GameUpdateType.TargetPlayer,
playerID: targeter.smallID(),
targetID: target.smallID(),
type: GameUpdateType.TargetPlayer,
});
}
@@ -604,9 +604,9 @@ export class GameImpl implements Game {
}
this.alliances_ = this.alliances_.filter((a) => a !== alliances[0]);
this.addUpdate({
type: GameUpdateType.BrokeAlliance,
traitorID: breaker.smallID(),
betrayedID: other.smallID(),
traitorID: breaker.smallID(),
type: GameUpdateType.BrokeAlliance,
});
}
@@ -623,24 +623,24 @@ export class GameImpl implements Game {
}
this.alliances_ = this.alliances_.filter((a) => a !== alliances[0]);
this.addUpdate({
type: GameUpdateType.AllianceExpired,
player1ID: alliance.requestor().smallID(),
player2ID: alliance.recipient().smallID(),
type: GameUpdateType.AllianceExpired,
});
}
sendEmojiUpdate(msg: EmojiMessage): void {
this.addUpdate({
type: GameUpdateType.Emoji,
emoji: msg,
type: GameUpdateType.Emoji,
});
}
setWinner(winner: Player | Team, allPlayersStats: AllPlayersStats): void {
this.addUpdate({
allPlayersStats,
type: GameUpdateType.Win,
winner: this.makeWinner(winner),
allPlayersStats,
});
}
@@ -683,12 +683,12 @@ export class GameImpl implements Game {
id = this.player(playerID).smallID();
}
this.addUpdate({
type: GameUpdateType.DisplayEvent,
goldAmount,
message,
messageType: type,
message: message,
params,
playerID: id,
goldAmount: goldAmount,
params: params,
type: GameUpdateType.DisplayEvent,
});
}
@@ -705,13 +705,13 @@ export class GameImpl implements Game {
id = this.player(playerID).smallID();
}
this.addUpdate({
type: GameUpdateType.DisplayChatEvent,
key: message,
category: category,
target: target,
playerID: id,
category,
isFrom,
recipient: recipient,
key: message,
playerID: id,
recipient,
target,
type: GameUpdateType.DisplayChatEvent,
});
}
@@ -724,11 +724,11 @@ export class GameImpl implements Game {
const id = this.player(playerID).smallID();
this.addUpdate({
type: GameUpdateType.UnitIncoming,
unitID: unitID,
message: message,
message,
messageType: type,
playerID: id,
type: GameUpdateType.UnitIncoming,
unitID,
});
}
@@ -889,10 +889,10 @@ export class GameImpl implements Game {
conqueror.addGold(gold);
conquered.removeGold(gold);
this.addUpdate({
type: GameUpdateType.ConquestEvent,
conquerorId: conqueror.id(),
conqueredId: conquered.id(),
conquerorId: conqueror.id(),
gold,
type: GameUpdateType.ConquestEvent,
});
// Record stats
+10 -7
View File
@@ -126,6 +126,7 @@ export class PlayerImpl implements Player {
);
const stats = this.mg.stats().getPlayerStats(this);
/* eslint-disable sort-keys */
return {
type: GameUpdateType.Player,
clientID: this.clientID(),
@@ -176,6 +177,7 @@ export class PlayerImpl implements Player {
hasSpawned: this.hasSpawned(),
betrayals: stats?.betrayals,
};
/* eslint-enable sort-keys */
}
smallID(): number {
@@ -508,6 +510,7 @@ export class PlayerImpl implements Player {
}
target(other: Player): void {
// eslint-disable-next-line sort-keys
this.targets_.push({ tick: this.mg.ticks(), target: other });
this.mg.target(this, other);
}
@@ -533,10 +536,10 @@ export class PlayerImpl implements Player {
throw Error(`Cannot send emoji to oneself: ${this}`);
}
const msg: EmojiMessage = {
message: emoji,
senderID: this.smallID(),
recipientID: recipient === AllPlayers ? recipient : recipient.smallID(),
createdAt: this.mg.ticks(),
message: emoji,
recipientID: recipient === AllPlayers ? recipient : recipient.smallID(),
senderID: this.smallID(),
};
this.outgoingEmojis_.push(msg);
this.mg.sendEmojiUpdate(msg);
@@ -716,11 +719,11 @@ export class PlayerImpl implements Player {
this._gold += toAdd;
if (tile) {
this.mg.addUpdate({
type: GameUpdateType.BonusEvent,
gold: Number(toAdd),
player: this.id(),
tile,
gold: Number(toAdd),
troops: 0,
type: GameUpdateType.BonusEvent,
});
}
}
@@ -837,12 +840,12 @@ export class PlayerImpl implements Player {
}
}
return {
type: u,
canBuild: this.mg.inSpawnPhase()
? false
: this.canBuild(u, tile, validTiles),
canUpgrade: canUpgrade,
cost: this.mg.config().unitInfo(u).cost(this),
type: u,
} as BuildableUnit;
});
}
@@ -1042,13 +1045,13 @@ export class PlayerImpl implements Player {
public playerProfile(): PlayerProfile {
const rel = {
alliances: this.alliances().map((a) => a.other(this).smallID()),
relations: Object.fromEntries(
this.allRelationsSorted().map(({ player, relation }) => [
player.smallID(),
relation,
]),
),
alliances: this.alliances().map((a) => a.other(this).smallID()),
};
return rel;
}
+2
View File
@@ -216,6 +216,7 @@ export class RailNetworkImpl implements RailNetwork {
const visited = new Set<TrainStation>();
const queue: Array<{ station: TrainStation; distance: number }> = [
// eslint-disable-next-line sort-keys
{ station: start, distance: 0 },
];
@@ -229,6 +230,7 @@ export class RailNetworkImpl implements RailNetwork {
for (const neighbor of station.neighbors()) {
if (neighbor === dest) return distance + 1;
if (!visited.has(neighbor)) {
// eslint-disable-next-line sort-keys
queue.push({ station: neighbor, distance: distance + 1 });
}
}
+2 -2
View File
@@ -12,13 +12,13 @@ export class Railroad {
delete(game: Game) {
const railTiles: RailTile[] = this.tiles.map((tile) => ({
tile,
railType: RailType.VERTICAL,
tile,
}));
game.addUpdate({
type: GameUpdateType.RailroadEvent,
isActive: false,
railTiles,
type: GameUpdateType.RailroadEvent,
});
this.from.getRailroads().delete(this);
this.to.getRailroads().delete(this);
+1 -1
View File
@@ -48,8 +48,8 @@ export async function loadTerrainMap(
await mapFiles.miniMapBin(),
);
const result = {
manifest: await mapFiles.manifest(),
gameMap: gameMap,
manifest: await mapFiles.manifest(),
miniGameMap: miniGameMap,
};
loadedMaps.set(map, result);
+2 -2
View File
@@ -119,13 +119,13 @@ export class TrainStation {
);
if (toRemove) {
const railTiles: RailTile[] = toRemove.tiles.map((tile) => ({
tile,
railType: RailType.VERTICAL,
tile,
}));
this.mg.addUpdate({
type: GameUpdateType.RailroadEvent,
isActive: false,
railTiles,
type: GameUpdateType.RailroadEvent,
});
this.railroads.delete(toRemove);
}
+2 -2
View File
@@ -183,10 +183,10 @@ export function candidateShoreTiles(
let bestByManhattan: TileRef | null = null;
const extremumTiles: Record<string, TileRef | null> = {
minX: null,
minY: null,
maxX: null,
maxY: null,
minX: null,
minY: null,
};
const borderShoreTiles = Array.from(player.borderTiles()).filter((t) =>
+2 -1
View File
@@ -113,7 +113,7 @@ export class UnitGrid {
gridY + Math.ceil((range - (cellSize - (y % cellSize))) / cellSize),
);
return { startGridX, endGridX, startGridY, endGridY };
return { endGridX, endGridY, startGridX, startGridY };
}
private squaredDistanceFromTile(
@@ -154,6 +154,7 @@ export class UnitGrid {
if (!unit.isActive()) continue;
const distSquared = this.squaredDistanceFromTile(unit, tile);
if (distSquared > rangeSquared) continue;
// eslint-disable-next-line sort-keys
const value = { unit, distSquared };
if (predicate !== undefined && !predicate(value)) continue;
nearby.push(value);
+2
View File
@@ -114,6 +114,7 @@ export class UnitImpl implements Unit {
return this._id;
}
/* eslint-disable sort-keys */
toUpdate(): UnitUpdate {
return {
type: GameUpdateType.Unit,
@@ -139,6 +140,7 @@ export class UnitImpl implements Unit {
loaded: this._loaded,
};
}
/* eslint-enable sort-keys */
type(): UnitType {
return this._type;
+2
View File
@@ -148,6 +148,7 @@ export class PathFinder {
}
if (this.game.manhattanDist(curr, dst) < dist) {
// eslint-disable-next-line sort-keys
return { type: PathFindResultType.Completed, node: curr };
}
@@ -164,6 +165,7 @@ export class PathFinder {
if (tile === undefined) {
throw new Error("missing tile");
}
// eslint-disable-next-line sort-keys
return { type: PathFindResultType.NextTile, node: tile };
}
}
+3
View File
@@ -49,6 +49,7 @@ export class SerialAStar<NodeType> implements AStar<NodeType> {
this.fwdGScore.set(startPoint, 0);
this.fwdOpenSet.add({
tile: startPoint,
// eslint-disable-next-line sort-keys
fScore: this.heuristic(startPoint, dst),
});
});
@@ -57,6 +58,7 @@ export class SerialAStar<NodeType> implements AStar<NodeType> {
this.bwdGScore.set(dst, 0);
this.bwdOpenSet.add({
tile: dst,
// eslint-disable-next-line sort-keys
fScore: this.heuristic(dst, this.findClosestSource(dst)),
});
}
@@ -145,6 +147,7 @@ export class SerialAStar<NodeType> implements AStar<NodeType> {
const fScore =
totalG +
this.heuristic(neighbor, isForward ? this.dst : this.closestSource);
// eslint-disable-next-line sort-keys
openSet.add({ tile: neighbor, fScore: fScore });
}
}
+4 -3
View File
@@ -50,33 +50,34 @@ export function validateUsername(username: string): {
error?: string;
} {
if (typeof username !== "string") {
// eslint-disable-next-line sort-keys
return { isValid: false, error: translateText("username.not_string") };
}
if (username.length < MIN_USERNAME_LENGTH) {
return {
isValid: false,
error: translateText("username.too_short", {
min: MIN_USERNAME_LENGTH,
}),
isValid: false,
};
}
if (username.length > MAX_USERNAME_LENGTH) {
return {
isValid: false,
error: translateText("username.too_long", {
max: MAX_USERNAME_LENGTH,
}),
isValid: false,
};
}
if (!validPattern.test(username)) {
return {
isValid: false,
error: translateText("username.invalid_chars", {
max: MAX_USERNAME_LENGTH,
}),
isValid: false,
};
}
+7 -7
View File
@@ -23,8 +23,8 @@ function gameUpdate(gu: GameUpdateViewData | ErrorUpdate) {
return;
}
sendMessage({
type: "game_update",
gameUpdate: gu,
type: "game_update",
});
}
@@ -48,8 +48,8 @@ ctx.addEventListener("message", async (e: MessageEvent<MainThreadMessage>) => {
gameUpdate,
).then((gr) => {
sendMessage({
type: "initialized",
id: message.id,
type: "initialized",
} as InitializedMessage);
return gr;
});
@@ -85,9 +85,9 @@ ctx.addEventListener("message", async (e: MessageEvent<MainThreadMessage>) => {
message.y,
);
sendMessage({
type: "player_actions_result",
id: message.id,
result: actions,
type: "player_actions_result",
} as PlayerActionsResultMessage);
} catch (error) {
console.error("Failed to check borders:", error);
@@ -102,9 +102,9 @@ ctx.addEventListener("message", async (e: MessageEvent<MainThreadMessage>) => {
try {
const profile = (await gameRunner).playerProfile(message.playerID);
sendMessage({
type: "player_profile_result",
id: message.id,
result: profile,
type: "player_profile_result",
} as PlayerProfileResultMessage);
} catch (error) {
console.error("Failed to check borders:", error);
@@ -121,9 +121,9 @@ ctx.addEventListener("message", async (e: MessageEvent<MainThreadMessage>) => {
message.playerID,
);
sendMessage({
type: "player_border_tiles_result",
id: message.id,
result: borderTiles,
type: "player_border_tiles_result",
} as PlayerBorderTilesResultMessage);
} catch (error) {
console.error("Failed to get border tiles:", error);
@@ -141,8 +141,8 @@ ctx.addEventListener("message", async (e: MessageEvent<MainThreadMessage>) => {
message.attackID,
);
sendMessage({
type: "attack_average_position_result",
id: message.id,
type: "attack_average_position_result",
x: averagePosition ? averagePosition.x : null,
y: averagePosition ? averagePosition.y : null,
} as AttackAveragePositionResultMessage);
@@ -162,9 +162,9 @@ ctx.addEventListener("message", async (e: MessageEvent<MainThreadMessage>) => {
message.targetTile,
);
sendMessage({
type: "transport_ship_spawn_result",
id: message.id,
result: spawnTile,
type: "transport_ship_spawn_result",
} as TransportShipSpawnResultMessage);
} catch (error) {
console.error("Failed to spawn transport ship:", error);
+16 -16
View File
@@ -66,10 +66,10 @@ export class WorkerClient {
});
this.worker.postMessage({
type: "init",
id: messageId,
gameStartInfo: this.gameStartInfo,
clientID: this.clientID,
gameStartInfo: this.gameStartInfo,
id: messageId,
type: "init",
});
// Add timeout for initialization
@@ -95,8 +95,8 @@ export class WorkerClient {
}
this.worker.postMessage({
type: "turn",
turn,
type: "turn",
});
}
@@ -125,9 +125,9 @@ export class WorkerClient {
});
this.worker.postMessage({
type: "player_profile",
id: messageId,
playerID: playerID,
playerID,
type: "player_profile",
});
});
}
@@ -151,9 +151,9 @@ export class WorkerClient {
});
this.worker.postMessage({
type: "player_border_tiles",
id: messageId,
playerID: playerID,
playerID,
type: "player_border_tiles",
});
});
}
@@ -181,9 +181,9 @@ export class WorkerClient {
});
this.worker.postMessage({
type: "player_actions",
id: messageId,
playerID: playerID,
playerID,
type: "player_actions",
x: x,
y: y,
});
@@ -217,10 +217,10 @@ export class WorkerClient {
});
this.worker.postMessage({
type: "attack_average_position",
attackID,
id: messageId,
playerID: playerID,
attackID: attackID,
playerID,
type: "attack_average_position",
});
});
}
@@ -247,10 +247,10 @@ export class WorkerClient {
});
this.worker.postMessage({
type: "transport_ship_spawn",
id: messageId,
playerID: playerID,
targetTile: targetTile,
playerID,
targetTile,
type: "transport_ship_spawn",
});
});
}