Skip to content

Commit 734f286

Browse files
committed
add ExtractZipServiceWorker, update vite config to build ExtractZipService to separate file
1 parent 8451368 commit 734f286

File tree

6 files changed

+111
-29
lines changed

6 files changed

+111
-29
lines changed

src/backend/ipcHandlers/mods.ts

-2
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,6 @@ export async function prepareBaseGameForModding({
125125
extractService.on('canceled', () => {
126126
logInfo(`Canceled Extracting of base game file`, LogPrefix.HyperPlay)
127127

128-
process.noAsar = false
129-
130128
cancelQueueExtraction()
131129
callAbortController(appName)
132130

src/backend/services/ExtractZipService.ts

+41-6
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ import { EventEmitter } from 'node:events'
22
import { Readable } from 'node:stream'
33
import { open, ZipFile, Entry } from 'yauzl'
44
import { mkdirSync, createWriteStream, rmSync, existsSync } from 'graceful-fs'
5-
import { captureException } from '@sentry/electron'
65
import { join } from 'path'
6+
import { extractOptions, ExtractZipServiceCommand } from './types'
7+
import { isMainThread, parentPort } from 'node:worker_threads'
78

89
export interface ExtractZipProgressResponse {
910
/** Percentage of extraction progress. */
@@ -22,10 +23,6 @@ enum ExtractionValidation {
2223
VALID = 'VALID'
2324
}
2425

25-
type extractOptions = {
26-
deleteOnEnd?: boolean
27-
}
28-
2926
/**
3027
* Service class to handle extraction of ZIP files.
3128
* @extends {EventEmitter}
@@ -423,7 +420,6 @@ export class ExtractZipService extends EventEmitter {
423420
return await this.#extractionPromise
424421
} catch (error) {
425422
this.#onError(error as Error)
426-
captureException(error)
427423

428424
return false
429425
} finally {
@@ -434,3 +430,42 @@ export class ExtractZipService extends EventEmitter {
434430
}
435431
}
436432
}
433+
434+
let extractZipService: ExtractZipService | undefined = undefined
435+
const eventsToListenTo = ['progress', 'finished', 'error', 'canceled']
436+
437+
if (!isMainThread) {
438+
// disables electron's fs wrapper called when extracting .asar files
439+
// which is necessary to extract electron app/game zip files
440+
process.noAsar = true
441+
parentPort?.on('message', (data) => {
442+
const command: ExtractZipServiceCommand = data.type
443+
switch (command) {
444+
case 'INIT':
445+
extractZipService = new ExtractZipService(
446+
data.zipFile,
447+
data.destinationPath,
448+
data.options
449+
)
450+
for (const evName of eventsToListenTo) {
451+
extractZipService.on(evName, (...args) => {
452+
parentPort?.postMessage({ eventType: evName, args })
453+
})
454+
}
455+
parentPort?.postMessage({ initEvent: 'ZIP_SERVICE_INSTANTIATED' })
456+
break
457+
case 'CANCEL':
458+
extractZipService?.cancel()
459+
break
460+
case 'PAUSE':
461+
extractZipService?.pause()
462+
break
463+
case 'EXTRACT':
464+
extractZipService?.extract()
465+
break
466+
default:
467+
break
468+
}
469+
})
470+
parentPort?.postMessage({ initEvent: 'INITIALIZED' })
471+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { Worker } from 'node:worker_threads'
2+
import { extractOptions } from './types'
3+
import { EventEmitter } from 'node:events'
4+
import path from 'node:path'
5+
6+
export class ExtractZipServiceWorker extends EventEmitter {
7+
worker: Worker
8+
initPromise: Promise<void>
9+
#resolveInitPromise: () => void = () => {
10+
console.error('resolve init promise not assigned!')
11+
}
12+
13+
constructor(
14+
zipFile: string,
15+
destinationPath: string,
16+
options?: extractOptions
17+
) {
18+
super()
19+
this.initPromise = new Promise((res) => {
20+
this.#resolveInitPromise = res
21+
})
22+
const extractZipServicePath = path.join(
23+
__dirname,
24+
'../preload/ExtractZipService.js'
25+
)
26+
this.worker = new Worker(extractZipServicePath)
27+
28+
this.worker.on('message', (data) => {
29+
if (data?.initEvent === 'INITIALIZED') {
30+
this.worker.postMessage({
31+
type: 'INIT',
32+
zipFile,
33+
destinationPath,
34+
options
35+
})
36+
return
37+
} else if (data?.initEvent === 'ZIP_SERVICE_INSTANTIATED') {
38+
this.#resolveInitPromise()
39+
return
40+
}
41+
const { eventType, args } = data
42+
this.emit(eventType, ...args)
43+
})
44+
}
45+
46+
public cancel() {
47+
this.worker.postMessage({ type: 'CANCEL' })
48+
}
49+
50+
public pause(): void {
51+
this.worker.postMessage({ type: 'PAUSE' })
52+
}
53+
54+
async extract() {
55+
this.worker.postMessage({ type: 'EXTRACT' })
56+
}
57+
}

src/backend/services/types.ts

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export type extractOptions = {
2+
deleteOnEnd?: boolean
3+
}
4+
5+
export type ExtractZipServiceCommand = 'INIT' | 'CANCEL' | 'PAUSE' | 'EXTRACT'

src/backend/storeManagers/hyperplay/games.ts

+6-20
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,7 @@ import {
2121
logInfo,
2222
logWarning
2323
} from 'backend/logger/logger'
24-
import {
25-
ExtractZipService,
26-
ExtractZipProgressResponse
27-
} from 'backend/services/ExtractZipService'
24+
import { ExtractZipProgressResponse } from 'backend/services/ExtractZipService'
2825
import {
2926
existsSync,
3027
mkdirSync,
@@ -102,6 +99,7 @@ import { ipfsGateway } from 'backend/vite_constants'
10299
import { GlobalConfig } from 'backend/config'
103100
import { PatchingError } from './types'
104101
import { SiweMessage } from 'siwe'
102+
import { ExtractZipServiceWorker } from 'backend/services/ExtractZipServiceWorker'
105103

106104
interface ProgressDownloadingItem {
107105
DownloadItem: DownloadItem
@@ -114,7 +112,7 @@ interface ProgressDownloadingItem {
114112
}
115113

116114
const inProgressDownloadsMap: Map<string, ProgressDownloadingItem> = new Map()
117-
export const inProgressExtractionsMap: Map<string, ExtractZipService> =
115+
export const inProgressExtractionsMap: Map<string, ExtractZipServiceWorker> =
118116
new Map()
119117

120118
export async function getSettings(appName: string): Promise<GameSettings> {
@@ -764,8 +762,6 @@ export async function cancelExtraction(appName: string) {
764762
)
765763

766764
try {
767-
process.noAsar = false
768-
769765
const extractZipService = inProgressExtractionsMap.get(appName)
770766
if (extractZipService) {
771767
extractZipService.cancel()
@@ -963,8 +959,6 @@ export async function install(
963959
}
964960
return { status: 'done' }
965961
} catch (error) {
966-
process.noAsar = false
967-
968962
logInfo(
969963
`Error while downloading and extracting game: ${error}`,
970964
LogPrefix.HyperPlay
@@ -1052,10 +1046,6 @@ export async function extract(
10521046
const zipFile = path.join(directory, fileName)
10531047
logInfo(`Extracting ${zipFile} to ${destinationPath}`, LogPrefix.HyperPlay)
10541048

1055-
// disables electron's fs wrapper called when extracting .asar files
1056-
// which is necessary to extract electron app/game zip files
1057-
process.noAsar = true
1058-
10591049
sendFrontendMessage('gameStatusUpdate', {
10601050
appName,
10611051
status: 'extracting',
@@ -1078,7 +1068,8 @@ export async function extract(
10781068
}
10791069
})
10801070

1081-
const extractService = new ExtractZipService(zipFile, destinationPath)
1071+
const extractService = new ExtractZipServiceWorker(zipFile, destinationPath)
1072+
await extractService.initPromise
10821073

10831074
inProgressExtractionsMap.set(appName, extractService)
10841075

@@ -1154,8 +1145,6 @@ export async function extract(
11541145
status: 'extracting'
11551146
})
11561147

1157-
process.noAsar = false
1158-
11591148
if (isMac && executable.endsWith('.app')) {
11601149
const macAppExecutable = readdirSync(
11611150
join(executable, 'Contents', 'MacOS')
@@ -1192,6 +1181,7 @@ export async function extract(
11921181
)
11931182
extractService.once('error', (error: Error) => {
11941183
logError(`Extracting Error ${error.message}`, LogPrefix.HyperPlay)
1184+
captureException(error)
11951185

11961186
cancelQueueExtraction()
11971187
callAbortController(appName)
@@ -1210,8 +1200,6 @@ export async function extract(
12101200
LogPrefix.HyperPlay
12111201
)
12121202

1213-
process.noAsar = false
1214-
12151203
cancelQueueExtraction()
12161204
callAbortController(appName)
12171205

@@ -1254,8 +1242,6 @@ export async function extract(
12541242
extractService.extract().then()
12551243
})
12561244
} catch (error: unknown) {
1257-
process.noAsar = false
1258-
12591245
logInfo(`Error while extracting game ${error}`, LogPrefix.HyperPlay)
12601246

12611247
window.webContents.send('gameStatusUpdate', {

vite.config.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ const preloads = [
5454
'src/backend/proxy/providerPreload.ts',
5555
'src/backend/hyperplay_store_preload.ts',
5656
'src/backend/webview_style_preload.ts',
57-
'src/backend/auth_provider_preload.ts'
57+
'src/backend/auth_provider_preload.ts',
58+
'src/backend/services/ExtractZipService.ts'
5859
]
5960

6061
export default defineConfig(({ mode }) => ({

0 commit comments

Comments
 (0)