Skip to content

Commit

Permalink
fix: Encoding detecting wrong on error log
Browse files Browse the repository at this point in the history
  • Loading branch information
ci010 committed Jun 26, 2023
1 parent a0ff29c commit a1e84d2
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 55 deletions.
18 changes: 10 additions & 8 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions xmcl-electron-app/esbuild.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import pluginNode from './plugins/esbuild.node.plugin'
import plugin7Zip from './plugins/esbuild.native.plugin'
import pluginPrint from './plugins/esbuild.print.plugin'
import pluginStatic from './plugins/esbuild.static.plugin'
import pluginJsdetect from './plugins/esbuild.jschardet.plugin'
import { yamlPlugin } from 'esbuild-plugin-yaml'
import { BuildOptions } from 'esbuild'
import 'dotenv/config'
Expand Down Expand Up @@ -49,6 +50,7 @@ const config = {
pluginPreload(path.resolve(__dirname, './preload')),
pluginVueDevtools(path.resolve(__dirname, '../extensions')),
pluginWorker(),
pluginJsdetect(),
plugin7Zip(path.resolve(__dirname, './node_modules')),
pluginNode(),
yamlPlugin({}) as any,
Expand Down
22 changes: 22 additions & 0 deletions xmcl-electron-app/plugins/esbuild.jschardet.plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Plugin } from 'esbuild'
import { readFile } from 'fs/promises'

/**
* @returns {import('esbuild').Plugin}
*/
export default function createJschardetPlugin(): Plugin {
return {
name: 'jschardet-import',
setup(build) {
if (build.initialOptions) {
build.onLoad({ filter: /universaldetectors\.js/g }, async ({ path }) => {
const content = await readFile(path, 'utf-8')
return {
contents: content.replace('denormalizedEncodings = [];', 'const denormalizedEncodings = [];'),
loader: 'js',
}
})
}
},
}
}
42 changes: 21 additions & 21 deletions xmcl-keystone-ui/src/views/AppGameExitDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,30 @@
:width="750"
:persistent="true"
>
<v-toolbar color="error">
<v-toolbar-title
class="white--text"
>
{{ data.isCrash ? t('launchFailed.crash') : t('launchFailed.title') }}
</v-toolbar-title>
<v-spacer />
<v-toolbar-items>
<v-card class="overflow-auto visible-scroll flex flex-col max-h-[80vh]">
<v-toolbar color="error">
<v-toolbar-title
class="white--text"
>
{{ data.isCrash ? t('launchFailed.crash') : t('launchFailed.title') }}
</v-toolbar-title>
<v-spacer />
<v-toolbar-items>
<v-btn
text
@click="openFolder"
>
{{ data.isCrash ? t('instance.openCrashReportFolder') : t('instance.openLogFolder') }}
</v-btn>
</v-toolbar-items>
<v-btn
text
@click="openFolder"
icon
@click="data.isShown=false"
>
{{ data.isCrash ? t('instance.openCrashReportFolder') : t('instance.openLogFolder') }}
<v-icon>arrow_drop_down</v-icon>
</v-btn>
</v-toolbar-items>
<v-btn
icon
@click="data.isShown=false"
>
<v-icon>arrow_drop_down</v-icon>
</v-btn>
</v-toolbar>
<v-card class="overflow-auto max-h-200 visible-scroll">
<v-card-text>
</v-toolbar>
<v-card-text class="overflow-auto flex flex-col">
<div
style="padding: 10px"
>
Expand Down
56 changes: 35 additions & 21 deletions xmcl-runtime/lib/services/LaunchService.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,26 @@
import { createMinecraftProcessWatcher, diagnoseJar, diagnoseLibraries, generateArguments, launch, LaunchOption, LaunchPrecheck, MinecraftFolder, ResolvedVersion, Version } from '@xmcl/core'
import { LaunchException, LaunchOptions, LaunchService as ILaunchService, LaunchServiceKey, LaunchState } from '@xmcl/runtime-api'
import { LaunchService as ILaunchService, LaunchException, LaunchOptions, LaunchServiceKey, LaunchState } from '@xmcl/runtime-api'
import { ChildProcess } from 'child_process'
import { EOL } from 'os'
import LauncherApp from '../app/LauncherApp'
import { LauncherAppKey } from '../app/utils'
import { EncodingWorker, kEncodingWorker } from '../entities/encodingWorker'
import { JavaValidation } from '../entities/java'
import { kUserTokenStorage, UserTokenStorage } from '../entities/userTokenStore'
import { UTF8 } from '../util/encoding'
import { Inject } from '../util/objectRegistry'
import { BaseService } from './BaseService'
import { DiagnoseService } from './DiagnoseService'
import { ExternalAuthSkinService } from './ExternalAuthSkinService'
import { InstallService } from './InstallService'
import { InstanceJavaService } from './InstanceJavaService'
import { InstanceResourcePackService } from './InstanceResourcePacksService'
import { InstanceService } from './InstanceService'
import { InstanceShaderPacksService } from './InstanceShaderPacksService'
import { InstanceVersionService } from './InstanceVersionService'
import { JavaService } from './JavaService'
import { ExposeServiceKey, StatefulService } from './Service'
import { UserService } from './UserService'
import { InstanceResourcePackService } from './InstanceResourcePacksService'
import { InstanceShaderPacksService } from './InstanceShaderPacksService'

