-
Notifications
You must be signed in to change notification settings - Fork 31
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Feat] Add patching speed check #1148
Changes from 6 commits
42db804
97d41b6
dfc2dc7
a05a8dd
9f46923
943153a
d2f4067
b062c25
63f2039
b794055
d738dfc
b92e32a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -100,6 +100,7 @@ import { trackEvent } from 'backend/metrics/metrics' | |
import { getFlag } from 'backend/flags/flags' | ||
import { ipfsGateway } from 'backend/vite_constants' | ||
import { GlobalConfig } from 'backend/config' | ||
import { PatchingError } from './types' | ||
|
||
interface ProgressDownloadingItem { | ||
DownloadItem: DownloadItem | ||
|
@@ -1653,6 +1654,82 @@ export async function downloadGameIpdtManifest( | |
} | ||
} | ||
|
||
async function checkIfPatchingIsFaster( | ||
oldManifestPath: string, | ||
newManifestPath: string, | ||
gameInfo: GameInfo | ||
) { | ||
// read manifests | ||
const oldManifestJson = JSON.parse(readFileSync(oldManifestPath).toString()) | ||
const newManifestJson = JSON.parse(readFileSync(newManifestPath).toString()) | ||
|
||
// compare manifests | ||
|
||
const { compareManifests } = await import('@hyperplay/patcher') | ||
const { estimatedPatchSizeInKB } = compareManifests( | ||
oldManifestJson, | ||
newManifestJson | ||
) | ||
|
||
// calc break point % where patching is faster | ||
if ( | ||
gameInfo?.install?.platform && | ||
gameInfo.channels && | ||
gameInfo?.install?.channelName && | ||
Object.hasOwn(gameInfo.channels, gameInfo.install.channelName) | ||
) { | ||
const channelName = gameInfo.install.channelName | ||
const [releaseMeta] = getReleaseMeta(gameInfo, channelName) | ||
const platform = handleArchAndPlatform( | ||
gameInfo.install.platform, | ||
releaseMeta | ||
) | ||
const downloadSize = parseInt( | ||
releaseMeta.platforms[platform]?.downloadSize ?? '0' | ||
) | ||
const installSize = parseInt( | ||
releaseMeta.platforms[platform]?.installSize ?? '0' | ||
) | ||
// @TODO: get these speed values from local checks of download/write speed | ||
const downloadSpeedInKBPerSecond = 25 * 1024 | ||
const extractionSpeedInKBPerSecond = 50 * 1024 | ||
const estTimeToInstallFullGameInSec = | ||
(downloadSize / 1024) * downloadSpeedInKBPerSecond + | ||
(installSize / 1024) * extractionSpeedInKBPerSecond | ||
|
||
// @TODO: get this value from local check of patching speed | ||
const patchingSpeedEstimateInKBPerSecond = 5 * 1024 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. open to suggestions here as well. perhaps this could be the fallback value and we could attempt to get it from a LD flag? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe add an object with these amounts and we can import them from LD. |
||
const estTimeToPatchGameInSec = | ||
estimatedPatchSizeInKB / patchingSpeedEstimateInKBPerSecond | ||
|
||
if (estTimeToPatchGameInSec > estTimeToInstallFullGameInSec) { | ||
const abortMessage = `Downloading full game instead of patching. \n | ||
Estimated time to install full game: ${estTimeToInstallFullGameInSec} seconds. \n | ||
Estimated time to patch: ${estTimeToPatchGameInSec} | ||
` | ||
logInfo(abortMessage, LogPrefix.HyperPlay) | ||
const patchingError = new PatchingError( | ||
abortMessage, | ||
'slower-than-install', | ||
{ | ||
event: 'Patching Too Slow', | ||
properties: { | ||
game_name: gameInfo.app_name, | ||
game_title: gameInfo.title, | ||
platform: getPlatformName(platform), | ||
platform_arch: platform, | ||
est_time_to_install_sec: estTimeToInstallFullGameInSec.toString(), | ||
est_time_to_patch_sec: estTimeToPatchGameInSec.toString(), | ||
old_game_version: gameInfo.install.version ?? 'unknown', | ||
new_game_version: gameInfo.version ?? 'unknown' | ||
} | ||
} | ||
) | ||
throw patchingError | ||
} | ||
} | ||
} | ||
|
||
async function applyPatching( | ||
gameInfo: GameInfo, | ||
newVersion: string, | ||
|
@@ -1717,6 +1794,9 @@ async function applyPatching( | |
const previousManifest = await getManifest(appName, platform, version) | ||
const currentManifest = await getManifest(appName, platform, newVersion) | ||
|
||
// check if it is faster to patch or install and throw if install is faster | ||
await checkIfPatchingIsFaster(previousManifest, currentManifest, gameInfo) | ||
|
||
logInfo( | ||
`Patching ${gameInfo.title} from ${version} to ${newVersion}`, | ||
LogPrefix.HyperPlay | ||
|
@@ -1859,6 +1939,16 @@ async function applyPatching( | |
|
||
return { status: 'done' } | ||
} catch (error) { | ||
if (error instanceof PatchingError) { | ||
if (error.reason === 'slower-than-install') { | ||
if (error.eventToTrack) { | ||
trackEvent(error.eventToTrack) | ||
} | ||
// this will not track any error events or call captureException in the calling code. it will try to install | ||
return { status: 'error' } | ||
} | ||
} | ||
|
||
logError(`Error while patching ${error}`, LogPrefix.HyperPlay) | ||
|
||
trackEvent({ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { PossibleMetricPayloads } from 'backend/metrics/types' | ||
|
||
export type PatchingErrorReason = 'slower-than-install' | ||
|
||
export class PatchingError extends Error { | ||
reason: PatchingErrorReason | ||
eventToTrack?: PossibleMetricPayloads | ||
|
||
constructor( | ||
message: string, | ||
reason: PatchingErrorReason, | ||
eventToTrack?: PossibleMetricPayloads | ||
) { | ||
super(message) // Pass the message to the base Error class | ||
this.reason = reason | ||
this.eventToTrack = eventToTrack | ||
this.name = 'PatchingError' // Set a custom error name | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
open to suggestions here. I'd suggest we leave for another future iteration though