Skip to content

Commit

Permalink
fix: Forge cannot install with wrong java version
Browse files Browse the repository at this point in the history
  • Loading branch information
ci010 committed Jan 16, 2025
1 parent be71455 commit f4ddce0
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 70 deletions.
114 changes: 54 additions & 60 deletions xmcl-keystone-ui/src/composables/instanceVersionInstall.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { appInsights } from '@/telemetry'
import { AnyError } from '@/util/error'
import { getSWRV } from '@/util/swrvGet'
import type { AssetIndexIssue, AssetIssue, JavaVersion, LibraryIssue, MinecraftJarIssue, ResolvedVersion } from '@xmcl/core'
import type { InstallProfileIssueReport } from '@xmcl/installer'
import { Mutex } from 'async-mutex'
import { DiagnoseServiceKey, InstallServiceKey, Instance, InstanceServiceKey, JavaRecord, JavaServiceKey, RuntimeVersions, ServerVersionHeader, VersionHeader, VersionServiceKey, parseOptifineVersion } from '@xmcl/runtime-api'
import { Mutex } from 'async-mutex'
import { InjectionKey, Ref, ShallowRef } from 'vue'
import { InstanceResolveVersion } from './instanceVersion'
import { useNotifier } from './notifier'
import { useService } from './service'
import { kSWRVConfig } from './swrvConfig'
import { getForgeVersionsModel, getLabyModManifestModel, getMinecraftVersionsModel, getNeoForgedVersionModel } from './version'
import { useNotifier } from './notifier'
import { appInsights } from '@/telemetry'
import { AnyError } from '@/util/error'

