feat: add way to select game dir
This commit is contained in:
+35
-1
@@ -1,4 +1,4 @@
|
||||
import { app, shell, BrowserWindow, ipcMain } from 'electron'
|
||||
import { app, shell, BrowserWindow, ipcMain, dialog } from 'electron'
|
||||
import { join } from 'path'
|
||||
import { setMainWindow } from './events'
|
||||
import { IPC, type UserSettings, type PlayOptions } from '../shared/ipc'
|
||||
@@ -8,6 +8,12 @@ import { getPackMetaCached } from './modpack'
|
||||
import { getSettings, setSettings } from './settings'
|
||||
import { paths } from './paths'
|
||||
import { initUpdater, quitAndInstallUpdate } from './updater'
|
||||
import {
|
||||
isFirstLaunch,
|
||||
writeLauncherConfig,
|
||||
defaultDataDir,
|
||||
readLauncherConfigSync
|
||||
} from './launcher-config'
|
||||
|
||||
function createWindow(): BrowserWindow {
|
||||
const win = new BrowserWindow({
|
||||
@@ -60,6 +66,34 @@ function registerIpc(): void {
|
||||
ipcMain.handle(IPC.openInstanceDir, () => shell.openPath(paths.instanceDir))
|
||||
ipcMain.handle(IPC.openLogsDir, () => shell.openPath(paths.logsDir))
|
||||
ipcMain.handle(IPC.updateInstall, () => quitAndInstallUpdate())
|
||||
|
||||
ipcMain.handle(IPC.isFirstLaunch, () => isFirstLaunch())
|
||||
|
||||
ipcMain.handle(IPC.dataDirGet, () => {
|
||||
const { dataDir } = readLauncherConfigSync()
|
||||
const defaultSuggestion = defaultDataDir()
|
||||
return { current: dataDir ?? defaultSuggestion, defaultSuggestion }
|
||||
})
|
||||
|
||||
ipcMain.handle(IPC.dataDirBrowse, async () => {
|
||||
const { dataDir } = readLauncherConfigSync()
|
||||
const { canceled, filePaths } = await dialog.showOpenDialog({
|
||||
title: 'Choisir le dossier de données',
|
||||
defaultPath: dataDir ?? defaultDataDir(),
|
||||
properties: ['openDirectory', 'createDirectory']
|
||||
})
|
||||
return canceled ? null : filePaths[0]
|
||||
})
|
||||
|
||||
ipcMain.handle(IPC.dataDirSet, async (_e, dir: string, relaunch: boolean) => {
|
||||
await writeLauncherConfig({ dataDir: dir })
|
||||
paths.invalidate()
|
||||
if (relaunch) {
|
||||
app.relaunch()
|
||||
app.exit(0)
|
||||
}
|
||||
return dir
|
||||
})
|
||||
}
|
||||
|
||||
app.whenReady().then(() => {
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import { app } from 'electron'
|
||||
import { join } from 'path'
|
||||
import { existsSync, readFileSync } from 'fs'
|
||||
import { writeFile } from 'fs/promises'
|
||||
|
||||
interface LauncherConfig {
|
||||
dataDir?: string
|
||||
}
|
||||
|
||||
function configPath(): string {
|
||||
return join(app.getPath('userData'), 'launcher.json')
|
||||
}
|
||||
|
||||
export function readLauncherConfigSync(): LauncherConfig {
|
||||
try {
|
||||
return JSON.parse(readFileSync(configPath(), 'utf-8')) as LauncherConfig
|
||||
} catch {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
export function isFirstLaunch(): boolean {
|
||||
return !existsSync(configPath())
|
||||
}
|
||||
|
||||
export async function writeLauncherConfig(cfg: LauncherConfig): Promise<void> {
|
||||
const current = readLauncherConfigSync()
|
||||
await writeFile(configPath(), JSON.stringify({ ...current, ...cfg }, null, 2))
|
||||
}
|
||||
|
||||
export function defaultDataDir(): string {
|
||||
return join(app.getPath('home'), 'Games', 'OFLauncher')
|
||||
}
|
||||
+22
-9
@@ -1,19 +1,34 @@
|
||||
import { app } from 'electron'
|
||||
import { join } from 'path'
|
||||
import { mkdirSync } from 'fs'
|
||||
import { readLauncherConfigSync } from './launcher-config'
|
||||
|
||||
/**
|
||||
* Arborescence des données du launcher, rangée sous le userData d'Electron :
|
||||
* Windows : %APPDATA%/OFLauncher
|
||||
* Linux : ~/.config/OFLauncher
|
||||
* Arborescence des données du launcher.
|
||||
*
|
||||
* On garde le runtime (MC/NeoForge/assets/libs/java) séparé de l'instance de
|
||||
* jeu (mods/config/saves) pour que la sync packwiz ne touche jamais au runtime.
|
||||
* La racine est configurable via launcher.json (userData/launcher.json) :
|
||||
* { "dataDir": "/chemin/choisi/par/l/utilisateur" }
|
||||
* Par défaut : userData d'Electron (%APPDATA%/OFLauncher sur Windows,
|
||||
* ~/.config/OFLauncher sur Linux).
|
||||
*
|
||||
* La config launcher.json elle-même reste toujours dans userData
|
||||
* (point d'ancrage fixe, indépendant du dataDir choisi).
|
||||
*/
|
||||
class LauncherPaths {
|
||||
/** Racine : userData d'Electron. */
|
||||
private _root: string | null = null
|
||||
|
||||
/** Racine des données du jeu (configurable). */
|
||||
get root(): string {
|
||||
return app.getPath('userData')
|
||||
if (!this._root) {
|
||||
const { dataDir } = readLauncherConfigSync()
|
||||
this._root = dataDir ?? app.getPath('userData')
|
||||
}
|
||||
return this._root
|
||||
}
|
||||
|
||||
/** Invalide le cache de root (à appeler après écriture d'un nouveau dataDir). */
|
||||
invalidate(): void {
|
||||
this._root = null
|
||||
}
|
||||
|
||||
/** Dossier "Minecraft" géré par @xmcl : versions/, libraries/, assets/. */
|
||||
@@ -53,8 +68,6 @@ class LauncherPaths {
|
||||
|
||||
/** jar packwiz-installer-bootstrap embarqué dans les resources. */
|
||||
get packwizBootstrapJar(): string {
|
||||
// En prod, electron-builder copie resources/ via extraResources.
|
||||
// En dev, on lit directement le dossier resources/ du repo.
|
||||
const base = app.isPackaged
|
||||
? join(process.resourcesPath, 'resources')
|
||||
: join(app.getAppPath(), 'resources')
|
||||
|
||||
Reference in New Issue
Block a user