@ExposeServiceKey(LaunchServiceKey)
export class LaunchService extends StatefulService<LaunchState> implements ILaunchService {
Expand All @@ -36,6 +38,7 @@ export class LaunchService extends StatefulService<LaunchState> implements ILaun
@Inject(InstanceShaderPacksService) private instanceShaderPacksService: InstanceShaderPacksService,
@Inject(JavaService) private javaService: JavaService,
@Inject(kUserTokenStorage) private userTokenStorage: UserTokenStorage,
@Inject(kEncodingWorker) private encoder: EncodingWorker,
@Inject(UserService) private userService: UserService,
) {
super(app, () => new LaunchState())
Expand Down Expand Up @@ -330,10 +333,28 @@ export class LaunchService extends StatefulService<LaunchState> implements ILaun
lastPlayedDate: startTime,
})

process.stderr?.on('data', (buf: any) => {
const lines = buf.toString().split(EOL)
const processError = async (buf: Buffer) => {
const encoding = await this.encoder.guessEncodingByBuffer(buf).catch(e => { })
const result = await this.encoder.decode(buf, encoding || UTF8)
this.emit('minecraft-stderr', { pid: process.pid, stderr: result })
const lines = result.split(EOL)
errorLogs.push(...lines)
this.warn(result)
}
const processLog = async (buf: any) => {
const encoding = await this.encoder.guessEncodingByBuffer(buf).catch(e => undefined)
const result = await this.encoder.decode(buf, encoding || UTF8)
this.emit('minecraft-stdout', { pid: process.pid, stdout: result })
}

const errPromises = [] as Promise<any>[]
process.stderr?.on('data', async (buf: any) => {
errPromises.push(processError(buf))
})
process.stdout?.on('data', (s) => {
processLog(s).catch(this.error)
})

watcher.on('error', (err) => {
this.emit('error', new LaunchException({ type: 'launchGeneralException', error: err }))
}).on('minecraft-exit', ({ code, signal, crashReport, crashReportLocation }) => {
Expand All @@ -349,27 +370,20 @@ export class LaunchService extends StatefulService<LaunchState> implements ILaun
if (crashReportLocation) {
crashReportLocation = crashReportLocation.substring(0, crashReportLocation.lastIndexOf('.txt') + 4)
}
this.emit('minecraft-exit', {
pid: process.pid,
code,
signal,
crashReport,
crashReportLocation: crashReportLocation ? crashReportLocation.replace('\r\n', '').trim() : '',
errorLog: errorLogs.join('\n'),
Promise.all(errPromises).catch((e) => { this.error(e) }).finally(() => {
this.emit('minecraft-exit', {
pid: process.pid,
code,
signal,
crashReport,
crashReportLocation: crashReportLocation ? crashReportLocation.replace('\r\n', '').trim() : '',
errorLog: errorLogs.join('\n'),
})
})
this.launchedProcesses = this.launchedProcesses.filter(p => p !== process)
}).on('minecraft-window-ready', () => {
this.emit('minecraft-window-ready', { pid: process.pid })
})
/* eslint-disable no-unused-expressions */
process.stdout?.on('data', (s) => {
const string = s.toString()
this.emit('minecraft-stdout', { pid: process.pid, stdout: string })
})
process.stderr?.on('data', (s: Buffer) => {
this.warn(s.toString('utf8'))
this.emit('minecraft-stderr', { pid: process.pid, stderr: s.toString() })
})
process.unref()
this.state.launchStatus('idle')

Expand Down
5 changes: 2 additions & 3 deletions xmcl-runtime/lib/workers/encodingWorkerEntry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { EncodingWorker } from '../entities/encodingWorker'
import { setHandler } from './helper'
import * as iconv from 'iconv-lite'
import 'source-map-support/register'
import { detect } from 'jschardet'

const AUTO_ENCODING_GUESS_MAX_BYTES = 512 * 128 // set an upper limit for the number of bytes we pass on to jschardet

Expand All @@ -11,9 +12,7 @@ const handler: EncodingWorker = {
return iconv.decode(buffer, toNodeEncoding(encoding))
},
async guessEncodingByBuffer (buffer: Buffer): Promise<string | null> {
const jschardet = await import('jschardet')

const guessed = jschardet.detect(buffer.slice(0, AUTO_ENCODING_GUESS_MAX_BYTES)) // ensure to limit buffer for guessing due to https://github.com/aadsm/jschardet/issues/53
const guessed = detect(Buffer.from(buffer).subarray(0, AUTO_ENCODING_GUESS_MAX_BYTES)) // ensure to limit buffer for guessing due to https://github.com/aadsm/jschardet/issues/53
if (!guessed || !guessed.encoding) {
return null
}
Expand Down
4 changes: 2 additions & 2 deletions xmcl-runtime/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"@xmcl/core": "workspace:*",
"@xmcl/curseforge": "workspace:*",
"@xmcl/discord-rpc": "workspace:*",
"@xmcl/file-transfer": "workspace:*",
"@xmcl/forge-site-parser": "workspace:*",
"@xmcl/game-data": "workspace:*",
"@xmcl/gamesetting": "workspace:*",
Expand All @@ -36,7 +37,6 @@
"@xmcl/nbt": "workspace:*",
"@xmcl/resourcepack": "workspace:*",
"@xmcl/runtime-api": "workspace:*",
"@xmcl/file-transfer": "workspace:*",
"@xmcl/stun-client": "^1.0.0",
"@xmcl/system": "workspace:*",
"@xmcl/task": "workspace:*",
Expand All @@ -56,7 +56,7 @@
"graceful-fs": "^4.2.10",
"http-cache-semantics": "^4.1.0",
"iconv-lite": "^0.6.3",
"jschardet": "^2.3.0",
"jschardet": "github:aadsm/jschardet",
"lodash.debounce": "^4.0.8",
"lodash.throttle": "^4.1.1",
"node-datachannel": "0.4.1",
Expand Down

0 comments on commit a1e84d2

Please sign in to comment.