Files
Fractured/tools/fractured-launcher-electron/lib/config-store.js
T
Docker Build b455db0db8 fix(launcher): drop patch-Z.MPQ from default files and migrate old configs
- default-launcher.json files: only Wow-patched.exe from release.
- config-store: strip deprecated patch-Z.MPQ from merged files; rewrite
  launcher.json on load if user still had that entry.
- Docs/scripts examples updated; version 1.0.4.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-10 21:36:50 -05:00

113 lines
4.2 KiB
JavaScript

'use strict';
const path = require('path');
const fs = require('fs').promises;
/** Sources no longer shipped; drop from merged files so old launcher.json does not keep fetching them. */
const DEPRECATED_FILE_SOURCES = new Set(['patch-Z.MPQ']);
function mergeFilesList(defaults, user) {
const raw =
Array.isArray(user.files) && user.files.length ? user.files.map((e) => ({ ...e })) : defaults.files.map((e) => ({ ...e }));
const filtered = raw.filter((e) => !DEPRECATED_FILE_SOURCES.has(String(e && e.source ? e.source : '').trim()));
if (filtered.length) return filtered;
return defaults.files.map((e) => ({ ...e }));
}
function userFilesContainDeprecated(user) {
const files = user && user.files;
if (!Array.isArray(files)) return false;
return files.some((e) => DEPRECATED_FILE_SOURCES.has(String(e && e.source ? e.source : '').trim()));
}
function mergeConfig(defaults, user) {
return {
...defaults,
...user,
update_feed_url:
user.update_feed_url != null && user.update_feed_url !== ''
? user.update_feed_url
: defaults.update_feed_url,
launcher_updates_from_github:
user.launcher_updates_from_github != null
? user.launcher_updates_from_github
: defaults.launcher_updates_from_github,
github: { ...defaults.github, ...(user.github || {}) },
gitea: { ...defaults.gitea, ...(user.gitea || {}) },
patch_manifest: { ...defaults.patch_manifest, ...(user.patch_manifest || {}) },
launch: { ...defaults.launch, ...(user.launch || {}) },
auth: user.auth != null ? { ...defaults.auth, ...user.auth } : defaults.auth,
realmlist: user.realmlist != null ? { ...defaults.realmlist, ...user.realmlist } : defaults.realmlist,
files: mergeFilesList(defaults, user),
};
}
/** Hardcoded Gitea host/repo (see lib/baked-gitea-channel.js). Non-empty baked values win. */
function applyBakedGitea(cfg) {
let baked;
try {
baked = require('./baked-gitea-channel');
} catch {
return cfg;
}
if (!baked || typeof baked !== 'object') return cfg;
cfg.gitea = { ...(cfg.gitea || {}) };
for (const k of ['base_url', 'owner', 'repo', 'release_tag']) {
const v = baked[k];
if (v != null && String(v).trim() !== '') cfg.gitea[k] = String(v).trim();
}
return cfg;
}
function getConfigPath(app) {
if (process.env.FRACTURED_LAUNCHER_CONFIG) return process.env.FRACTURED_LAUNCHER_CONFIG;
if (app && app.isPackaged) {
// AppImage (and macOS .app) run from a read-only mount — cannot write beside execPath.
if (process.platform === 'linux' || process.platform === 'darwin') {
return path.join(app.getPath('userData'), 'launcher.json');
}
return path.join(path.dirname(process.execPath), 'launcher.json');
}
return path.join(__dirname, '..', 'launcher.json');
}
async function loadConfig(app) {
const p = getConfigPath(app);
const defPath = path.join(__dirname, '..', 'default-launcher.json');
const defaults = JSON.parse(await fs.readFile(defPath, 'utf8'));
try {
const user = JSON.parse(await fs.readFile(p, 'utf8'));
const config = applyBakedGitea(mergeConfig(defaults, user));
if (userFilesContainDeprecated(user)) {
await fs.writeFile(p, JSON.stringify(config, null, 2), 'utf8');
}
return { configPath: p, config };
} catch (e) {
if (e.code === 'ENOENT') {
const initial = applyBakedGitea(mergeConfig(defaults, {}));
await fs.writeFile(p, JSON.stringify(initial, null, 2), 'utf8');
return { configPath: p, config: JSON.parse(JSON.stringify(initial)) };
}
throw e;
}
}
async function saveGameDir(configPath, gameDir) {
const defPath = path.join(__dirname, '..', 'default-launcher.json');
const defaults = JSON.parse(await fs.readFile(defPath, 'utf8'));
const user = JSON.parse(await fs.readFile(configPath, 'utf8'));
user.game_dir = gameDir;
const merged = applyBakedGitea(mergeConfig(defaults, user));
await fs.writeFile(configPath, JSON.stringify(merged, null, 2), 'utf8');
return merged;
}
function resolveGameDir(cfg, configPath) {
const gd = cfg.game_dir;
if (!gd) return '';
if (path.isAbsolute(gd)) return path.normalize(gd);
return path.normalize(path.join(path.dirname(configPath), gd));
}
module.exports = { getConfigPath, loadConfig, saveGameDir, resolveGameDir, mergeConfig, applyBakedGitea };