fix(launcher): Gitea http URL; Wine Z: path + Wow.exe case check
- baked-gitea-channel: http:// for brassnet mirror. - win-game-dir: map Unix /home/... to Z:\ under win32 (Wine folder picker). - resolveGameDir + saveGameDir + patch paths use it; Wow.exe resolved case-insensitively. - Version 1.0.6; README checklist for Wine. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -125,7 +125,8 @@ CI workflow **Sync release to Gitea** (`.github/workflows/gitea-release-sync.yml
|
||||
6. **Secrets** — **`GITEA_BASE_URL`**, **`GITEA_TOKEN`**, **`GITEA_OWNER`**, **`GITEA_REPO`** must be set under **Settings → Secrets and variables → Actions**. A failed “Upload to Gitea” step usually prints which is missing.
|
||||
7. **Actions tab** — Open the latest **Sync release to Gitea** run; a red **build-electron** (old tag without `package-lock.json`, etc.) or **Upload to Gitea** step shows the real error.
|
||||
8. **HTTP 422 `repo is empty`** — The Gitea repo has **no commits** yet. Push any initial commit (e.g. **Add README** in the Gitea web UI, or `git push` to **`main`**). Optionally set **`GITEA_TARGET_REF`** to match your real default branch if it is not **`main`**. From this repo you can run **`scripts/bootstrap-gitea-repo.sh`** (see script header for `GITEA_*` env or pass the HTTPS/SSH clone URL as the first argument).
|
||||
9. **`sync Wow.exe: fetch failed`** (Linux/AppImage especially) — Usually **TLS** (self-signed Gitea cert) or **cannot reach** the host. Use a **trusted certificate** on Gitea, or put your CA in the system trust store, or set **`NODE_EXTRA_CA_CERTS=/path/to/ca.pem`** when launching the AppImage if you must use self-signed HTTPS. If the mirror is LAN-only HTTP, set **`base_url`** in **`lib/baked-gitea-channel.js`** to **`http://…`** (only if acceptable). Ensure **`Wow-patched.exe`** exists on the **same Gitea release** you configured (**`release_tag`**: `latest` vs pinned tag). Newer builds show the failing URL and TLS hints in the error text.
|
||||
9. **`sync Wow.exe: fetch failed`** — Often **HTTPS/TLS** to Gitea; use **`http://…`** in **`lib/baked-gitea-channel.js`** if you only serve plain HTTP, or fix certs / **`NODE_EXTRA_CA_CERTS`**. Ensure **`Wow-patched.exe`** exists on the release (**`release_tag`**: `latest` vs pinned). Errors include the failing URL when possible.
|
||||
10. **Wine + Windows portable** — If the folder picker returns **`/home/...`**, the launcher maps it to **`Z:\home\...`** (Wine’s Unix root). **`Wow.exe`** is matched case-insensitively for Linux-backed folders. Re-save the WoW folder after upgrading if validation still fails.
|
||||
|
||||
### Private Gitea token for players
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
* Token stays in env: GITEA_TOKEN or launcher.json → gitea.token_env.
|
||||
*/
|
||||
module.exports = {
|
||||
// Scheme optional — gitea-release normalizes to https:// if missing.
|
||||
base_url: 'https://brassnet.ddns.net:33983',
|
||||
// http:// kept as-is; bare host gets https in gitea-release.js
|
||||
base_url: 'http://brassnet.ddns.net:33983',
|
||||
owner: 'Dawnsorrow',
|
||||
repo: 'Fractured-Distro',
|
||||
release_tag: 'latest',
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
const path = require('path');
|
||||
const fs = require('fs').promises;
|
||||
const { normalizeWinGameDir } = require('./win-game-dir');
|
||||
|
||||
/** 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']);
|
||||
@@ -105,8 +106,9 @@ async function saveGameDir(configPath, gameDir) {
|
||||
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));
|
||||
const abs = path.isAbsolute(gd) ? path.normalize(gd) : path.normalize(path.join(path.dirname(configPath), gd));
|
||||
if (process.platform === 'win32') return normalizeWinGameDir(abs);
|
||||
return abs;
|
||||
}
|
||||
|
||||
module.exports = { getConfigPath, loadConfig, saveGameDir, resolveGameDir, mergeConfig, applyBakedGitea };
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
const path = require('path');
|
||||
const fs = require('fs').promises;
|
||||
const fsSync = require('fs');
|
||||
const { downloadGitHubRepoFile, downloadReleaseAsset } = require('./github');
|
||||
const { normalizeWinGameDir } = require('./win-game-dir');
|
||||
|
||||
function pad2(n) {
|
||||
return String(n).padStart(2, '0');
|
||||
@@ -13,19 +15,38 @@ function backupSuffix() {
|
||||
}
|
||||
|
||||
function wowExePath(cfg) {
|
||||
const gd = normalizeWinGameDir(cfg.game_dir || '');
|
||||
const exe = (cfg.launch && cfg.launch.exe) || 'Wow.exe';
|
||||
const parts = exe.replace(/\\/g, '/').split('/').filter(Boolean);
|
||||
return path.join(cfg.game_dir, ...parts);
|
||||
const primary = path.join(gd, ...parts);
|
||||
if (process.platform === 'win32' && gd && fsSync.existsSync(primary)) return primary;
|
||||
if (process.platform === 'win32' && gd) {
|
||||
try {
|
||||
const base = path.basename(primary);
|
||||
const dir = path.dirname(primary);
|
||||
const names = fsSync.readdirSync(dir);
|
||||
const hit = names.find((n) => n.toLowerCase() === base.toLowerCase());
|
||||
if (hit) {
|
||||
const alt = path.join(dir, hit);
|
||||
if (fsSync.statSync(alt).isFile()) return alt;
|
||||
}
|
||||
} catch (_) {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
return primary;
|
||||
}
|
||||
|
||||
function wowInstallValid(cfg) {
|
||||
if (!cfg.game_dir) return false;
|
||||
return require('fs').existsSync(wowExePath(cfg));
|
||||
const p = wowExePath(cfg);
|
||||
return fsSync.existsSync(p) && fsSync.statSync(p).isFile();
|
||||
}
|
||||
|
||||
async function installFile(cfg, entry) {
|
||||
const parts = String(entry.dest).replace(/\\/g, '/').split('/').filter(Boolean);
|
||||
const destAbs = path.join(cfg.game_dir, ...parts);
|
||||
const root = normalizeWinGameDir(cfg.game_dir || '');
|
||||
const destAbs = path.join(root, ...parts);
|
||||
if (entry.backup) {
|
||||
try {
|
||||
const st = await fs.stat(destAbs);
|
||||
@@ -66,7 +87,7 @@ async function applyRealmlist(cfg) {
|
||||
const r = String(rel).trim().replace(/\\/g, '/');
|
||||
if (!r) continue;
|
||||
const segs = r.split('/').filter(Boolean);
|
||||
const abs = path.join(cfg.game_dir, ...segs);
|
||||
const abs = path.join(normalizeWinGameDir(cfg.game_dir || ''), ...segs);
|
||||
await fs.mkdir(path.dirname(abs), { recursive: true });
|
||||
await fs.writeFile(abs, content, 'utf8');
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
'use strict';
|
||||
|
||||
const path = require('path');
|
||||
|
||||
/**
|
||||
* Under Wine, the folder picker often returns a Unix absolute path (/home/...).
|
||||
* Windows Node does not resolve that to the WoW install; map to Wine's Z: drive
|
||||
* (Z: == / on typical Wine prefixes).
|
||||
*/
|
||||
function normalizeWinGameDir(gameDir) {
|
||||
if (process.platform !== 'win32') return String(gameDir || '').trim();
|
||||
let s = String(gameDir || '').trim();
|
||||
if (!s) return s;
|
||||
s = s.replace(/\//g, path.win32.sep);
|
||||
if (s.startsWith('\\\\')) return path.normalize(s);
|
||||
if (/^[A-Za-z]:/.test(s)) return path.normalize(s);
|
||||
if (s.startsWith(path.win32.sep)) return path.win32.normalize(`Z:${s}`);
|
||||
return path.normalize(s);
|
||||
}
|
||||
|
||||
module.exports = { normalizeWinGameDir };
|
||||
@@ -4,6 +4,7 @@ const { app, BrowserWindow, ipcMain, dialog, Menu } = require('electron');
|
||||
const path = require('path');
|
||||
const { spawn } = require('child_process');
|
||||
const { loadConfig, saveGameDir, resolveGameDir } = require('./lib/config-store');
|
||||
const { normalizeWinGameDir } = require('./lib/win-game-dir');
|
||||
const { applyPatches, wowExePath, wowInstallValid, doAuth } = require('./lib/patch');
|
||||
const { readPatchState } = require('./lib/patch-manifest');
|
||||
const { setupAutoUpdater } = require('./lib/auto-update');
|
||||
@@ -95,7 +96,8 @@ ipcMain.handle('launcher:saveGameDir', async (_e, dir) => {
|
||||
const trimmed = String(dir || '').trim();
|
||||
if (!trimmed) throw new Error('folder path is empty');
|
||||
const { configPath } = await loadConfig(app);
|
||||
const norm = path.normalize(trimmed);
|
||||
const norm =
|
||||
process.platform === 'win32' ? normalizeWinGameDir(path.normalize(trimmed)) : path.normalize(trimmed);
|
||||
const probe = { ...(await readMergedConfig()).config, game_dir: norm };
|
||||
if (!wowInstallValid(probe)) {
|
||||
throw new Error(`That folder does not contain ${(probe.launch && probe.launch.exe) || 'Wow.exe'}`);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "fractured-launcher-electron",
|
||||
"version": "1.0.5",
|
||||
"version": "1.0.6",
|
||||
"description": "Fractured WoW launcher (Electron) — no console window, native folder picker, auto-update",
|
||||
"main": "main.js",
|
||||
"repository": {
|
||||
@@ -36,6 +36,7 @@
|
||||
"renderer.js",
|
||||
"styles.css",
|
||||
"default-launcher.json",
|
||||
"lib/win-game-dir.js",
|
||||
"lib/baked-gitea-channel.js",
|
||||
"lib/gitea-release.js",
|
||||
"lib/patch-manifest.js",
|
||||
|
||||
Reference in New Issue
Block a user