Skip to content

Commit

Permalink
fix: Resource pack enable and instance user play time
Browse files Browse the repository at this point in the history
  • Loading branch information
ci010 committed Oct 4, 2023
1 parent 2269210 commit 943341a
Show file tree
Hide file tree
Showing 10 changed files with 104 additions and 182 deletions.
2 changes: 1 addition & 1 deletion xmcl
24 changes: 0 additions & 24 deletions xmcl-keystone-ui/src/store/VuexStateAdapter.ts

This file was deleted.

132 changes: 0 additions & 132 deletions xmcl-keystone-ui/src/store/utils.ts

This file was deleted.

4 changes: 2 additions & 2 deletions xmcl-runtime-api/src/services/LaunchService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ interface LaunchServiceEventMap {
minecraft: string
forge: string
fabricLoader: string
}
'minecraft-exit': { pid?: number; code?: number; signal?: string; crashReport?: string; crashReportLocation?: string; errorLog: string }
} & LaunchOptions
'minecraft-exit': LaunchOptions & { pid?: number; code?: number; signal?: string; duration: number; crashReport?: string; crashReportLocation?: string; errorLog: string }
'minecraft-stdout': { pid?: number; stdout: string }
'minecraft-stderr': { pid?: number; stdout: string }
'error': LaunchException
Expand Down
2 changes: 1 addition & 1 deletion xmcl-runtime/lib/managers/ServiceStateManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ export default class ServiceStateManager extends Manager {
return container.state
}
const onDestroy = () => {
while (!this.containers[id].deref()) { /* empty */ }
while (this.containers[id] && !this.containers[id].deref()) { /* empty */ }
delete this.containers[id]
}
return this.register(id, ...await supplier(onDestroy))
Expand Down
4 changes: 4 additions & 0 deletions xmcl-runtime/lib/plugins/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ import { pluginModrinthModpackHandler } from './pluginModrinthModpackHandler'
import { pluginNetworkInterface } from './pluginNetworkInterface'
import { pluginOfficialUserApi } from './pluginOfficialUserApi'
import { pluginOffineUser } from './pluginOfflineUser'
import { pluginResourcePackLink } from './pluginResourcePackLink'
import { pluginServicesHandler } from './pluginServicesHandler'
import { pluginSettings } from './pluginSettings'
import { pluginTelemetry } from './pluginTelemetry'
import { pluginUndiciLogger } from './pluginUndiciLogger'
import { pluginUserPlaytime } from './pluginUserPlaytime'
import { pluginUserTokenStorage } from './pluginUserTokenStorage'
import { pluginWorker } from './pluginWorker'
import { pluginYggdrasilHandler } from './pluginYggdrasilHandler'
Expand All @@ -36,6 +38,8 @@ export const plugins = [
pluginUndiciLogger,
pluginUserTokenStorage,
pluginCurseforgeClient,
pluginResourcePackLink,
pluginUserPlaytime,
pluginWorker,
pluginYggdrasilHandler,
]
48 changes: 48 additions & 0 deletions xmcl-runtime/lib/plugins/pluginResourcePackLink.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { ResourceDomain } from '@xmcl/runtime-api'
import { existsSync } from 'fs'
import { readdir } from 'fs/promises'
import { join } from 'path'
import { LauncherAppPlugin } from '../app/LauncherApp'
import { kGameDataPath } from '../entities/gameDataPath'
import { InstanceOptionsService } from '../services/InstanceOptionsService'
import { InstanceResourcePackService } from '../services/InstanceResourcePacksService'
import { LaunchService } from '../services/LaunchService'
import { linkWithTimeoutOrCopy } from '../util/fs'

export const pluginResourcePackLink: LauncherAppPlugin = async (app) => {
const launchService = await app.registry.get(LaunchService)
const resourcePackService = await app.registry.get(InstanceResourcePackService)
const options = await app.registry.get(InstanceOptionsService)
const getPath = await app.registry.get(kGameDataPath)

launchService.registerPlugin({
async onBeforeLaunch(input, output) {
const path = output.gamePath
const linked = await resourcePackService.link(path)
if (linked) return

const files = await readdir(join(path, ResourceDomain.ResourcePacks))

// if not linked, we need to link the resource pack to the instance
const promises: Promise<any>[] = []
const gameOptions = await options.getGameOptions(path)
const packs = gameOptions.resourcePacks || []
for (let fileName of packs) {
if (fileName === 'vanilla') {
continue
}
fileName = fileName.startsWith('file/') ? fileName.slice(5) : fileName
if (files.includes(fileName)) {
// Skip for existed file
continue
}
const src = getPath(ResourceDomain.ResourcePacks, fileName)
const dest = join(path, ResourceDomain.ResourcePacks, fileName)
if (!existsSync(dest)) {
promises.push(linkWithTimeoutOrCopy(src, dest).catch((e) => resourcePackService.error(e)))
}
}
await Promise.all(promises)
},
})
}
28 changes: 28 additions & 0 deletions xmcl-runtime/lib/plugins/pluginUserPlaytime.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { LauncherAppPlugin } from '../app/LauncherApp'
import { InstanceService } from '../services/InstanceService'
import { LaunchService } from '../services/LaunchService'
import { LaunchService as ILaunchService } from '@xmcl/runtime-api'

