Skip to content

Commit

Permalink
fix: Fix various corner failures
Browse files Browse the repository at this point in the history
  • Loading branch information
ci010 committed Nov 26, 2024
1 parent 742ce04 commit a8db289
Show file tree
Hide file tree
Showing 11 changed files with 76 additions and 37 deletions.
2 changes: 1 addition & 1 deletion xmcl
1 change: 1 addition & 0 deletions xmcl-electron-app/main/ElectronController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,7 @@ export class ElectronController implements LauncherAppController {
browser.webContents.on('did-create-window', this.onWebContentCreateWindow)
browser.webContents.setWindowOpenHandler(this.windowOpenHandler)
browser.on('closed', () => {
this.mainWin = undefined
this.multiplayerRef?.close()
})

Expand Down
9 changes: 9 additions & 0 deletions xmcl-keystone-ui/src/util/error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export class AnyError extends Error {
constructor(name: string, message?: string, options?: ErrorOptions, properties?: any) {
super(message, options)
this.name = name
if (properties) {
Object.assign(this, properties)
}
}
}
8 changes: 4 additions & 4 deletions xmcl-runtime/app/LauncherApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { Shell } from './Shell'
import { kGameDataPath, kTempDataPath } from './gameDataPath'
import { InjectionKey, ObjectFactory } from './objectRegistry'

export const LauncherAppKey: InjectionKey<LauncherApp> = Symbol('LauncherAppKeyunchAppKey')
export const LauncherAppKey: InjectionKey<LauncherApp> = Symbol('LauncherAppKey')

export interface LauncherApp {
on(channel: 'app-booted', listener: (manifest: InstalledAppManifest) => void): this
Expand Down Expand Up @@ -149,7 +149,7 @@ export class LauncherApp extends EventEmitter {
/**
* The disposers to dispose when the app is going to quit.
*/
#disposers: (() => Promise<void>)[] = []
#disposers: (() => (Promise<void> | void))[] = []

protected logger: Logger = this.getLogger('App')

Expand Down Expand Up @@ -211,12 +211,12 @@ export class LauncherApp extends EventEmitter {
* Reigster the disposer. The disposer will be called when the app is going to quit.
* @param disposer The function to dispose the resource
*/
registryDisposer(disposer: () => Promise<void>) {
registryDisposer(disposer: () => Promise<void> | void) {
this.#disposers.push(disposer)
}

async dispose() {
await Promise.all(this.#disposers.map(m => m().catch(() => { })))
await Promise.allSettled(this.#disposers.map(m => m()))
}

/**
Expand Down
5 changes: 2 additions & 3 deletions xmcl-runtime/app/objectRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export class ObjectFactory {
if (type) {
params[i] = await this.getOrCreate(type)
} else {
throw new AnyError('ObjectRegistryError', `Fail to get [${i}] param type for ${typeof Type === 'symbol' ? Type.toString() : (Type as Function).name} since it's not registered`)
throw new AnyError('ObjectRegistryError', `Fail to get [${i}](${type}) param type for ${typeof Type === 'symbol' ? Type.toString() : (Type as Function).name} since it's not registered`)
}
}
}
Expand All @@ -85,12 +85,11 @@ export class ObjectFactory {
}

type Constructor<T = any> = (new (...args: any[]) => T) | (abstract new (...args: any[]) => T)
type ConstructorParameter<T, X> = T extends (new (...args: infer P) => X) ? P : never
const kParams = Symbol('params')

export interface InjectionKey<T> extends Symbol { }

export function Inject<T, V extends Constructor<T> | InjectionKey<T>>(con: V/* , ...args: V extends Constructor<T> ? ConstructorParameter<V, T> : never[] */) {
export function Inject<T, V extends Constructor<T> | InjectionKey<T>>(con: V) {
return (target: any, _key: any, index: number) => {
if (Reflect.has(target, kParams)) {
Reflect.get(target, kParams)[index] = con
Expand Down
6 changes: 5 additions & 1 deletion xmcl-runtime/install/InstallService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ export class InstallService extends AbstractService implements IInstallService {
@Lock((v) => [LockKey.version(v.minecraftVersion)])
async installLabyModVersion(options: InstallLabyModOptions) {
const location = this.getPath()
const task = installLabyMod4Task(options.manifest, options.minecraftVersion, location, this.getInstallOptions()).setName('installLabyMod', { version: options.manifest.labyModVersion })
const task = installLabyMod4Task(options.manifest, options.minecraftVersion, location, { ...this.getInstallOptions(), fetch: this.app.fetch }).setName('installLabyMod', { version: options.manifest.labyModVersion })
const version = await this.submit(task)
return version
}
Expand Down Expand Up @@ -401,6 +401,10 @@ export class InstallService extends AbstractService implements IInstallService {
const installOptions = this.getForgeInstallOptions()
const side = options.side ?? 'client'

if (!validJavaPaths.length) {
throw new AnyError('ForgeInstallError', 'No valid java found!')
}

validJavaPaths.sort((a, b) => a.majorVersion === 8 ? -1 : b.majorVersion === 8 ? 1 : -1)
const setting = await this.app.registry.get(kSettings)

Expand Down
23 changes: 15 additions & 8 deletions xmcl-runtime/instanceIO/InstanceFileDiscover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,15 +98,17 @@ export async function decorateInstanceFiles(files: [InstanceFile, Stats][],
const filePath = join(instancePath, relativePath)
const ino = stat.ino
if (isSpecialFile(relativePath)) {
const sha1 = sha1Lookup[ino] ?? await worker.checksum(filePath, 'sha1')
sha1Lookup[ino] = sha1
const sha1 = sha1Lookup[ino] || await worker.checksum(filePath, 'sha1')
localFile.hashes.sha1 = sha1
}
}

const metadataLookup = await resourceManager.getMetadataByHashes(Object.values(sha1Lookup)).then(v => {
const exsitedSha1 = files.map(f => f[0].hashes.sha1).filter(isNonnull)

const metadataLookup = await resourceManager.getMetadataByHashes(exsitedSha1).then(v => {
return Object.fromEntries(v.filter(isNonnull).map(m => [m.sha1, m]))
})
const urisLookup = await resourceManager.getUrisByHash(Object.values(sha1Lookup)).then(v => {
const urisLookup = await resourceManager.getUrisByHash(exsitedSha1).then(v => {
return v.reduce((acc, cur) => {
if (!acc[cur.sha1]) {
acc[cur.sha1] = []
Expand All @@ -119,9 +121,8 @@ export async function decorateInstanceFiles(files: [InstanceFile, Stats][],
for (const [localFile, stat] of files) {
const relativePath = localFile.path
const filePath = join(instancePath, relativePath)
const ino = stat.ino
if (isSpecialFile(relativePath)) {
const sha1 = sha1Lookup[ino]
const sha1 = localFile.hashes.sha1
const metadata = metadataLookup[sha1]
if (metadata?.modrinth) {
localFile.modrinth = {
Expand All @@ -138,14 +139,20 @@ export async function decorateInstanceFiles(files: [InstanceFile, Stats][],

const uris = urisLookup[sha1]
localFile.downloads = uris && uris.some(u => u.startsWith('http')) ? uris.filter(u => u.startsWith('http')) : undefined
localFile.hashes = await resolveHashes(filePath, worker, hashes, sha1)
localFile.hashes = {
...localFile.hashes,
...await resolveHashes(filePath, worker, hashes, sha1),
}

// No download url...
if ((!localFile.downloads || localFile.downloads.length === 0) && metadata) {
undecoratedResources.add(localFile)
}
} else {
localFile.hashes = await resolveHashes(filePath, worker)
localFile.hashes = {
...localFile.hashes,
...await resolveHashes(filePath, worker, hashes),
}
}
}
}
1 change: 1 addition & 0 deletions xmcl-runtime/instanceIO/InstanceManifestService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ export class InstanceManifestService extends AbstractService implements IInstanc
}).startAndWait()

const updates = [...pendingResourceUpdates].map((file) => {
if (!file.hashes.sha1) return undefined
return {
hash: file.hashes.sha1,
metadata: {
Expand Down
9 changes: 8 additions & 1 deletion xmcl-runtime/resource/core/watchResourcesDirectory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,14 @@ function createRevalidateFunction(
onResourcePostRevalidate: (files: File[]) => void,
) {
async function getUpserts() {
const entries = await getFiles(dir)
const entries = await getFiles(dir).catch((e) => {
if (isSystemError(e)) {
if (e.code === 'ENOENT') {
return []
}
}
throw e
})
const inos = entries.map(e => e.ino)
const records: Record<string, ResourceSnapshotTable> = await context.db.selectFrom('snapshots')
.selectAll()
Expand Down
20 changes: 12 additions & 8 deletions xmcl-runtime/save/InstanceSavesService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -240,17 +240,21 @@ export class InstanceSavesService extends AbstractService implements IInstanceSa
if (newIsLink !== isLinkedMemo) {
isLinkedMemo = !!newIsLink
tryWatch()
const savePaths = await readdir(savesDir)
const savePaths = await readdir(savesDir).catch(() => [])
const saves = await readAll(savePaths)
state.instanceSaves(saves)
} else if (!newIsLink) {
const savePaths = await readdir(savesDir)
if (savePaths.length !== state.saves.length) {
const toRemove = state.saves.filter((s) => !savePaths.includes(basename(s.path)))
toRemove.forEach((s) => state.instanceSaveRemove(s.path))
const toAdd = savePaths.filter((s) => !state.saves.some((ss) => ss.name === s))
const saves = await readAll(toAdd)
state.instanceSaves(saves)
const savePaths = await readdir(savesDir).catch(() => undefined)
if (!savePaths) {
state.instanceSaves([])
} else {
if (savePaths.length !== state.saves.length) {
const toRemove = state.saves.filter((s) => !savePaths.includes(basename(s.path)))
toRemove.forEach((s) => state.instanceSaveRemove(s.path))
const toAdd = savePaths.filter((s) => !state.saves.some((ss) => ss.name === s))
const saves = await readAll(toAdd)
state.instanceSaves(saves)
}
}
}
})
Expand Down
29 changes: 18 additions & 11 deletions xmcl-runtime/user/OfficialUserService.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,31 @@
import { OfficialUserService as IOfficialUserService, OfficialUserServiceKey, UserProfile } from '@xmcl/runtime-api'
import { MojangChallengeResponse, MojangClient } from '@xmcl/user'
import { LauncherApp, LauncherAppKey, Inject } from '~/app'
import { Inject, LauncherApp, LauncherAppKey } from '~/app'
import { AbstractService, ExposeServiceKey } from '~/service'
import { UserTokenStorage, kUserTokenStorage } from '~/user'
import { kUserTokenStorage } from '~/user'
import { AnyError } from '../util/error'

const UserAuthenticationError = AnyError.make('UserAuthenticationError')

@ExposeServiceKey(OfficialUserServiceKey)
export class OfficialUserService extends AbstractService implements IOfficialUserService {
constructor(@Inject(LauncherAppKey) app: LauncherApp,
@Inject(kUserTokenStorage) private userTokenStorage: UserTokenStorage,
@Inject(MojangClient) private mojangApi: MojangClient) {
super(app)
}

async setName(user: UserProfile, name: string) {
const token = await this.userTokenStorage.get(user)
const userTokenStorage = await this.app.registry.get(kUserTokenStorage)
const token = await userTokenStorage.get(user)
if (!token) {
throw new UserAuthenticationError()
}
await this.mojangApi.setName(name, token)
}

async getNameChangeInformation(user: UserProfile) {
const token = await this.userTokenStorage.get(user)
const userTokenStorage = await this.app.registry.get(kUserTokenStorage)
const token = await userTokenStorage.get(user)
if (!token) {
throw new UserAuthenticationError()
}
Expand All @@ -33,7 +34,8 @@ export class OfficialUserService extends AbstractService implements IOfficialUse
}

async checkNameAvailability(user: UserProfile, name: string) {
const token = await this.userTokenStorage.get(user)
const userTokenStorage = await this.app.registry.get(kUserTokenStorage)
const token = await userTokenStorage.get(user)
if (!token) {
throw new UserAuthenticationError()
}
Expand All @@ -42,39 +44,44 @@ export class OfficialUserService extends AbstractService implements IOfficialUse
}

async hideCape(user: UserProfile) {
const token = await this.userTokenStorage.get(user)
const userTokenStorage = await this.app.registry.get(kUserTokenStorage)
const token = await userTokenStorage.get(user)
if (!token) {
throw new UserAuthenticationError()
}
await this.mojangApi.hideCape(token)
}

async showCape(user: UserProfile, capeId: string) {
const token = await this.userTokenStorage.get(user)
const userTokenStorage = await this.app.registry.get(kUserTokenStorage)
const token = await userTokenStorage.get(user)
if (!token) {
throw new UserAuthenticationError()
}
await this.mojangApi.showCape(capeId, token)
}

async verifySecurityLocation(user: UserProfile) {
const token = await this.userTokenStorage.get(user)
const userTokenStorage = await this.app.registry.get(kUserTokenStorage)
const token = await userTokenStorage.get(user)
if (!token) {
throw new UserAuthenticationError()
}
return await this.mojangApi.verifySecurityLocation(token)
}

async getSecurityChallenges(user: UserProfile) {
const token = await this.userTokenStorage.get(user)
const userTokenStorage = await this.app.registry.get(kUserTokenStorage)
const token = await userTokenStorage.get(user)
if (!token) {
throw new UserAuthenticationError()
}
return await this.mojangApi.getSecurityChallenges(token)
}

async submitSecurityChallenges(user: UserProfile, answers: MojangChallengeResponse[]) {
const token = await this.userTokenStorage.get(user)
const userTokenStorage = await this.app.registry.get(kUserTokenStorage)
const token = await userTokenStorage.get(user)
if (!token) {
throw new UserAuthenticationError()
}
Expand Down

0 comments on commit a8db289

Please sign in to comment.