Skip to content

Commit

Permalink
fix: Handle some install failures making the modpack install process …
Browse files Browse the repository at this point in the history
…more fluent
  • Loading branch information
ci010 committed Dec 30, 2023
1 parent 4ced1de commit c6aa77f
Show file tree
Hide file tree
Showing 11 changed files with 52 additions and 14 deletions.
2 changes: 1 addition & 1 deletion xmcl
2 changes: 1 addition & 1 deletion xmcl-keystone-ui/src/components/ModpackListItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@
<v-btn
icon
text
@click="emit('create')"
@click.stop="emit('create')"
>
<v-icon>
add
Expand Down
5 changes: 4 additions & 1 deletion xmcl-keystone-ui/src/composables/curseforgeInstall.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { getCurseforgeProjectModel } from './curseforge'
import { useDialog } from './dialog'
import { AddInstanceDialogKey } from './instanceTemplates'
import { InstanceInstallDialog } from './instanceUpdate'
import { kInstanceVersionDiagnose } from './instanceVersionDiagnose'
import { kInstances } from './instances'
import { useNotifier } from './notifier'
import { useResourceUrisDiscovery } from './resources'
Expand Down Expand Up @@ -120,6 +121,7 @@ export function useCurseforgeInstallModpack(icon: Ref<string | undefined>) {
const { installInstanceFiles } = useService(InstanceInstallServiceKey)
const { createInstance } = useService(InstanceServiceKey)
const { installFile } = useService(CurseForgeServiceKey)
const { fix } = injection(kInstanceVersionDiagnose)
const installModpack = async (f: File) => {
const result = await installFile({ file: f, type: 'modpacks', icon: icon.value })
const resource = result.resource
Expand All @@ -135,7 +137,8 @@ export function useCurseforgeInstallModpack(icon: Ref<string | undefined>) {
await installInstanceFiles({
path,
files,
})
}).catch(() => { })
await fix()
}
return installModpack
}
13 changes: 10 additions & 3 deletions xmcl-keystone-ui/src/composables/instanceCreation.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { useService } from '@/composables'
import { injection } from '@/util/inject'
import { generateBaseName, generateDistinctName } from '@/util/instanceName'
import { Instance, InstanceData, InstanceFile, InstanceInstallServiceKey, InstanceServiceKey, LocalVersionHeader, RuntimeVersions, VersionMetadataServiceKey } from '@xmcl/runtime-api'
import { Instance, InstanceData, InstanceFile, InstanceInstallServiceKey, InstanceServiceKey, LocalVersionHeader, VersionMetadataServiceKey } from '@xmcl/runtime-api'
import type { GameProfile } from '@xmcl/user'
import { InjectionKey, Ref, reactive } from 'vue'
import { kInstanceVersionDiagnose } from './instanceVersionDiagnose'

