feat: add config options for bootstrapper tunables (#3010)
Reviewed-on: #3010 Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com> Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>
This commit was merged in pull request #3010.
This commit is contained in:
@@ -12,10 +12,19 @@ import { handleNonceInvalidation } from "../../services/wsService.ts";
|
||||
import { getInventory } from "../../services/inventoryService.ts";
|
||||
import { createMessage } from "../../services/inboxService.ts";
|
||||
import { fromStoreItem } from "../../services/itemDataService.ts";
|
||||
import { getTokenForClient } from "../../services/tunablesService.ts";
|
||||
import type { AddressInfo } from "node:net";
|
||||
|
||||
export const loginController: RequestHandler = async (request, response) => {
|
||||
const loginRequest = JSON.parse(String(request.body)) as ILoginRequest; // parse octet stream of json data to json object
|
||||
|
||||
if (config.tunables?.useLoginToken) {
|
||||
if (request.query.token !== getTokenForClient((request.socket.address() as AddressInfo).address)) {
|
||||
response.status(400).json({ error: "missing or incorrect token" });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const account = await Account.findOne({ email: loginRequest.email });
|
||||
|
||||
const buildLabel: string =
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
import type { RequestHandler } from "express";
|
||||
import type { ITunables } from "../../types/bootstrapperTypes.ts";
|
||||
import { getTunablesForClient } from "../../services/tunablesService.ts";
|
||||
import type { AddressInfo } from "node:net";
|
||||
|
||||
// This endpoint is specific to the OpenWF Bootstrapper: https://openwf.io/bootstrapper-manual
|
||||
|
||||
export const tunablesController: RequestHandler = (_req, res) => {
|
||||
const tunables: ITunables = {};
|
||||
//tunables.prohibit_skip_mission_start_timer = true;
|
||||
//tunables.prohibit_fov_override = true;
|
||||
//tunables.prohibit_freecam = true;
|
||||
//tunables.prohibit_teleport = true;
|
||||
//tunables.prohibit_scripts = true;
|
||||
export const tunablesController: RequestHandler = (req, res) => {
|
||||
const tunables: ITunables = getTunablesForClient((req.socket.address() as AddressInfo).address);
|
||||
res.json(tunables);
|
||||
};
|
||||
|
||||
@@ -75,6 +75,14 @@ export interface IConfig {
|
||||
circuitGameModes?: string[];
|
||||
darvoStockMultiplier?: number;
|
||||
};
|
||||
tunables?: {
|
||||
useLoginToken?: boolean;
|
||||
prohibitSkipMissionStartTimer?: boolean;
|
||||
prohibitFovOverride?: boolean;
|
||||
prohibitFreecam?: boolean;
|
||||
prohibitTeleport?: boolean;
|
||||
prohibitScripts?: boolean;
|
||||
};
|
||||
dev?: {
|
||||
keepVendorsExpired?: boolean;
|
||||
};
|
||||
|
||||
@@ -11,11 +11,14 @@ import {
|
||||
} from "./configService.ts";
|
||||
import { saveConfig, shouldReloadConfig } from "./configWriterService.ts";
|
||||
import { getWebBindings, startWebServer, stopWebServer } from "./webService.ts";
|
||||
import { sendWsBroadcast } from "./wsService.ts";
|
||||
import { forEachWsClient, sendWsBroadcast, type IWsMsgToClient } from "./wsService.ts";
|
||||
import varzia from "../../static/fixed_responses/worldState/varzia.json" with { type: "json" };
|
||||
import { getTunablesForClient } from "./tunablesService.ts";
|
||||
|
||||
chokidar.watch(configPath).on("change", () => {
|
||||
if (shouldReloadConfig()) {
|
||||
const prevTunables = JSON.stringify(config.tunables);
|
||||
|
||||
logger.info("Detected a change to config file, reloading its contents.");
|
||||
try {
|
||||
loadConfig();
|
||||
@@ -26,6 +29,17 @@ chokidar.watch(configPath).on("change", () => {
|
||||
validateConfig();
|
||||
syncConfigWithDatabase();
|
||||
|
||||
if (JSON.stringify(config.tunables) != prevTunables) {
|
||||
logger.debug(`tunables changed, informing clients`);
|
||||
forEachWsClient(client => {
|
||||
if (client.isGame) {
|
||||
client.send(
|
||||
JSON.stringify({ tunables: getTunablesForClient(client.address) } satisfies IWsMsgToClient)
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const configBindings = configGetWebBindings();
|
||||
const bindings = getWebBindings();
|
||||
if (
|
||||
|
||||
41
src/services/tunablesService.ts
Normal file
41
src/services/tunablesService.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import crypto from "node:crypto";
|
||||
import { args } from "../helpers/commandLineArguments.ts";
|
||||
import type { ITunables } from "../types/bootstrapperTypes.ts";
|
||||
import { config } from "./configService.ts";
|
||||
|
||||
let secret;
|
||||
if (args.secret) {
|
||||
secret = args.secret; // Maintain same secret across hot reloads in dev mode
|
||||
} else {
|
||||
secret = "";
|
||||
for (let i = 0; i != 10; ++i) {
|
||||
secret += String.fromCharCode(Math.floor(Math.random() * 26) + 0x41);
|
||||
}
|
||||
}
|
||||
|
||||
export const getTokenForClient = (clientAddress: string): string => {
|
||||
return crypto.createHmac("sha256", secret).update(clientAddress).digest("hex");
|
||||
};
|
||||
|
||||
export const getTunablesForClient = (clientAddress: string): ITunables => {
|
||||
const tunables: ITunables = {};
|
||||
if (config.tunables?.useLoginToken) {
|
||||
tunables.token = getTokenForClient(clientAddress);
|
||||
}
|
||||
if (config.tunables?.prohibitSkipMissionStartTimer) {
|
||||
tunables.prohibit_skip_mission_start_timer = true;
|
||||
}
|
||||
if (config.tunables?.prohibitFovOverride) {
|
||||
tunables.prohibit_fov_override = true;
|
||||
}
|
||||
if (config.tunables?.prohibitFreecam) {
|
||||
tunables.prohibit_freecam = true;
|
||||
}
|
||||
if (config.tunables?.prohibitTeleport) {
|
||||
tunables.prohibit_teleport = true;
|
||||
}
|
||||
if (config.tunables?.prohibitScripts) {
|
||||
tunables.prohibit_scripts = true;
|
||||
}
|
||||
return tunables;
|
||||
};
|
||||
@@ -9,6 +9,7 @@ import type { HydratedDocument } from "mongoose";
|
||||
import { logError, logger } from "../utils/logger.ts";
|
||||
import type { Request } from "express";
|
||||
import type { ITunables } from "../types/bootstrapperTypes.ts";
|
||||
import type { AddressInfo } from "node:net";
|
||||
|
||||
let wsServer: WebSocketServer | undefined;
|
||||
let wssServer: WebSocketServer | undefined;
|
||||
@@ -48,6 +49,7 @@ let lastWsid: number = 0;
|
||||
|
||||
interface IWsCustomData extends WebSocket {
|
||||
id: number;
|
||||
address: string;
|
||||
accountId?: string;
|
||||
isGame?: boolean;
|
||||
}
|
||||
@@ -66,7 +68,7 @@ interface IWsMsgFromClient {
|
||||
sync_inventory?: boolean;
|
||||
}
|
||||
|
||||
interface IWsMsgToClient {
|
||||
export interface IWsMsgToClient {
|
||||
// common
|
||||
wsid?: number;
|
||||
|
||||
@@ -104,6 +106,7 @@ const wsOnConnect = (ws: WebSocket, req: http.IncomingMessage): void => {
|
||||
}
|
||||
|
||||
(ws as IWsCustomData).id = ++lastWsid;
|
||||
(ws as IWsCustomData).address = (req.socket.address() as AddressInfo).address;
|
||||
ws.send(JSON.stringify({ wsid: lastWsid } satisfies IWsMsgToClient));
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
@@ -212,7 +215,7 @@ const wsOnConnect = (ws: WebSocket, req: http.IncomingMessage): void => {
|
||||
});
|
||||
};
|
||||
|
||||
const forEachClient = (cb: (client: IWsCustomData) => void): void => {
|
||||
export const forEachWsClient = (cb: (client: IWsCustomData) => void): void => {
|
||||
if (wsServer) {
|
||||
for (const client of wsServer.clients) {
|
||||
cb(client as IWsCustomData);
|
||||
@@ -227,7 +230,7 @@ const forEachClient = (cb: (client: IWsCustomData) => void): void => {
|
||||
|
||||
export const haveGameWs = (accountId: string): boolean => {
|
||||
let ret = false;
|
||||
forEachClient(client => {
|
||||
forEachWsClient(client => {
|
||||
if (client.isGame && client.accountId == accountId) {
|
||||
ret = true;
|
||||
}
|
||||
@@ -237,14 +240,14 @@ export const haveGameWs = (accountId: string): boolean => {
|
||||
|
||||
export const sendWsBroadcast = (data: IWsMsgToClient): void => {
|
||||
const msg = JSON.stringify(data);
|
||||
forEachClient(client => {
|
||||
forEachWsClient(client => {
|
||||
client.send(msg);
|
||||
});
|
||||
};
|
||||
|
||||
export const sendWsBroadcastTo = (accountId: string, data: IWsMsgToClient): void => {
|
||||
const msg = JSON.stringify(data);
|
||||
forEachClient(client => {
|
||||
forEachWsClient(client => {
|
||||
if (client.accountId == accountId) {
|
||||
client.send(msg);
|
||||
}
|
||||
@@ -253,7 +256,7 @@ export const sendWsBroadcastTo = (accountId: string, data: IWsMsgToClient): void
|
||||
|
||||
export const sendWsBroadcastToGame = (accountId: string, data: IWsMsgToClient): void => {
|
||||
const msg = JSON.stringify(data);
|
||||
forEachClient(client => {
|
||||
forEachWsClient(client => {
|
||||
if (client.isGame && client.accountId == accountId) {
|
||||
client.send(msg);
|
||||
}
|
||||
@@ -262,7 +265,7 @@ export const sendWsBroadcastToGame = (accountId: string, data: IWsMsgToClient):
|
||||
|
||||
export const sendWsBroadcastEx = (data: IWsMsgToClient, accountId?: string, excludeWsid?: number): void => {
|
||||
const msg = JSON.stringify(data);
|
||||
forEachClient(client => {
|
||||
forEachWsClient(client => {
|
||||
if ((!accountId || client.accountId == accountId) && client.id != excludeWsid) {
|
||||
client.send(msg);
|
||||
}
|
||||
@@ -271,7 +274,7 @@ export const sendWsBroadcastEx = (data: IWsMsgToClient, accountId?: string, excl
|
||||
|
||||
export const sendWsBroadcastToWebui = (data: IWsMsgToClient, accountId?: string, excludeWsid?: number): void => {
|
||||
const msg = JSON.stringify(data);
|
||||
forEachClient(client => {
|
||||
forEachWsClient(client => {
|
||||
if (!client.isGame && (!accountId || client.accountId == accountId) && client.id != excludeWsid) {
|
||||
client.send(msg);
|
||||
}
|
||||
@@ -294,7 +297,7 @@ export const broadcastInventoryUpdate = (req: Request): void => {
|
||||
};
|
||||
|
||||
export const handleNonceInvalidation = (accountId: string): void => {
|
||||
forEachClient(client => {
|
||||
forEachWsClient(client => {
|
||||
if (client.accountId == accountId) {
|
||||
if (client.isGame) {
|
||||
client.accountId = undefined; // prevent processing of the close event
|
||||
|
||||
Reference in New Issue
Block a user