Fix various ESLint violations (#402)

## Description:

Fixes a number of ESLint violations. Although I have tested these
changes through the local dev server, I don't have a high confidence
that the testing is sufficient, as I am new to this codebase. This
change would benefit from heightened scrutiny.

## 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:

fake.neo

---------

Co-authored-by: Scott Anderson <662325+scottanderson@users.noreply.github.com>
This commit is contained in:
Scott Anderson
2025-04-02 21:06:08 -04:00
committed by GitHub
parent 77a60d5a00
commit 52f64db6d3
21 changed files with 127 additions and 126 deletions
-3
View File
@@ -21,16 +21,13 @@ export default [
{
rules: {
// Disable rules that would fail. The failures should be fixed, and the entries here removed.
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/no-empty-object-type": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-namespace": "off",
"@typescript-eslint/no-require-imports": "off",
"@typescript-eslint/no-unused-expressions": "off",
"@typescript-eslint/no-unused-vars": "off",
"no-case-declarations": "off",
"no-useless-escape": "off",
"prefer-const": "off",
},
},
];
+1
View File
@@ -1,5 +1,6 @@
{
"main": {
"title": "OpenFront (ALPHA)",
"join_discord": "Join the Discord!",
"create_lobby": "Create Lobby",
"join_lobby": "Join Lobby",
+5 -7
View File
@@ -25,7 +25,7 @@ import { loadTerrainMap } from "../core/game/TerrainMapLoader";
import { UserSettings } from "../core/game/UserSettings";
import { WorkerClient } from "../core/worker/WorkerClient";
import { InputHandler, MouseMoveEvent, MouseUpEvent } from "./InputHandler";
import { LocalPersistantStats } from "./LocalPersistantStats";
import { endGame, startGame, startTime } from "./LocalPersistantStats";
import { getPersistentIDFromCookie } from "./Main";
import {
SendAttackIntentEvent,
@@ -36,6 +36,7 @@ import {
import { createCanvas } from "./Utils";
import { createRenderer, GameRenderer } from "./graphics/GameRenderer";
export // Is this function needed?
function distSortUnitWorld(tile: TileRef, game: GameView) {
return (a: Unit | UnitView, b: Unit | UnitView) => {
return (
@@ -69,10 +70,7 @@ export function joinLobby(
);
const userSettings: UserSettings = new UserSettings();
LocalPersistantStats.startGame(
lobbyConfig.gameID,
lobbyConfig.gameStartInfo?.config,
);
startGame(lobbyConfig.gameID, lobbyConfig.gameStartInfo?.config);
const transport = new Transport(lobbyConfig, eventBus);
@@ -198,13 +196,13 @@ export class ClientGameRunner {
players,
// Not saving turns locally
[],
LocalPersistantStats.startTime(),
startTime(),
Date.now(),
winner,
update.winnerType,
update.allPlayersStats,
);
LocalPersistantStats.endGame(record);
endGame(record);
}
public start() {
+2 -2
View File
@@ -3,7 +3,7 @@ import { customElement, property } from "lit/decorators.js";
declare global {
interface Window {
adsbygoogle: any[];
adsbygoogle: unknown[];
}
}
@@ -89,7 +89,7 @@ const isElectron = () => {
if (
typeof window !== "undefined" &&
typeof window.process === "object" &&
// @ts-ignore
// @ts-expect-error hidden
window.process.type === "renderer"
) {
return true;
+2
View File
@@ -432,6 +432,7 @@ export class HostLobbyModal extends LitElement {
} as GameConfig),
},
);
return response;
}
private getRandomMap(): GameMapType {
@@ -460,6 +461,7 @@ export class HostLobbyModal extends LitElement {
},
},
);
return response;
}
private async copyToClipboard() {
+1 -1
View File
@@ -93,7 +93,7 @@ export class InputHandler {
private alternateView = false;
private moveInterval: any = null;
private moveInterval: NodeJS.Timeout = null;
private activeKeys = new Set<string>();
private readonly PAN_SPEED = 5;
+35 -33
View File
@@ -20,10 +20,12 @@ const translations = {
es: esTranslations,
};
type Translation = Partial<(typeof translations)[keyof typeof translations]>;
@customElement("lang-selector")
export class LangSelector extends LitElement {
@state() public translations: any = {};
@state() private defaultTranslations: any = {};
@state() public translations: Translation = {};
@state() private defaultTranslations = {};
@state() private currentLang: string = "en";
createRenderRoot() {
@@ -44,10 +46,10 @@ export class LangSelector extends LitElement {
this.translations = await this.loadLanguage(userLang);
this.currentLang = userLang;
this.applyTranslation(this.translations);
this.applyTranslation();
}
private async loadLanguage(lang: string): Promise<any> {
private async loadLanguage(lang: string): Promise<Translation> {
try {
const translation = translations[lang as keyof typeof translations];
if (!translation) throw new Error(`Language file not found: ${lang}`);
@@ -58,7 +60,7 @@ export class LangSelector extends LitElement {
}
}
private applyTranslation(translations: any) {
private applyTranslation() {
const components = [
"single-player-modal",
"host-lobby-modal",
@@ -75,24 +77,14 @@ export class LangSelector extends LitElement {
"public-lobby",
];
document.title = translations.main?.title || document.title;
const main = this.translations.main;
if (main && "title" in main) {
document.title = main.title;
}
document.querySelectorAll("[data-i18n]").forEach((element) => {
const key = element.getAttribute("data-i18n");
const keys = key.split(".");
let text = translations;
for (const k of keys) {
text = text?.[k];
if (!text) break;
}
if (!text && this.defaultTranslations) {
let fallback = this.defaultTranslations;
for (const k of keys) {
fallback = fallback?.[k];
if (!fallback) break;
}
text = fallback;
}
const text = this.translateText(key);
if (text) {
element.innerHTML = text;
} else {
@@ -101,7 +93,7 @@ export class LangSelector extends LitElement {
});
components.forEach((tagName) => {
const el = document.querySelector(tagName) as any;
const el = document.querySelector(tagName) as LitElement;
if (el && typeof el.requestUpdate === "function") {
el.requestUpdate();
} else {
@@ -117,19 +109,13 @@ export class LangSelector extends LitElement {
params: Record<string, string | number> = {},
): string {
const keys = key.split(".");
let text: any = this.translations;
for (const k of keys) {
text = text?.[k];
if (!text) break;
let text = findTranslation(keys, this.translations);
if (!text && this.defaultTranslations) {
text = findTranslation(keys, this.defaultTranslations);
}
if (!text && this.defaultTranslations) {
text = this.defaultTranslations;
for (const k of keys) {
text = text?.[k];
if (!text) return key;
}
if (text == null || typeof text !== "string") {
return null;
}
for (const [param, value] of Object.entries(params)) {
@@ -143,7 +129,7 @@ export class LangSelector extends LitElement {
localStorage.setItem("lang", lang);
this.translations = await this.loadLanguage(lang);
this.currentLang = lang;
this.applyTranslation(this.translations);
this.applyTranslation();
}
render() {
@@ -178,3 +164,19 @@ export class LangSelector extends LitElement {
`;
}
}
function findTranslation(
keys: string[],
translations: Translation,
): string | null {
let ptr: unknown = translations;
for (const k of keys) {
ptr = ptr?.[k];
if (!ptr) break;
}
if (ptr && typeof ptr === "string") {
return ptr;
} else {
return null;
}
}
+46 -48
View File
@@ -9,53 +9,51 @@ export interface LocalStatsData {
};
}
export namespace LocalPersistantStats {
let _startTime: number;
let _startTime: number;
function getStats(): LocalStatsData {
const statsStr = localStorage.getItem("game-records");
return statsStr ? JSON.parse(statsStr) : {};
}
function save(stats: LocalStatsData) {
// To execute asynchronously
setTimeout(
() => localStorage.setItem("game-records", JSON.stringify(stats)),
0,
);
}
// The user can quit the game anytime so better save the lobby as soon as the
// game starts.
export function startGame(id: GameID, lobby: GameConfig) {
if (typeof localStorage === "undefined") {
return;
}
_startTime = Date.now();
const stats = getStats();
stats[id] = { lobby };
save(stats);
}
export function startTime() {
return _startTime;
}
export function endGame(gameRecord: GameRecord) {
if (typeof localStorage === "undefined") {
return;
}
const stats = getStats();
const gameStat = stats[gameRecord.id];
if (!gameStat) {
consolex.log("LocalPersistantStats: game not found");
return;
}
gameStat.gameRecord = gameRecord;
save(stats);
}
function getStats(): LocalStatsData {
const statsStr = localStorage.getItem("game-records");
return statsStr ? JSON.parse(statsStr) : {};
}
function save(stats: LocalStatsData) {
// To execute asynchronously
setTimeout(
() => localStorage.setItem("game-records", JSON.stringify(stats)),
0,
);
}
// The user can quit the game anytime so better save the lobby as soon as the
// game starts.
export function startGame(id: GameID, lobby: GameConfig) {
if (typeof localStorage === "undefined") {
return;
}
_startTime = Date.now();
const stats = getStats();
stats[id] = { lobby };
save(stats);
}
export function startTime() {
return _startTime;
}
export function endGame(gameRecord: GameRecord) {
if (typeof localStorage === "undefined") {
return;
}
const stats = getStats();
const gameStat = stats[gameRecord.id];
if (!gameStat) {
consolex.log("LocalPersistantStats: game not found");
return;
}
gameStat.gameRecord = gameRecord;
save(stats);
}
+2 -2
View File
@@ -84,7 +84,7 @@ class Client {
"google-ad",
) as NodeListOf<GoogleAdElement>;
window.addEventListener("beforeunload", (event) => {
window.addEventListener("beforeunload", () => {
consolex.log("Browser is closing");
if (this.gameStop != null) {
this.gameStop();
@@ -213,7 +213,7 @@ class Client {
);
}
private async handleLeaveLobby(event: CustomEvent) {
private async handleLeaveLobby(/* event: CustomEvent */) {
if (this.gameStop == null) {
return;
}
-1
View File
@@ -40,7 +40,6 @@ export function createRenderer(
const startingModal = document.querySelector(
"game-starting-modal",
) as GameStartingModal;
startingModal instanceof GameStartingModal;
startingModal.hide();
// TODO maybe append this to dcoument instead of querying for them?
+1 -1
View File
@@ -216,7 +216,7 @@ export class StructureLayer implements Layer {
private handleUnitRendering(unit: UnitView) {
const unitType = unit.constructionType() ?? unit.type();
let iconType = unitType;
const iconType = unitType;
if (!this.isUnitTypeSupported(unitType)) return;
const config = this.unitConfigs[unitType];
+4 -3
View File
@@ -12,11 +12,12 @@ import { Layer } from "./Layer";
// Add this at the top of your file
declare global {
interface Window {
adsbygoogle: any[];
adsbygoogle: unknown[];
}
}
// Add this at the top of your file
declare let adsbygoogle: any[];
declare let adsbygoogle: unknown[];
@customElement("win-modal")
export class WinModal extends LitElement implements Layer {
@@ -257,7 +258,7 @@ export class WinModal extends LitElement implements Layer {
});
}
renderLayer(context: CanvasRenderingContext2D) {}
renderLayer(/* context: CanvasRenderingContext2D */) {}
shouldTransform(): boolean {
return false;
+2 -2
View File
@@ -137,8 +137,8 @@ export class Cell {
private strRepr: string;
constructor(
public readonly x,
public readonly y,
public readonly x: number,
public readonly y: number,
) {
this.strRepr = `Cell[${this.x},${this.y}]`;
}
+2 -2
View File
@@ -141,7 +141,7 @@ export class GameImpl implements Game {
}
addUpdate(update: GameUpdate) {
(this.updates[update.type] as any[]).push(update);
(this.updates[update.type] as GameUpdate[]).push(update);
}
nextUnitID(): number {
@@ -383,7 +383,7 @@ export class GameImpl implements Game {
}
playerByClientID(id: ClientID): Player | null {
for (const [pID, player] of this._players) {
for (const [, player] of this._players) {
if (player.clientID() == id) {
return player;
}
+1 -1
View File
@@ -1004,7 +1004,7 @@ export class PlayerImpl implements Player {
// It's a probability list, so if an element appears twice it's because it's
// twice more likely to be picked later.
tradingPorts(port: Unit): Unit[] {
let ports = this.mg
const ports = this.mg
.players()
.filter((p) => p != port.owner() && p.canTrade(port.owner()))
.flatMap((p) => p.units(UnitType.Port))
-4
View File
@@ -32,7 +32,3 @@ declare module "*.html" {
const content: string;
export default content;
}
declare module "*.json" {
const value: any;
export default value;
}
+7 -7
View File
@@ -456,7 +456,7 @@ export class GameServer {
const lastHashTurn = this.turns.length - 10;
let { mostCommonHash, outOfSyncClients } =
const { mostCommonHash, outOfSyncClients } =
this.findOutOfSyncClients(lastHashTurn);
if (outOfSyncClients.length == 0) {
@@ -464,11 +464,6 @@ export class GameServer {
return;
}
if (outOfSyncClients.length >= Math.floor(this.activeClients.length / 2)) {
// If half clients out of sync assume all are out of sync.
outOfSyncClients = this.activeClients;
}
const serverDesync = ServerDesyncSchema.safeParse({
type: "desync",
turn: lastHashTurn,
@@ -519,7 +514,7 @@ export class GameServer {
}
// Create a list of clients whose hash doesn't match the most common one
const outOfSyncClients: Client[] = [];
let outOfSyncClients: Client[] = [];
for (const client of this.activeClients) {
if (client.hashes.has(turnNumber)) {
@@ -530,6 +525,11 @@ export class GameServer {
}
}
// If half clients out of sync assume all are out of sync.
if (outOfSyncClients.length >= Math.floor(this.activeClients.length / 2)) {
outOfSyncClients = this.activeClients;
}
return {
mostCommonHash,
outOfSyncClients,
+5 -5
View File
@@ -16,7 +16,7 @@ export interface Gatekeeper {
// The wrapper for request handlers with optional rate limiting
httpHandler: (
limiterType: LimiterType,
fn: (req: Request, res: Response, next: NextFunction) => Promise<any>,
fn: (req: Request, res: Response, next: NextFunction) => Promise<unknown>,
) => (req: Request, res: Response, next: NextFunction) => Promise<void>;
// The wrapper for WebSocket message handlers with rate limiting
@@ -67,8 +67,8 @@ async function getGatekeeper(): Promise<Gatekeeper> {
// Use dynamic import for ES modules
// Using a type assertion to avoid TypeScript errors for optional modules
const module = await import(
"./gatekeeper/RealGatekeeper.js" as any
).catch(() => import("./gatekeeper/RealGatekeeper.js" as any));
"./gatekeeper/RealGatekeeper.js" as string
).catch(() => import("./gatekeeper/RealGatekeeper.js" as string));
if (!module || !module.RealGatekeeper) {
console.log(
@@ -95,7 +95,7 @@ export class GatekeeperWrapper implements Gatekeeper {
httpHandler(
limiterType: LimiterType,
fn: (req: Request, res: Response, next: NextFunction) => Promise<any>,
fn: (req: Request, res: Response, next: NextFunction) => Promise<unknown>,
) {
return async (req: Request, res: Response, next: NextFunction) => {
try {
@@ -129,7 +129,7 @@ export class NoOpGatekeeper implements Gatekeeper {
// Simple pass-through with no rate limiting
httpHandler(
limiterType: LimiterType,
fn: (req: Request, res: Response, next: NextFunction) => Promise<any>,
fn: (req: Request, res: Response, next: NextFunction) => Promise<unknown>,
) {
return async (req: Request, res: Response, next: NextFunction) => {
try {
+3 -3
View File
@@ -50,7 +50,7 @@ export function setupMetricsServer() {
} else if (line.trim() && !line.startsWith("#")) {
// Add worker label to each metric line and collect for later
const processedLine = line.replace(
/^([a-z][a-z0-9_]*)(?:{([^}]*)})?(\s+[0-9\.e+-]+.*)/,
/^([a-z][a-z0-9_]*)(?:{([^}]*)})?(\s+[0-9.e+-]+.*)/,
(match, metricName, existingLabels, valueAndRest) => {
if (existingLabels) {
return `${metricName}{${existingLabels},worker="master"}${valueAndRest}`;
@@ -108,7 +108,7 @@ export function setupMetricsServer() {
// Process and collect actual metric values
try {
const processedLine = line.replace(
/^([a-z][a-z0-9_]*)(?:{([^}]*)})?(\s+[0-9\.e+-]+.*)/,
/^([a-z][a-z0-9_]*)(?:{([^}]*)})?(\s+[0-9.e+-]+.*)/,
(match, metricName, existingLabels, valueAndRest) => {
if (existingLabels) {
return `${metricName}{${existingLabels},worker="worker-${i}"}${valueAndRest}`;
@@ -122,7 +122,7 @@ export function setupMetricsServer() {
if (processedLine !== line) {
allMetricValues.push(processedLine);
} else if (
line.match(/^[a-z][a-z0-9_]*(?:{[^}]*})?\s+[0-9\.e+-]+.*/)
line.match(/^[a-z][a-z0-9_]*(?:{[^}]*})?\s+[0-9.e+-]+.*/)
) {
// This looks like a metric line but didn't match our regex, try a more general approach
const parts = line.split(/({|\s+)/);
+7 -1
View File
@@ -3,7 +3,13 @@ import { ClientID, GameID, LogSeverity } from "../core/Schemas";
export interface slogMsg {
logKey: string;
msg: string;
data?: any;
data?: {
stack?: unknown;
clientID?: unknown;
clientIP?: unknown;
gameID?: unknown;
isRejoin?: unknown;
};
severity?: LogSeverity;
gameID?: GameID;
clientID?: ClientID;
+1
View File
@@ -8,6 +8,7 @@
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"experimentalDecorators": true,
"resolveJsonModule": true,
"useDefineForClassFields": false
},
"include": [