export const kInstanceCreation: InjectionKey<{
data: InstanceData
Expand All @@ -15,12 +17,13 @@ export const kInstanceCreation: InjectionKey<{
/**
* Hook to create a general instance
*/
export function useInstanceCreation(gameProfile: Ref<GameProfile>, versions: Ref<LocalVersionHeader[]>, instances: Ref<Instance[]>, path: Ref<string>) {
export function useInstanceCreation(gameProfile: Ref<GameProfile>, instances: Ref<Instance[]>, path: Ref<string>) {
const { createInstance: create } = useService(InstanceServiceKey)
const { installInstanceFiles } = useService(InstanceInstallServiceKey)
const { getLatestMinecraftRelease } = useService(VersionMetadataServiceKey)
let latest = ''
getLatestMinecraftRelease().then(v => { latest = v })
const { fix } = injection(kInstanceVersionDiagnose)
const getNewRuntime = () => ({
minecraft: latest || '',
forge: '',
Expand Down Expand Up @@ -61,17 +64,21 @@ export function useInstanceCreation(gameProfile: Ref<GameProfile>, versions: Ref
* Commit this creation. It will create and select the instance.
*/
async create() {
const runtime = { ...data.runtime }
if (!data.name) {
data.name = generateDistinctName(generateBaseName(data.runtime), instances.value.map(i => i.name))
data.name = generateDistinctName(generateBaseName(runtime), instances.value.map(i => i.name))
}
const newPath = await create(data)
if (files.value.length > 0) {
await installInstanceFiles({
path: newPath,
files: files.value,
}).catch((e) => {
console.error(e)
})
}
path.value = newPath
await fix()
return newPath
},
/**
Expand Down
12 changes: 10 additions & 2 deletions xmcl-keystone-ui/src/composables/instanceVersionDiagnose.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ export function useInstanceVersionDiagnose(path: Ref<string>, runtime: Ref<Runti
let operation = undefined as undefined | (() => Promise<void>)
const { t } = useI18n()
const { install } = useInstanceVersionInstall(versions)
const { installAssetsForVersion, installAssets, installLibraries, installDependencies, installByProfile } = useService(InstallServiceKey)
const { installAssetsForVersion, installForge, installAssets, installLibraries, installDependencies, installByProfile } = useService(InstallServiceKey)
const { editInstance } = useService(InstanceServiceKey)
let abortController = new AbortController()
let pendingFix = false

const loading = ref(false)

Expand All @@ -34,6 +35,8 @@ export function useInstanceVersionDiagnose(path: Ref<string>, runtime: Ref<Runti
console.log('update version diagnose')

const abortSignal = abortController.signal
// invalidate operation
operation = () => Promise.resolve()

if ('requirements' in version) {
const runtime = version.requirements
Expand Down Expand Up @@ -152,15 +155,20 @@ export function useInstanceVersionDiagnose(path: Ref<string>, runtime: Ref<Runti
issueItems.value = items
if (ops.length > 0) {
operation = async () => {
pendingFix = false
for (const op of ops) {
await op()
}
await _update(resolvedVersion.value)
}
if (pendingFix) {
operation()
}
}
}

function fix() {
async function fix() {
pendingFix = true
if (operation) {
operation()
}
Expand Down
3 changes: 1 addition & 2 deletions xmcl-keystone-ui/src/views/AppAddInstanceDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -256,10 +256,9 @@ const { t } = useI18n()

// Instance create data
const { gameProfile } = injection(kUserContext)
const { versions } = injection(kLocalVersions)
const { instances } = injection(kInstances)
const { path } = injection(kInstance)
const { create, reset, data: creationData, files } = useInstanceCreation(gameProfile, versions, instances, path)
const { create, reset, data: creationData, files } = useInstanceCreation(gameProfile, instances, path)
const isInvalid = computed(() => {
return creationData.name === '' || creationData.runtime.minecraft === '' || instances.value.some(i => i.name === creationData.name)
})
Expand Down
1 change: 1 addition & 0 deletions xmcl-keystone-ui/src/views/HomeLaunchButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ const showMenu = ref(false)
function onMouseEnter() {
if (handle) clearTimeout(handle)
if (loading.value) return
showMenu.value = true
}
Expand Down
7 changes: 6 additions & 1 deletion xmcl-keystone-ui/src/views/StoreProjectModrinth.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import StoreProjectBase, { StoreProject } from '@/components/StoreProject.vue'
import { StoreProjectVersion } from '@/components/StoreProjectInstallVersionDialog.vue'
import { TeamMember } from '@/components/StoreProjectMembers.vue'
import { useService } from '@/composables'
import { kInstanceVersionDiagnose } from '@/composables/instanceVersionDiagnose'
import { useInstanceVersionInstall } from '@/composables/instanceVersionInstall'
import { kInstances } from '@/composables/instances'
import { useMarkdown } from '@/composables/markdown'
import { useModrinthTags } from '@/composables/modrinth'
Expand All @@ -11,6 +13,7 @@ import { useModrinthVersions } from '@/composables/modrinthVersions'
import { usePresence } from '@/composables/presence'
import { kSWRVConfig } from '@/composables/swrvConfig'
import { useTasks } from '@/composables/task'
import { kLocalVersions } from '@/composables/versionLocal'
import { clientModrinthV2 } from '@/util/clients'
import { injection } from '@/util/inject'
import { generateDistinctName } from '@/util/instanceName'
Expand Down Expand Up @@ -137,6 +140,7 @@ const { getModpackInstallFiles } = useService(ModpackServiceKey)
const { installInstanceFiles } = useService(InstanceInstallServiceKey)
const { createInstance } = useService(InstanceServiceKey)
const { installVersion } = useService(ModrinthServiceKey)
const { fix } = injection(kInstanceVersionDiagnose)
const installModpack = async (v: ProjectVersion) => {
const result = await installVersion({ version: v, icon: project.value?.iconUrl })
const resource = result.resources[0]
Expand All @@ -152,7 +156,8 @@ const installModpack = async (v: ProjectVersion) => {
await installInstanceFiles({
path,
files,
})
}).catch(console.error)
fix()
}
const { isValidating: loadingMembers, error: teamError, data } = useSWRV(computed(() => `/modrinth/team/${props.id}`),
Expand Down
5 changes: 5 additions & 0 deletions xmcl-runtime/install/InstallService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,11 @@ export class InstallService extends AbstractService implements IInstallService {
...this.getForgeInstallOptions(),
}).setName('installForge', { id: version ?? profile.version }))
} catch (err) {
const forgeVersion = profile.version.indexOf('-forge') !== -1 ? profile.version.replace(/-forge/, '') : profile.version
await this.installForge({
version: forgeVersion,
mcversion: profile.minecraft,
})
this.warn(err)
}
}
Expand Down
2 changes: 1 addition & 1 deletion xmcl-runtime/instanceIO/InstanceFileOperationHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ export class InstanceFileOperationHandler {
const urls = file.downloads?.filter(u => u.startsWith('http')) || []
const resource = await this.resourceService.getResourceByHash(sha1)

if (resource) {
if (resource && await this.resourceService.touchResource(resource)) {
if (
(metadata.modrinth && !resource.metadata.modrinth) ||
(metadata.curseforge && resource.metadata.curseforge) ||
Expand Down
14 changes: 12 additions & 2 deletions xmcl-runtime/resource/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { basename, join } from 'path'
import { LauncherApp, LauncherAppKey, PathResolver, kGameDataPath, Inject } from '~/app'
import { ImageStorage } from '~/imageStore'
import { AbstractService, ExposeServiceKey } from '~/service'
import { copyPassively, readdirEnsured } from '~/util/fs'
import { copyPassively, missing, readdirEnsured } from '~/util/fs'
import { PromiseSignal, createPromiseSignal } from '~/util/promiseSignal'
import { ResourceContext } from './core/ResourceContext'
import { createResourceContext } from './core/createResourceContext'
Expand Down Expand Up @@ -195,13 +195,23 @@ export class ResourceService extends AbstractService implements IResourceService
return generateResource(this.getPath(), result[0], result[0])
}

async touchResource(resource: Resource) {
const missed = await missing(resource.storedPath || resource.path)
if (missed) {
// Remove from database
await this.removeResources([resource.hash])
return false
}
return true
}

async removeResources(hashes: Array<string>) {
const existed = await this.context.db.selectFrom('snapshots')
.where('sha1', 'in', hashes)
.select('domainedPath')
.execute()
const resourcePaths = existed.map(r => this.getPath(r.domainedPath))
await Promise.all(resourcePaths.map(p => unlink(p)))
await Promise.all(resourcePaths.map(p => unlink(p).catch(() => {})))
}

async getResourcesByKeyword(keyword: string, domain: ResourceDomain, pagination?: Pagination): Promise<Array<Resource>> {
Expand Down

0 comments on commit c6aa77f

Please sign in to comment.