export interface InstanceInstallInstruction {
instance: string
Expand All @@ -37,7 +37,18 @@ export interface InstanceInstallInstruction {
export const kInstanceVersionInstall = Symbol('InstanceVersionInstall') as InjectionKey<ReturnType<typeof useInstanceVersionInstallInstruction>>
const kAbort = Symbol('Aborted')

function useInstanceVersionInstall(versions: Ref<VersionHeader[]>, servers: Ref<ServerVersionHeader[]>, javas: Ref<JavaRecord[]>) {
function getJavaPathOrInstall(instances: Instance[], javas: JavaRecord[], resolved: ResolvedVersion, instance: string) {
const inst = instances.find(i => i.path === instance)
if (inst?.java) {
return inst.java
}
const validJava = javas.find(v => v.majorVersion === resolved.javaVersion.majorVersion && v.valid)
console.log('validJava', validJava)
return validJava ? validJava.path : resolved.javaVersion
}


function useInstanceVersionInstall(versions: Ref<VersionHeader[]>, servers: Ref<ServerVersionHeader[]>, instances: Ref<Instance[]>, javas: Ref<JavaRecord[]>) {
const {
installForge,
installNeoForged,
Expand Down Expand Up @@ -74,6 +85,7 @@ function useInstanceVersionInstall(versions: Ref<VersionHeader[]>, servers: Ref<
await installMinecraftJar(localMinecraft.id, 'client')
}

const resolvedMcVersion = await resolveLocalVersion(minecraft)
let forgeVersion = undefined as undefined | string
if (forge) {
const localForge = local.find(v => v.forge === forge && v.minecraft === minecraft)
Expand All @@ -82,13 +94,10 @@ function useInstanceVersionInstall(versions: Ref<VersionHeader[]>, servers: Ref<
const found = forgeVersions.find(v => v.version === forge)
const forgeVersionId = found?.version ?? forge

if (javas.value.length === 0 || javas.value.every(java => !java.valid)) {
// no valid java
const mcVersionResolved = await resolveLocalVersion(minecraft)
await installDefaultJava(mcVersionResolved.javaVersion)
}
const javaOrInstall = getJavaPathOrInstall(instances.value, javas.value, resolvedMcVersion, '')
const javaPath = typeof javaOrInstall === 'string' ? javaOrInstall : await installDefaultJava(javaOrInstall).then((r) => r.path)

forgeVersion = await installForge({ mcversion: minecraft, version: forgeVersionId, installer: found?.installer })
forgeVersion = await installForge({ mcversion: minecraft, version: forgeVersionId, installer: found?.installer, java: javaPath })
} else {
forgeVersion = localForge.id
await refreshVersion(localForge.id)
Expand All @@ -102,13 +111,10 @@ function useInstanceVersionInstall(versions: Ref<VersionHeader[]>, servers: Ref<
const found = neoForgedVersion.find(v => v === neoForged)
const id = found ?? neoForged

if (javas.value.length === 0 || javas.value.every(java => !java.valid)) {
// no valid java
const mcVersionResolved = await resolveLocalVersion(minecraft)
await installDefaultJava(mcVersionResolved.javaVersion)
}
const javaOrInstall = getJavaPathOrInstall(instances.value, javas.value, resolvedMcVersion, '')
const javaPath = typeof javaOrInstall === 'string' ? javaOrInstall : await installDefaultJava(javaOrInstall).then((r) => r.path)

forgeVersion = await installNeoForged({ version: id, minecraft })
forgeVersion = await installNeoForged({ version: id, minecraft, java: javaPath })
} else {
forgeVersion = localNeoForge.id
await refreshVersion(localNeoForge.id)
Expand All @@ -126,7 +132,11 @@ function useInstanceVersionInstall(versions: Ref<VersionHeader[]>, servers: Ref<
return localOptifine.id
}
const { type, patch } = parseOptifineVersion(optifineVersion)
const [ver] = await installOptifine({ type, patch, mcversion: minecraft, inheritFrom: forgeVersion })

const javaOrInstall = getJavaPathOrInstall(instances.value, javas.value, resolvedMcVersion, '')
const javaPath = typeof javaOrInstall === 'string' ? javaOrInstall : await installDefaultJava(javaOrInstall).then((r) => r.path)

const ver = await installOptifine({ type, patch, mcversion: minecraft, inheritFrom: forgeVersion, java: javaPath })
return ver
} else if (forgeVersion) {
return forgeVersion
Expand Down Expand Up @@ -239,7 +249,7 @@ export function useInstanceVersionInstallInstruction(path: Ref<string>, instance
const { installDefaultJava } = useService(JavaServiceKey)
const { notify } = useNotifier()

const { install, installServer } = useInstanceVersionInstall(versions, servers, javas)
const { install, installServer } = useInstanceVersionInstall(versions, servers, instances, javas)

let abortController = new AbortController()
const instruction: ShallowRef<InstanceInstallInstruction | undefined> = shallowRef(undefined)
Expand Down Expand Up @@ -293,16 +303,6 @@ export function useInstanceVersionInstallInstruction(path: Ref<string>, instance
return newLock
}

function getJavaInstall(javas: JavaRecord[], resolved: ResolvedVersion, instance: string) {
const inst = instances.value.find(i => i.path === instance)
if (inst?.java) {
return undefined
}
const validJava = javas.find(v => v.majorVersion === resolved.javaVersion.majorVersion && v.valid)
console.log('validJava', validJava)
return validJava ? undefined : resolved.javaVersion
}

/**
* @param instance The instance path
* @param runtime The runtime version
Expand All @@ -322,7 +322,10 @@ export function useInstanceVersionInstallInstruction(path: Ref<string>, instance

result.resolvedVersion = resolved.id

result.java = getJavaInstall(javas, resolved, instance)
const javaInstallOrPath = getJavaPathOrInstall(instances.value, javas, resolved, instance)
if (typeof javaInstallOrPath === 'object') {
result.java = javaInstallOrPath
}

const profileIssue = await diagnoseProfile(resolved.id, 'client', path.value)
if (abortSignal?.aborted) { throw kAbort }
Expand Down Expand Up @@ -414,8 +417,8 @@ export function useInstanceVersionInstallInstruction(path: Ref<string>, instance
if (version) {
await installDependencies(version, 'client')
const resolved = await resolveLocalVersion(version)
const java = getJavaInstall(javas.value, resolved, instruction.instance)
if (java) {
const java = getJavaPathOrInstall(instances.value, javas.value, resolved, instruction.instance)
if (typeof java === 'object') {
await installDefaultJava(java)
}
}
Expand All @@ -427,14 +430,14 @@ export function useInstanceVersionInstallInstruction(path: Ref<string>, instance
await installMinecraftJar(instruction.runtime.minecraft, 'client')
}
if (instruction.profile) {
await installByProfile(instruction.profile.installProfile)
const resolved = await resolveLocalVersion(instruction.version)
const java = getJavaPathOrInstall(instances.value, javas.value, resolved, instruction.instance)
const javaPath = typeof java === 'string' ? java : await installDefaultJava(java).then((r) => r.path)

await installByProfile({ profile: instruction.profile.installProfile, side: 'client', java: javaPath })

if (instruction.version) {
await installDependencies(instruction.version, 'client')
const resolved = await resolveLocalVersion(instruction.version)
const java = getJavaInstall(javas.value, resolved, instruction.instance)
if (java) {
await installDefaultJava(java)
}
}
return
}
Expand All @@ -444,43 +447,34 @@ export function useInstanceVersionInstallInstruction(path: Ref<string>, instance
type: instruction.optifine.type,
patch: instruction.optifine.patch,
})
if (version) {
await installDependencies(version, 'client')
const resolved = await resolveLocalVersion(version)
const java = getJavaInstall(javas.value, resolved, instruction.instance)
if (java) {
await installDefaultJava(java)
}
await installDependencies(version, 'client')
const resolved = await resolveLocalVersion(version)
const java = getJavaPathOrInstall(instances.value, javas.value, resolved, instruction.instance)
if (typeof java === 'object') {
await installDefaultJava(java)
}
await commit(version)
return
}
if (instruction.forge) {
if (javas.value.length === 0 || javas.value.every(java => !java.valid)) {
// no valid java
const mcVersionResolved = await resolveLocalVersion(instruction.forge.minecraft)
await installDefaultJava(mcVersionResolved.javaVersion)
}
const resolved = await resolveLocalVersion(instruction.forge.minecraft)
const java = getJavaPathOrInstall(instances.value, javas.value, resolved, instruction.instance)
const javaPath = typeof java === 'string' ? java : await installDefaultJava(java).then((r) => r.path)

const version = await installForge({
mcversion: instruction.forge.minecraft,
version: instruction.forge.version,
java: javaPath,
side: 'client',
})
if (version) {
await installDependencies(version, 'client')
const resolved = await resolveLocalVersion(version)
const java = getJavaInstall(javas.value, resolved, instruction.instance)
if (java) {
await installDefaultJava(java)
}
}
await installDependencies(version, 'client')
await commit(version)
return
}

const resolved = await resolveLocalVersion(instruction.resolvedVersion)
const java = getJavaInstall(javas.value, resolved, instruction.instance)
if (java) {
const java = getJavaPathOrInstall(instances.value, javas.value, resolved, instruction.instance)
if (typeof java === 'object') {
await installDefaultJava(java)
}
if (instruction.libriares) {
Expand Down
23 changes: 22 additions & 1 deletion xmcl-runtime-api/src/services/InstallService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ export interface InstallOptifineOptions extends OptifineVersion {
*/
forgeVersion?: string
inheritFrom?: string

java?: string
}

export interface InstallOptifineAsModOptions extends OptifineVersion {
Expand Down Expand Up @@ -59,11 +61,26 @@ export interface InstallForgeOptions {
*/
version: string

/**
* The java path
*/
java?: string

side?: 'client' | 'server'

root?: string
}

export interface InstallProfileOptions {
profile: InstallProfile

version?: string

side?: 'client' | 'server'

java?: string
}

export interface InstallNeoForgedOptions {
/**
* The minecraft version
Expand All @@ -73,6 +90,10 @@ export interface InstallNeoForgedOptions {
* The forge version (without minecraft version)
*/
version: string
/**
* The java path
*/
java?: string

side?: 'client' | 'server'
}
Expand Down Expand Up @@ -178,7 +199,7 @@ export interface InstallService {

installQuilt(meta: InstallQuiltOptions): Promise<string>

installByProfile(profile: InstallProfile): Promise<void>
installByProfile(profile: InstallProfileOptions): Promise<void>
}

export const InstallServiceKey: ServiceKey<InstallService> = 'InstallService'
2 changes: 1 addition & 1 deletion xmcl-runtime-api/src/services/JavaService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export interface JavaService {
/**
* Install a default jdk 8 or 16 to the a preserved location. It'll be installed under your launcher root location `jre` or `jre-next` folder
*/
installDefaultJava(version?: JavaVersion): Promise<undefined | Java>
installDefaultJava(version?: JavaVersion): Promise<Java>
/**
* Resolve java info. If the java is not known by launcher. It will cache it into the launcher java list.
*/
Expand Down
24 changes: 18 additions & 6 deletions xmcl-runtime/install/InstallService.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { checksum, MinecraftFolder, ResolvedLibrary, Version } from '@xmcl/core'
import { DownloadBaseOptions } from '@xmcl/file-transfer'
import { DEFAULT_FORGE_MAVEN, DEFAULT_RESOURCE_ROOT_URL, DownloadTask, installAssetsTask, installByProfileTask, installFabric, InstallForgeOptions, installForgeTask, InstallJarTask, InstallJsonTask, installLabyMod4Task, installLibrariesTask, installLiteloaderTask, installNeoForgedTask, installOptifineTask, InstallProfile, installQuiltVersion, installResolvedAssetsTask, installResolvedLibrariesTask, installVersionTask, LiteloaderVersion, MinecraftVersion, Options, PostProcessFailedError } from '@xmcl/installer'
import { InstallForgeOptions as _InstallForgeOptions, Asset, InstallService as IInstallService, InstallableLibrary, InstallFabricOptions, InstallLabyModOptions, InstallNeoForgedOptions, InstallOptifineAsModOptions, InstallOptifineOptions, InstallQuiltOptions, InstallServiceKey, isFabricLoaderLibrary, isForgeLibrary, LockKey, SharedState, OptifineVersion, Settings } from '@xmcl/runtime-api'
import { DEFAULT_FORGE_MAVEN, DEFAULT_RESOURCE_ROOT_URL, DownloadTask, installAssetsTask, installByProfileTask, installFabric, InstallForgeOptions, installForgeTask, InstallJarTask, InstallJsonTask, installLabyMod4Task, installLibrariesTask, installLiteloaderTask, installNeoForgedTask, installOptifineTask, installQuiltVersion, installResolvedAssetsTask, installResolvedLibrariesTask, installVersionTask, LiteloaderVersion, MinecraftVersion, Options, PostProcessFailedError } from '@xmcl/installer'
import { InstallForgeOptions as _InstallForgeOptions, Asset, InstallService as IInstallService, InstallableLibrary, InstallFabricOptions, InstallLabyModOptions, InstallNeoForgedOptions, InstallOptifineAsModOptions, InstallOptifineOptions, InstallProfileOptions, InstallQuiltOptions, InstallServiceKey, isFabricLoaderLibrary, isForgeLibrary, LockKey, OptifineVersion, Settings, SharedState } from '@xmcl/runtime-api'
import { CancelledError, task } from '@xmcl/task'
import { spawn } from 'child_process'
import { existsSync } from 'fs'
Expand Down Expand Up @@ -355,7 +355,13 @@ export class InstallService extends AbstractService implements IInstallService {
const validJavaPaths = this.javaService.state.all.filter(v => v.valid)
const installOptions = this.getForgeInstallOptions()

validJavaPaths.sort((a, b) => a.majorVersion === 8 ? -1 : b.majorVersion === 8 ? 1 : -1)
if (options.java) {
const java = validJavaPaths.find(v => v.path === options.java)
if (java) {
validJavaPaths.splice(validJavaPaths.indexOf(java), 1)
validJavaPaths.unshift(java)
}
}

let version: string | undefined
for (const java of validJavaPaths) {
Expand Down Expand Up @@ -621,7 +627,7 @@ export class InstallService extends AbstractService implements IInstallService {
}
}

const java = this.javaService.getPreferredJava()?.path
const java = options.java ?? this.javaService.getPreferredJava()?.path

const urls = [
await this.getOptifineDownloadUrl(options),
Expand Down Expand Up @@ -652,7 +658,7 @@ export class InstallService extends AbstractService implements IInstallService {
return id
}, { id: optifineVersion }))

this.log(`Succeed to install optifine ${version} on ${options.inheritFrom ?? options.mcversion}. ${result[0]}`)
this.log(`Succeed to install optifine ${version} on ${options.inheritFrom ?? options.mcversion}. ${result}`)

return result
}
Expand All @@ -666,10 +672,12 @@ export class InstallService extends AbstractService implements IInstallService {
}
}

async installByProfile(profile: InstallProfile, version?: string) {
async installByProfile({ profile, version, side, java }: InstallProfileOptions) {
try {
await this.submit(installByProfileTask(profile, this.getPath(), {
...this.getForgeInstallOptions(),
side,
java,
}).setName('installForge', { id: version ?? profile.version }))
} catch (err) {
if (err instanceof CancelledError) {
Expand All @@ -679,6 +687,8 @@ export class InstallService extends AbstractService implements IInstallService {
await this.installNeoForged({
minecraft: profile.minecraft,
version: profile.version.substring('neoforge-'.length),
side,
java,
})
} else {
const forgeVersion = profile.version.indexOf('-forge-') !== -1
Expand All @@ -689,6 +699,8 @@ export class InstallService extends AbstractService implements IInstallService {
await this.installForge({
version: forgeVersion,
mcversion: profile.minecraft,
side,
java,
})
}
this.warn(err)
Expand Down
9 changes: 7 additions & 2 deletions xmcl-runtime/java/JavaService.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { JavaVersion } from '@xmcl/core'
import { DEFAULT_RUNTIME_ALL_URL, JavaRuntimeManifest, JavaRuntimeTargetType, JavaRuntimes, installJavaRuntimeTask, parseJavaVersion, resolveJava, scanLocalJava } from '@xmcl/installer'
import { JavaService as IJavaService, Java, JavaRecord, JavaSchema, JavaServiceKey, JavaState, SharedState, Settings } from '@xmcl/runtime-api'
import { JavaService as IJavaService, Java, JavaRecord, JavaSchema, JavaServiceKey, JavaState, Settings, SharedState } from '@xmcl/runtime-api'
import { chmod, ensureFile, readFile, stat } from 'fs-extra'
import { dirname, join } from 'path'
import { Inject, LauncherAppKey, PathResolver, kGameDataPath } from '~/app'
Expand All @@ -10,6 +10,7 @@ import { kDownloadOptions } from '~/network'
import { ExposeServiceKey, ServiceStateManager, Singleton, StatefulService } from '~/service'
import { getApiSets, shouldOverrideApiSet } from '~/settings'
import { TaskFn, kTaskExecutor } from '~/task'
import { AnyError } from '~/util/error'
import { LauncherApp } from '../app/LauncherApp'
import { readdirIfPresent } from '../util/fs'
import { requireString } from '../util/object'
Expand Down Expand Up @@ -213,7 +214,11 @@ export class JavaService extends StatefulService<JavaState> implements IJavaServ
await chmod(location, 0o765)
}
this.log(`Successfully install java internally ${location}`)
return await this.resolveJava(location)
const result = await this.resolveJava(location)
if (!result) {
throw new AnyError('InstallDefaultJavaError', 'Fail to install java')
}
return result
}

async validateJavaPath(javaPath: string): Promise<JavaValidation> {
Expand Down

0 comments on commit f4ddce0

Please sign in to comment.