feat: add some funcs

This commit is contained in:
lucasdpt
2026-06-14 14:23:37 +02:00
parent 48fa508540
commit 4756420f8d
11 changed files with 240 additions and 44 deletions
+49 -19
View File
@@ -1,8 +1,10 @@
import { existsSync } from 'fs'
import { join } from 'path'
import { getVersionList, install, installNeoForged } from '@xmcl/installer'
import { getVersionList, installTask, installNeoForgedTask } from '@xmcl/installer'
import type { Task } from '@xmcl/task'
import { paths } from './paths'
import { emit } from './events'
import { type LaunchPhase } from '../shared/ipc'
import { downloadDispatcher, DOWNLOAD_CONCURRENCY, withRetries } from './net'
/** Options de téléchargement communes : dispatcher tolérant + concurrence bridée. */
@@ -12,6 +14,25 @@ const downloadOptions = {
librariesDownloadConcurrency: DOWNLOAD_CONCURRENCY
}
/**
* Exécute une task @xmcl en émettant sa progression réelle (0..1) vers le
* renderer. On lit la progression cumulée de la task racine ; on n'émet qu'au
* changement de pourcentage entier pour ne pas inonder l'IPC.
*/
async function runWithProgress<T>(phase: LaunchPhase, message: string, task: Task<T>): Promise<T> {
let lastPct = -1
return task.startAndWait({
onUpdate() {
if (task.total <= 0) return
const ratio = task.progress / task.total
const pct = Math.floor(ratio * 100)
if (pct === lastPct) return
lastPct = pct
emit.progress({ phase, message, progress: Math.min(1, ratio) })
}
})
}
/**
* Installe le runtime Minecraft (vanilla 1.21.1) puis NeoForge dans
* paths.gameRoot. Les deux étapes sont idempotentes : si la version est déjà
@@ -25,25 +46,27 @@ function versionInstalled(id: string): boolean {
return existsSync(join(paths.gameRoot, 'versions', id, `${id}.json`))
}
/** Installe Minecraft vanilla <version> (json + jar + assets + libraries). */
export async function installMinecraft(version: string): Promise<void> {
if (versionInstalled(version)) return
/**
* Installe Minecraft vanilla <version> (json + jar + assets + libraries).
* `force` saute le court-circuit "déjà installé" pour revalider/réparer les
* fichiers (l'install @xmcl ignore les fichiers déjà valides par checksum).
*/
export async function installMinecraft(version: string, force = false): Promise<void> {
if (!force && versionInstalled(version)) return
emit.progress({ phase: 'minecraft', message: `Minecraft ${version} : métadonnées…`, progress: undefined })
const list = await getVersionList()
const meta = list.versions.find((v) => v.id === version)
if (!meta) throw new Error(`Version Minecraft introuvable : ${version}`)
emit.progress({
phase: 'minecraft',
message: `Installation de Minecraft ${version} (assets + libs)…`,
progress: undefined
})
const label = force
? `Vérification de Minecraft ${version}`
: `Installation de Minecraft ${version} (assets + libs)…`
// ~3700 assets : on réessaie l'install entière plusieurs fois. Chaque passe
// ignore les fichiers déjà valides et ne reprend que les manquants.
await withRetries(
() => install(meta, paths.gameRoot, downloadOptions),
() => runWithProgress('minecraft', label, installTask(meta, paths.gameRoot, downloadOptions)),
5,
(attempt) =>
emit.progress({
@@ -61,26 +84,33 @@ export async function installMinecraft(version: string): Promise<void> {
export async function installNeoForge(
neoforge: string,
minecraft: string,
javaPath: string
javaPath: string,
force = false
): Promise<string> {
// @xmcl nomme la version installée "neoforge-<version>" (ex. neoforge-21.1.224).
// Si elle est déjà présente, on saute l'install (idempotent, comme Minecraft).
const expectedId = `neoforge-${neoforge}`
if (versionInstalled(expectedId)) {
if (!force && versionInstalled(expectedId)) {
void minecraft
return expectedId
}
emit.progress({ phase: 'neoforge', message: `Installation de NeoForge ${neoforge}`, progress: undefined })
const label = force
? `Vérification de NeoForge ${neoforge}`
: `Installation de NeoForge ${neoforge}`
// installNeoForged retourne l'id de version installée à lancer.
// installNeoForgedTask retourne l'id de version installée à lancer.
const versionId = await withRetries(
() =>
installNeoForged('neoforge', neoforge, paths.gameRoot, {
java: javaPath,
dispatcher: downloadDispatcher,
librariesDownloadConcurrency: DOWNLOAD_CONCURRENCY
}),
runWithProgress(
'neoforge',
label,
installNeoForgedTask('neoforge', neoforge, paths.gameRoot, {
java: javaPath,
dispatcher: downloadDispatcher,
librariesDownloadConcurrency: DOWNLOAD_CONCURRENCY
})
),
3,
(attempt) =>
emit.progress({