Skip to content

Commit

Permalink
fix: Should not mount to the unexisted instances
Browse files Browse the repository at this point in the history
  • Loading branch information
ci010 committed Dec 29, 2023
1 parent ddbf131 commit 763c837
Show file tree
Hide file tree
Showing 11 changed files with 89 additions and 36 deletions.
2 changes: 1 addition & 1 deletion xmcl-keystone-ui/src/composables/instanceJava.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export const kInstanceJava: InjectionKey<ReturnType<typeof useInstanceJava>> = S
export function useInstanceJava(instance: Ref<Instance>, version: Ref<InstanceResolveVersion | undefined>, all: Ref<JavaRecord[]>) {
const { resolveJava } = useService(JavaServiceKey)

const { data, mutate, isValidating, error } = useSWRV(() => instance.value.path && `/instance/${instance.value.path}/java-version?version=${version.value && 'id' in version.value ? version.value.id : undefined}`, async () => {
const { data, mutate, isValidating, error } = useSWRV(computed(() => instance.value.path && `/instance/${instance.value.path}/java-version?version=${version.value && 'id' in version.value ? version.value.id : undefined}`), async () => {
return await computeJava(all.value, resolveJava, instance.value, version.value)
}, { revalidateOnFocus: false })

Expand Down
2 changes: 1 addition & 1 deletion xmcl-keystone-ui/src/composables/instanceLaunch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ export function useInstanceLaunch(instance: Ref<Instance>, resolvedVersion: Ref<

const options: LaunchOptions = {
operationId: id,
version: instance.value.version || ver.id,
version: ver.id,
gameDirectory: instance.value.path,
user: userProfile.value,
java: javaRec.path,
Expand Down
6 changes: 3 additions & 3 deletions xmcl-keystone-ui/src/composables/instanceOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@ import { useState } from './syncableState'

export const kInstanceOptions: InjectionKey<ReturnType<typeof useInstanceOptions>> = Symbol('InstanceOptions')

export function useInstanceOptions(instance: Ref<Instance>) {
export function useInstanceOptions(instancePath: Ref<string>) {
const { editGameSetting, watch: watchOptions } = useService(InstanceOptionsServiceKey)
const { state, isValidating, error } = useState(() => instance.value.path ? watchOptions(instance.value.path) : undefined, GameOptionsState)
const { state, isValidating, error } = useState(() => instancePath.value ? watchOptions(instancePath.value) : undefined, GameOptionsState)
const { locale } = useI18n()

watch(state, (newOps) => {
if (newOps) {
if (newOps.lang === '') {
editGameSetting({
instancePath: instance.value.path,
instancePath: instancePath.value,
lang: locale.value.toLowerCase().replace('-', '_'),
resourcePacks: newOps.resourcePacks,
})
Expand Down
32 changes: 22 additions & 10 deletions xmcl-keystone-ui/src/composables/instanceVersion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,19 +30,31 @@ export function isResolvedVersion(v?: InstanceResolveVersion): v is ResolvedVers

export function useInstanceVersion(instance: Ref<Instance>, local: Ref<LocalVersionHeader[]>) {
const { resolveLocalVersion } = useService(VersionServiceKey)
const versionHeader = computed(() => getResolvedVersion(local.value,
instance.value.version,
instance.value.runtime.minecraft,
instance.value.runtime.forge,
instance.value.runtime.neoForged,
instance.value.runtime.fabricLoader,
instance.value.runtime.optifine,
instance.value.runtime.quiltLoader,
instance.value.runtime.labyMod) || { ...EMPTY_VERSION, id: getExpectVersion(instance.value.runtime) })
const folder = computed(() => versionHeader.value?.id || 'unknown')
const versionHeader = computed(() => {
let result: LocalVersionHeader | undefined
if (instance.value.path) {
result = getResolvedVersion(local.value,
instance.value.version,
instance.value.runtime.minecraft,
instance.value.runtime.forge,
instance.value.runtime.neoForged,
instance.value.runtime.fabricLoader,
instance.value.runtime.optifine,
instance.value.runtime.quiltLoader,
instance.value.runtime.labyMod)
}
if (!result) {
result = { ...EMPTY_VERSION, id: getExpectVersion(instance.value.runtime) }
}
return result
})
const folder = computed(() => versionHeader.value.id)

const { isValidating, mutate, data: resolvedVersion, error } = useSWRV(() => instance.value.path && `/instance/${instance.value.path}/version`, async () => {
console.log('update instance version')
if (!instance.value.path) {
return undefined
}
if (!versionHeader.value.path) {
return { requirements: { ...instance.value.runtime } }
}
Expand Down
55 changes: 41 additions & 14 deletions xmcl-keystone-ui/src/composables/instances.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ export const kInstances: InjectionKey<ReturnType<typeof useInstances>> = Symbol(
* Hook of a view of all instances & some deletion/selection functions
*/
export function useInstances() {
const path = useLocalStorageCacheStringValue('selectedInstancePath', '' as string)
const { createInstance, getSharedInstancesState, editInstance, deleteInstance } = useService(InstanceServiceKey)
const { createInstance, getSharedInstancesState, editInstance, deleteInstance, validateInstancePath } = useService(InstanceServiceKey)
const { state, isValidating, error } = useState(getSharedInstancesState, class extends InstanceState {
override instanceEdit(settings: DeepPartial<InstanceSchema> & { path: string }) {
const inst = this.instances.find(i => i.path === (settings.path))!
Expand Down Expand Up @@ -46,6 +45,8 @@ export function useInstances() {
})
const _instances = computed(() => state.value?.instances ?? [])
const { instances, setToPrevious } = useSortedInstance(_instances)
const _path = useLocalStorageCacheStringValue('selectedInstancePath', '' as string)
const path = ref('')

async function edit(options: EditInstanceOptions & { instancePath: string }) {
await editInstance(options)
Expand All @@ -65,21 +66,47 @@ export function useInstances() {
}
}
}
watch(state, async () => {
let firstInstancePath = instances.value[0]?.path ?? ''
if (!firstInstancePath) {
firstInstancePath = await createInstance({
name: 'Minecraft',
})
path.value = firstInstancePath
}
const existed = instances.value.find(i => i.path === path.value)
if (!path.value || !existed) {
// Select the first instance
path.value = firstInstancePath
watch(state, async (newVal, oldVal) => {
if (!newVal) return
debugger
if (!oldVal) {
// initialize
const instances = [...newVal.instances]
const lastSelectedPath = _path.value

const selectDefault = async () => {
// Select the first instance
let defaultPath = instances[0]?.path as string | undefined
if (!defaultPath) {
// Create a default instance
defaultPath = await createInstance({
name: 'Minecraft',
})
}
_path.value = defaultPath
}

if (lastSelectedPath) {
// Validate the last selected path
if (!instances.some(i => i.path === lastSelectedPath)) {
const badInstance = await validateInstancePath(lastSelectedPath)
if (badInstance) {
await selectDefault()
}
}
} else {
// No selected, try to select the first instance
await selectDefault()
}

path.value = _path.value
}
})
watch(path, (newPath) => {
if (newPath !== _path.value) {
// save to local storage
_path.value = newPath
}
editInstance({
instancePath: newPath,
lastAccessDate: Date.now(),
Expand Down
1 change: 1 addition & 0 deletions xmcl-keystone-ui/src/composables/launchTask.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export const kLaunchTask: InjectionKey<ReturnType<typeof useLaunchTask>> = Symbo

export function useLaunchTask(path: Ref<string>, version: Ref<Instance['runtime']>, localVersion: Ref<LocalVersionHeader>) {
return useTask((i) => {
if (!path.value) return false
const p = i.param as any
if (i.state === TaskState.Cancelled || i.state === TaskState.Succeed || i.state === TaskState.Failed) {
return false
Expand Down
4 changes: 2 additions & 2 deletions xmcl-keystone-ui/src/composables/save.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import { useState } from './syncableState'

export const kInstanceSave: InjectionKey<ReturnType<typeof useInstanceSaves>> = Symbol('InstanceSave')

export function useInstanceSaves(instance: Ref<Instance>) {
export function useInstanceSaves(instancePath: Ref<string>) {
const { watch } = useService(InstanceSavesServiceKey)
const { state, isValidating, error } = useState(() => instance.value.path ? watch(instance.value.path) : undefined, Saves)
const { state, isValidating, error } = useState(() => instancePath.value ? watch(instancePath.value) : undefined, Saves)

const saves = computed(() => state.value?.saves || [])

Expand Down
4 changes: 2 additions & 2 deletions xmcl-keystone-ui/src/windows/main/Context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ export default defineComponent({
const instanceVersion = useInstanceVersion(instance.instance, localVersions.versions)
const instanceJava = useInstanceJava(instance.instance, instanceVersion.resolvedVersion, java.all)
const instanceDefaultSource = useInstanceDefaultSource(instance.path)
const options = useInstanceOptions(instance.instance)
const saves = useInstanceSaves(instance.instance)
const options = useInstanceOptions(instance.path)
const saves = useInstanceSaves(instance.path)
const resourcePacks = useInstanceResourcePacks(instance.path, options.gameOptions)
const instanceMods = useInstanceMods(instance.path, instance.runtime, instanceJava.java)
const shaderPacks = useInstanceShaderPacks(instance.path, instance.runtime, instanceMods.mods, options.gameOptions)
Expand Down
2 changes: 2 additions & 0 deletions xmcl-runtime-api/src/services/InstanceService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,8 @@ export interface InstanceService {
* @returns The instance path
*/
acquireInstanceById(id: string): Promise<string>

validateInstancePath(path: string): Promise<'bad' | 'nondictionary' | 'noperm' | 'exists' | undefined>
}

export const InstanceServiceKey: ServiceKey<InstanceService> = 'InstanceService'
Expand Down
11 changes: 11 additions & 0 deletions xmcl-runtime/instance/InstanceService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -661,4 +661,15 @@ export class InstanceService extends StatefulService<InstanceState> implements I
this.log(`Create new instance ${id} -> ${instancePath}`)
return path
}

async validateInstancePath(path: string) {
const err = await validateDirectory(this.app.platform, path)
if (err && err !== 'exists') {
return err
}
if (this.state.all[path]) {
return undefined
}
return await this.loadInstance(path).catch(() => 'bad') ? undefined : 'bad'
}
}
6 changes: 3 additions & 3 deletions xmcl-runtime/util/validate.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Platform } from '@xmcl/runtime-api'
import { randomBytes } from 'crypto'
import { mkdir, readdir, stat, unlink, writeFile } from 'fs/promises'
import { mkdir, readdir, rmdir, stat, unlink, writeFile } from 'fs/promises'
import { join } from 'path'
import { isSystemError } from '../util/error'
import { Platform } from '@xmcl/runtime-api'

export async function validateDirectory(platform: Platform, path: string) {
// Check if the path is the root of the drive
Expand Down Expand Up @@ -38,7 +38,7 @@ export async function validateDirectory(platform: Platform, path: string) {
// Check if we have permission to create the directory
try {
await mkdir(path, { recursive: true })
await unlink(path)
await rmdir(path)
} catch (e) {
if (isSystemError(e)) {
if (e.code === 'EACCES') {
Expand Down

0 comments on commit 763c837

Please sign in to comment.