export const pluginUserPlaytime: LauncherAppPlugin = async (app) => {
const launchService: ILaunchService = await app.registry.get(LaunchService)
const instanceService = await app.registry.get(InstanceService)

launchService.on('minecraft-start', (options) => {
instanceService.editInstance({
instancePath: options.gameDirectory,
lastPlayedDate: Date.now(),
})
})

launchService.on('minecraft-exit', (options) => {
if (options.gameDirectory) {
const instance = instanceService.state.all[options.gameDirectory]
if (instance) {
instanceService.editInstance({
instancePath: options.gameDirectory,
playtime: instance.playtime + options.duration,
})
}
}
})
}
5 changes: 0 additions & 5 deletions xmcl-runtime/lib/services/InstanceResourcePacksService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,6 @@ export class InstanceResourcePackService extends AbstractService implements IIns
@Inject(InstanceOptionsService) gameSettingService: InstanceOptionsService,
) {
super(app)
this.resourceService.registerInstaller(ResourceDomain.ResourcePacks, async (resource, path) => {
gameSettingService.editGameSetting({
instancePath: path,
})
})
}

async install(instancePath: string, resourcePack: string) {
Expand Down
37 changes: 20 additions & 17 deletions xmcl-runtime/lib/services/LaunchService.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createMinecraftProcessWatcher, diagnoseJar, diagnoseLibraries, launch, LaunchOption, LaunchPrecheck, MinecraftFolder, ResolvedVersion, Version, generateArguments } from '@xmcl/core'
import { createMinecraftProcessWatcher, diagnoseJar, diagnoseLibraries, launch, LaunchOption as ResolvedLaunchOptions, LaunchPrecheck, MinecraftFolder, ResolvedVersion, Version, generateArguments } from '@xmcl/core'
import { AUTHORITY_DEV, LaunchService as ILaunchService, LaunchException, LaunchOptions, LaunchServiceKey } from '@xmcl/runtime-api'
import { ChildProcess } from 'child_process'
import { EOL } from 'os'
Expand All @@ -14,10 +14,16 @@ import { JavaService } from './JavaService'
import { AbstractService, ExposeServiceKey } from './Service'
import { kGameDataPath, PathResolver } from '../entities/gameDataPath'

export interface LaunchPlugin {
onBeforeLaunch(input: LaunchOptions, output: ResolvedLaunchOptions): Promise<void>
}

@ExposeServiceKey(LaunchServiceKey)
export class LaunchService extends AbstractService implements ILaunchService {
private processes: Record<number, ChildProcess> = {}

private plugins: LaunchPlugin[] = []

constructor(@Inject(LauncherAppKey) app: LauncherApp,
@Inject(InstallService) private installService: InstallService,
@Inject(JavaService) private javaService: JavaService,
Expand All @@ -28,6 +34,10 @@ export class LaunchService extends AbstractService implements ILaunchService {
super(app)
}

registerPlugin(plugin: LaunchPlugin) {
this.plugins.push(plugin)
}

getProcesses(): number[] {
return (Object.keys(this.processes).map(v => Number(v)))
}
Expand All @@ -47,7 +57,7 @@ export class LaunchService extends AbstractService implements ILaunchService {
/**
* Build launch condition
*/
const launchOptions: LaunchOption = {
const launchOptions: ResolvedLaunchOptions = {
gameProfile,
accessToken,
properties: {},
Expand Down Expand Up @@ -89,6 +99,7 @@ export class LaunchService extends AbstractService implements ILaunchService {
port: options.server?.port,
}
}

return launchOptions
}

Expand Down Expand Up @@ -186,6 +197,9 @@ export class LaunchService extends AbstractService implements ILaunchService {

const accessToken = user ? await this.userTokenStorage.get(user).catch(() => undefined) : undefined
const launchOptions = this.#generateOptions(options, version, accessToken)
for (const plugin of this.plugins) {
await plugin.onBeforeLaunch(options, launchOptions)
}

try {
const result = await this.javaService.validateJavaPath(javaPath)
Expand All @@ -210,20 +224,15 @@ export class LaunchService extends AbstractService implements ILaunchService {
const process = await launch(launchOptions)
this.processes[process.pid!] = (process)

const watcher = createMinecraftProcessWatcher(process)
const errorLogs = [] as string[]
const startTime = Date.now()
this.emit('minecraft-start', {
pid: process.pid,
minecraft: version.minecraftVersion,
...options,
startTime,
})
const watcher = createMinecraftProcessWatcher(process)
const errorLogs = [] as string[]
const startTime = Date.now()

// TODO: move this to plugin system
// this.instanceService.editInstance({
// instancePath: options.gameDirectory,
// lastPlayedDate: startTime,
// })

const processError = async (buf: Buffer) => {
const encoding = await this.encoder.guessEncodingByBuffer(buf).catch(e => { })
Expand Down Expand Up @@ -253,12 +262,6 @@ export class LaunchService extends AbstractService implements ILaunchService {
const endTime = Date.now()
const playTime = endTime - startTime

// TODO: move this to plugin system
// this.instanceService.editInstance({
// instancePath: options.gameDirectory,
// // playtime: instance.playtime + playTime,
// })

this.log(`Minecraft exit: ${code}, signal: ${signal}`)
if (crashReportLocation) {
crashReportLocation = crashReportLocation.substring(0, crashReportLocation.lastIndexOf('.txt') + 4)
Expand Down

0 comments on commit 943341a

Please sign in to comment.