Commit b7dfc180 authored by nanahira's avatar nanahira

update config format

parent 7dcde5ac
Pipeline #43217 passed with stages
in 1 minute and 44 seconds
......@@ -15,6 +15,7 @@
- 目录内引用:同级文件使用 `'./xxx'`,子目录文件使用 `'../xxx'`
- 目录外引用:必须通过 index.ts 引用,如 `'../room'` `'../client'`(指向 index.ts)
- 禁止直接引用具体文件:不要使用 `'../room/room'` `'../client/client'` 这样的路径
- 如果正在写的算法代码与 this 和业务无关,那么不要放在类方法里面,而是在 utility 目录新开一个 ts 文件放进去
## 模块结构
......
HOST: "::"
PORT: "7911"
REDIS_URL: ""
LOG_LEVEL: info
WS_PORT: "0"
SSL_PATH: ""
SSL_CERT: ""
SSL_KEY: ""
TRUSTED_PROXIES: 127.0.0.0/8,::1/128
NO_CONNECT_COUNT_LIMIT: ""
YGOPRO_VERSION: ""
ALT_VERSIONS: ""
USE_PROXY: ""
YGOPRO_PATH: ./ygopro
EXTRA_SCRIPT_PATH: ""
DECK_MAIN_MIN: "40"
DECK_MAIN_MAX: "60"
DECK_EXTRA_MAX: "15"
DECK_SIDE_MAX: "15"
DECK_MAX_COPIES: "3"
OCGCORE_DEBUG_LOG: ""
OCGCORE_WASM_PATH: ""
WELCOME: ""
ENABLE_RECONNECT: "1"
RECONNECT_TIMEOUT: "180000"
HOSTINFO_LFLIST: "0"
HOSTINFO_RULE: "0"
HOSTINFO_MODE: "0"
HOSTINFO_DUEL_RULE: "5"
HOSTINFO_NO_CHECK_DECK: "0"
HOSTINFO_NO_SHUFFLE_DECK: "0"
HOSTINFO_START_LP: "8000"
HOSTINFO_START_HAND: "5"
HOSTINFO_DRAW_COUNT: "1"
HOSTINFO_TIME_LIMIT: "240"
host: "::"
port: 7911
redisUrl: ""
logLevel: info
wsPort: 0
sslPath: ""
sslCert: ""
sslKey: ""
trustedProxies:
- 127.0.0.0/8
- ::1/128
noConnectCountLimit: ""
ygoproVersion: "0x1362"
altVersions:
- 2330
- 2331
useProxy: ""
ygoproPath: ./ygopro
extraScriptPath: ""
deckMainMin: 40
deckMainMax: 60
deckExtraMax: 15
deckSideMax: 15
deckMaxCopies: 3
ocgcoreDebugLog: 0
ocgcoreWasmPath: ""
welcome: ""
enableReconnect: 1
reconnectTimeout: 180000
hostinfoLflist: 0
hostinfoRule: 0
hostinfoMode: 0
hostinfoDuelRule: 5
hostinfoNoCheckDeck: 0
hostinfoNoShuffleDeck: 0
hostinfoStartLp: 8000
hostinfoStartHand: 5
hostinfoDrawCount: 1
hostinfoTimeLimit: 240
......@@ -2,6 +2,7 @@ import yaml from 'yaml';
import * as fs from 'node:fs';
import { DefaultHostinfo } from './room/default-hostinfo';
import { Prettify } from 'nfkit';
import { normalizeConfigByDefaultKeys } from './utility/normalize-config-by-default-keys';
export type HostinfoOptions = {
[K in keyof typeof DefaultHostinfo as `HOSTINFO_${Uppercase<K>}`]: string;
......@@ -30,9 +31,9 @@ export const defaultConfig = {
// Boolean parse rule (default false): ''/'0'/'false'/'null' => false, otherwise true.
NO_CONNECT_COUNT_LIMIT: '',
// Restrict accepted YGOPro version. Format: version string; empty means no restriction.
YGOPRO_VERSION: '',
YGOPRO_VERSION: '0x1362',
// Additional accepted versions. Format: comma-separated version strings.
ALT_VERSIONS: '',
ALT_VERSIONS: '2330,2331',
// Proxy URL for outbound HTTP(S) requests.
// Format: proxy URL string (e.g. http://127.0.0.1:7890). Empty means no proxy.
USE_PROXY: '',
......@@ -52,7 +53,7 @@ export const defaultConfig = {
DECK_MAX_COPIES: '3',
// Enable ocgcore debug logs.
// Boolean parse rule (default false): ''/'0'/'false'/'null' => false, otherwise true.
OCGCORE_DEBUG_LOG: '',
OCGCORE_DEBUG_LOG: '0',
// OCGCore wasm file path. Format: filesystem path string. Empty means use default wasm loading.
OCGCORE_WASM_PATH: '',
// Welcome message sent when players join. Format: plain string.
......@@ -77,16 +78,25 @@ export const defaultConfig = {
export type Config = Prettify<typeof defaultConfig & HostinfoOptions>;
export function loadConfig(): Config {
let readConfig: Partial<Config> = {};
let readConfig: Record<string, unknown> = {};
try {
const configText = fs.readFileSync('./config.yaml', 'utf-8');
readConfig = yaml.parse(configText);
const parsed = yaml.parse(configText);
if (parsed && typeof parsed === 'object') {
readConfig = parsed;
}
} catch (e) {
console.error(`Failed to read config: ${e.toString()}`);
}
const normalizedConfig = normalizeConfigByDefaultKeys(
readConfig,
defaultConfig,
);
return {
...defaultConfig,
...readConfig,
...normalizedConfig,
...process.env,
};
}
......@@ -2,8 +2,29 @@ import * as fs from 'fs';
import yaml from 'yaml';
import { defaultConfig } from '../config';
function toCamelCaseKey(key: string): string {
const lower = key.toLowerCase();
return lower.replace(/_([a-z0-9])/g, (_, ch: string) => ch.toUpperCase());
}
function toTypedValue(value: string): string | number {
if (/^\d+$/.test(value)) {
return Number.parseInt(value, 10);
}
return value;
}
async function main(): Promise<void> {
const output = yaml.stringify(defaultConfig);
const exampleConfig = Object.fromEntries(
Object.entries(defaultConfig).map(([key, value]) => {
if (value.includes(',')) {
const items = value.split(',').map((item) => toTypedValue(item));
return [toCamelCaseKey(key), items];
}
return [toCamelCaseKey(key), toTypedValue(value)];
}),
);
const output = yaml.stringify(exampleConfig);
await fs.promises.writeFile('./config.example.yaml', output, 'utf-8');
console.log('Generated config.example.yaml');
}
......
import { normalizeConfigValue } from './normalize-config-value';
function toCamelCaseKey(key: string): string {
const lower = key.toLowerCase();
return lower.replace(/_([a-z0-9])/g, (_, ch: string) => ch.toUpperCase());
}
export function normalizeConfigByDefaultKeys<T extends Record<string, string>>(
readConfig: Record<string, unknown>,
defaultConfig: T,
): Partial<T> {
const normalizedConfig: Partial<T> = {};
for (const key of Object.keys(defaultConfig) as Array<keyof T>) {
const rawKey = key as string;
const camelKey = toCamelCaseKey(rawKey);
const value =
readConfig[camelKey] !== undefined
? readConfig[camelKey]
: readConfig[rawKey];
const normalized = normalizeConfigValue(value);
if (normalized !== undefined) {
normalizedConfig[key] = normalized as T[typeof key];
}
}
return normalizedConfig;
}
function normalizeArrayItem(value: unknown): string {
if (typeof value === 'string') {
return value;
}
if (typeof value === 'number') {
return value.toString();
}
if (typeof value === 'boolean') {
return value ? '1' : '0';
}
return String(value);
}
export function normalizeConfigValue(value: unknown): string | undefined {
if (value === undefined) {
return undefined;
}
if (typeof value === 'string') {
return value;
}
if (typeof value === 'number') {
return value.toString();
}
if (typeof value === 'boolean') {
return value ? '1' : '0';
}
if (Array.isArray(value)) {
return value.map((item) => normalizeArrayItem(item)).join(',');
}
return String(value);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment