Skip to content

Commit b5a3de4

Browse files
authored
Merge branch 'main' into fix/rm_noasar_true
2 parents df90e0a + a5d8ccd commit b5a3de4

File tree

5 files changed

+20
-225
lines changed

5 files changed

+20
-225
lines changed

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "hyperplay",
3-
"version": "0.24.0",
3+
"version": "0.23.3",
44
"private": true,
55
"main": "build/main/main.js",
66
"homepage": "./",

src/backend/metrics/types.ts

-25
Original file line numberDiff line numberDiff line change
@@ -228,29 +228,6 @@ export interface PatchingFailed {
228228
sensitiveProperties?: never
229229
}
230230

231-
export interface PatchingAborted {
232-
event: 'Patching Aborted'
233-
properties: {
234-
game_name: string
235-
game_title: string
236-
platform: ReturnType<typeof getPlatformName>
237-
platform_arch: InstallPlatform
238-
}
239-
sensitiveProperties?: never
240-
}
241-
242-
export interface PatchingCleanupFailed {
243-
event: 'Patching Cleanup Failed'
244-
properties: {
245-
game_name: string
246-
error: string
247-
game_title: string
248-
platform?: ReturnType<typeof getPlatformName>
249-
platform_arch?: InstallPlatform
250-
}
251-
sensitiveProperties?: never
252-
}
253-
254231
export interface PatchingTooSlow {
255232
event: 'Patching Too Slow'
256233
properties: {
@@ -498,8 +475,6 @@ export type PossibleMetricPayloads =
498475
| PatchingStarted
499476
| PatchingSuccess
500477
| PatchingFailed
501-
| PatchingAborted
502-
| PatchingCleanupFailed
503478
| PatchingTooSlow
504479
| AccountDropdownPortfolioClicked
505480

src/backend/storeManagers/hyperplay/games.ts

+16-86
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,7 @@ import {
6565
handleArchAndPlatform,
6666
handlePlatformReversed,
6767
runModPatcher,
68-
sanitizeVersion,
69-
safeRemoveDirectory
68+
sanitizeVersion
7069
} from './utils'
7170
import { getSettings as getSettingsSideload } from 'backend/storeManagers/sideload/games'
7271
import {
@@ -424,11 +423,11 @@ const findExecutables = async (folderPath: string): Promise<string[]> => {
424423
return executables
425424
}
426425

427-
export async function cleanUpDownload(appName: string, directory: string) {
426+
export function cleanUpDownload(appName: string, directory: string) {
428427
inProgressDownloadsMap.delete(appName)
429428
inProgressExtractionsMap.delete(appName)
430429
deleteAbortController(appName)
431-
await safeRemoveDirectory(directory)
430+
rmSync(directory, { recursive: true, force: true })
432431
}
433432

434433
function getDownloadUrl(platformInfo: PlatformConfig, appName: string) {
@@ -524,9 +523,9 @@ async function downloadGame(
524523
res()
525524
}
526525

527-
async function onCancel() {
526+
function onCancel() {
528527
try {
529-
await cleanUpDownload(appName, directory)
528+
cleanUpDownload(appName, directory)
530529
} catch (err) {
531530
rej(err)
532531
}
@@ -1172,7 +1171,7 @@ export async function extract(
11721171
body: `Installed`
11731172
})
11741173

1175-
await cleanUpDownload(appName, directory)
1174+
cleanUpDownload(appName, directory)
11761175

11771176
sendFrontendMessage('refreshLibrary', 'hyperplay')
11781177

@@ -1181,21 +1180,21 @@ export async function extract(
11811180
})
11821181
}
11831182
)
1184-
extractService.once('error', async (error: Error) => {
1183+
extractService.once('error', (error: Error) => {
11851184
logError(`Extracting Error ${error.message}`, LogPrefix.HyperPlay)
11861185

11871186
cancelQueueExtraction()
11881187
callAbortController(appName)
11891188

1190-
await cleanUpDownload(appName, directory)
1189+
cleanUpDownload(appName, directory)
11911190

11921191
sendFrontendMessage('refreshLibrary', 'hyperplay')
11931192

11941193
resolve({
11951194
status: 'error'
11961195
})
11971196
})
1198-
extractService.once('canceled', async () => {
1197+
extractService.once('canceled', () => {
11991198
logInfo(
12001199
`Canceled Extracting: Cancellation completed on ${appName} - Destination ${destinationPath}`,
12011200
LogPrefix.HyperPlay
@@ -1231,7 +1230,7 @@ export async function extract(
12311230
body: 'Installation Stopped'
12321231
})
12331232

1234-
await cleanUpDownload(appName, directory)
1233+
cleanUpDownload(appName, directory)
12351234

12361235
sendFrontendMessage('refreshLibrary', 'hyperplay')
12371236

@@ -1901,18 +1900,13 @@ async function applyPatching(
19011900

19021901
if (signal.aborted) {
19031902
logInfo(`Patching ${appName} aborted`, LogPrefix.HyperPlay)
1904-
await safeRemoveDirectory(datastoreDir, {
1905-
sizeThresholdMB: blockSize * totalBlocks
1906-
})
1907-
aborted = true
1903+
rmSync(datastoreDir, { recursive: true })
19081904
return { status: 'abort' }
19091905
}
19101906

1911-
signal.onabort = async () => {
1907+
signal.onabort = () => {
19121908
aborted = true
1913-
await safeRemoveDirectory(datastoreDir, {
1914-
sizeThresholdMB: blockSize * totalBlocks
1915-
})
1909+
rmSync(datastoreDir, { recursive: true })
19161910
return { status: 'abort' }
19171911
}
19181912

@@ -1997,36 +1991,7 @@ async function applyPatching(
19971991
}
19981992
// need this to cover 100% of abort cases
19991993
if (aborted) {
2000-
try {
2001-
await safeRemoveDirectory(datastoreDir, {
2002-
sizeThresholdMB: blockSize * totalBlocks
2003-
})
2004-
} catch (cleanupError) {
2005-
trackEvent({
2006-
event: 'Patching Cleanup Failed',
2007-
properties: {
2008-
error: `${cleanupError}`,
2009-
game_name: gameInfo.app_name,
2010-
game_title: gameInfo.title,
2011-
platform: getPlatformName(platform),
2012-
platform_arch: platform
2013-
}
2014-
})
2015-
2016-
logWarning(
2017-
`Patching aborted and cleanup failed: ${cleanupError}`,
2018-
LogPrefix.HyperPlay
2019-
)
2020-
}
2021-
trackEvent({
2022-
event: 'Patching Aborted',
2023-
properties: {
2024-
game_name: gameInfo.app_name,
2025-
game_title: gameInfo.title,
2026-
platform: getPlatformName(platform),
2027-
platform_arch: platform
2028-
}
2029-
})
1994+
rmSync(datastoreDir, { recursive: true })
20301995
return { status: 'abort' }
20311996
}
20321997

@@ -2041,27 +2006,8 @@ async function applyPatching(
20412006
})
20422007

20432008
logInfo(`Patching ${appName} completed`, LogPrefix.HyperPlay)
2044-
try {
2045-
await safeRemoveDirectory(datastoreDir, {
2046-
sizeThresholdMB: blockSize * totalBlocks
2047-
})
2048-
} catch (cleanupError) {
2049-
trackEvent({
2050-
event: 'Patching Cleanup Failed',
2051-
properties: {
2052-
error: `${cleanupError}`,
2053-
game_name: gameInfo.app_name,
2054-
game_title: gameInfo.title,
2055-
platform: getPlatformName(platform),
2056-
platform_arch: platform
2057-
}
2058-
})
2009+
rmSync(datastoreDir, { recursive: true })
20592010

2060-
logWarning(
2061-
`Patching succeeded but cleanup failed: ${cleanupError}`,
2062-
LogPrefix.HyperPlay
2063-
)
2064-
}
20652011
return { status: 'done' }
20662012
} catch (error) {
20672013
if (error instanceof PatchingError) {
@@ -2101,23 +2047,7 @@ async function applyPatching(
21012047

21022048
// errors can be thrown before datastore dir created. rmSync on nonexistent dir blocks indefinitely
21032049
if (existsSync(datastoreDir)) {
2104-
try {
2105-
await safeRemoveDirectory(datastoreDir)
2106-
} catch (cleanupError) {
2107-
trackEvent({
2108-
event: 'Patching Cleanup Failed',
2109-
properties: {
2110-
error: `${cleanupError}`,
2111-
game_name: gameInfo.app_name,
2112-
game_title: gameInfo.title
2113-
}
2114-
})
2115-
2116-
logWarning(
2117-
`Patching failed and cleanup failed: ${cleanupError}`,
2118-
LogPrefix.HyperPlay
2119-
)
2120-
}
2050+
rmSync(datastoreDir, { recursive: true })
21212051
}
21222052

21232053
return { status: 'error', error: `Error while patching ${error}` }

src/backend/storeManagers/hyperplay/utils.ts

+2-112
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,12 @@ import {
1616
valistListingsApiUrl
1717
} from 'backend/constants'
1818
import { getGameInfo } from './games'
19-
import { LogPrefix, logError, logInfo, logWarning } from 'backend/logger/logger'
19+
import { LogPrefix, logError, logInfo } from 'backend/logger/logger'
2020
import { join } from 'path'
21-
import { existsSync, rmSync } from 'graceful-fs'
21+
import { existsSync } from 'graceful-fs'
2222
import { ProjectMetaInterface } from '@valist/sdk/dist/typesShared'
2323
import getPartitionCookies from 'backend/utils/get_partition_cookies'
2424
import { DEV_PORTAL_URL } from 'common/constants'
25-
import { captureException } from '@sentry/electron'
2625

2726
export async function getHyperPlayStoreRelease(
2827
appName: string
@@ -420,112 +419,3 @@ export const runModPatcher = async (appName: string) => {
420419
throw new Error(`Error running patcher: ${error}`)
421420
}
422421
}
423-
424-
type SafeRemoveDirectoryOptions = {
425-
maxRetries?: number
426-
retryDelay?: number
427-
sizeThresholdMB?: number
428-
}
429-
430-
/**
431-
* Safely removes a directory with retry logic to handle Windows EPERM issues
432-
* @param directory Path to the directory to remove
433-
* @param options Optional configuration for removal
434-
* @param options.maxRetries Maximum number of removal attempts before giving up (default: 5)
435-
* @param options.retryDelay Delay in milliseconds between removal attempts (default: 10000)
436-
* @param options.sizeThresholdMB Threshold in MB above which async removal is used (default: 500)
437-
* @returns True if directory was successfully removed or doesn't exist, false otherwise
438-
* @warning This function MUST always be awaited to prevent race conditions
439-
*/
440-
export async function safeRemoveDirectory(
441-
directory: string,
442-
{
443-
maxRetries = 10,
444-
retryDelay = 6000,
445-
sizeThresholdMB = 500
446-
}: SafeRemoveDirectoryOptions = {}
447-
): Promise<boolean> {
448-
if (!existsSync(directory)) {
449-
return true // Directory doesn't exist, nothing to remove
450-
}
451-
452-
// Log start of removal process
453-
logInfo(`Starting removal of directory ${directory}`, LogPrefix.HyperPlay)
454-
455-
// Import fs promises for async operations only when needed
456-
const fsPromises = await import('fs/promises')
457-
458-
for (let attempt = 1; attempt <= maxRetries; attempt++) {
459-
try {
460-
// Use different removal strategies based on expected size
461-
// For directories larger than threshold, use async removal to not block the main thread
462-
if (sizeThresholdMB > 250) {
463-
try {
464-
await fsPromises.rm(directory, {
465-
recursive: true,
466-
force: true,
467-
maxRetries: 3
468-
})
469-
} catch (asyncError) {
470-
// Fallback to sync if async fails
471-
logWarning(
472-
`Async removal failed, falling back to sync removal: ${asyncError}`,
473-
LogPrefix.HyperPlay
474-
)
475-
rmSync(directory, { recursive: true, force: true, maxRetries: 3 })
476-
}
477-
} else {
478-
// For smaller directories, use sync removal
479-
rmSync(directory, { recursive: true, force: true, maxRetries: 3 })
480-
}
481-
482-
// Verify directory was actually removed
483-
try {
484-
await fsPromises.access(directory)
485-
// If we get here, directory still exists
486-
logWarning(
487-
`Failed to remove directory ${directory} on attempt ${attempt}/${maxRetries}, directory still exists`,
488-
LogPrefix.HyperPlay
489-
)
490-
captureException(
491-
new Error(`Failed to remove directory, directory still exists`),
492-
{
493-
extra: {
494-
directory,
495-
attempts: attempt,
496-
method: 'safeRemoveDirectory'
497-
}
498-
}
499-
)
500-
} catch {
501-
// Directory doesn't exist (access threw an error), removal succeeded
502-
logInfo(
503-
`Successfully removed directory ${directory}`,
504-
LogPrefix.HyperPlay
505-
)
506-
return true
507-
}
508-
} catch (error) {
509-
logWarning(
510-
`Error removing directory ${directory} on attempt ${attempt}/${maxRetries}: ${error}`,
511-
LogPrefix.HyperPlay
512-
)
513-
}
514-
515-
// Use exponential backoff for retries (increases wait time with each attempt)
516-
if (attempt < maxRetries) {
517-
const backoffDelay = retryDelay * Math.pow(1.5, attempt - 1)
518-
logInfo(
519-
`Waiting ${backoffDelay}ms before retry ${attempt + 1}/${maxRetries}`,
520-
LogPrefix.HyperPlay
521-
)
522-
await new Promise((resolve) => setTimeout(resolve, backoffDelay))
523-
}
524-
}
525-
526-
logError(
527-
`Failed to remove directory ${directory} after ${maxRetries} attempts`,
528-
LogPrefix.HyperPlay
529-
)
530-
return false
531-
}

src/backend/utils.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -983,7 +983,7 @@ export async function downloadFile(
983983
abortController: AbortController,
984984
progressCallback?: ProgressCallback,
985985
onCompleted?: (file: File) => void,
986-
onCancel?: (item: DownloadItem) => Promise<void>
986+
onCancel?: (item: DownloadItem) => void
987987
): Promise<DownloadItem> {
988988
let lastProgressUpdateTime = Date.now()
989989
let lastBytesWritten = 0

0 commit comments

Comments
 (0)