Skip to content
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

Merged
merged 12 commits into from
Nov 16, 2024
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -366,8 +366,8 @@
"@hyperplay/extension-importer": "^0.0.4",
"@hyperplay/extension-provider": "^0.0.8",
"@hyperplay/mock-backend": "^0.0.1",
"@hyperplay/patcher": "^0.0.17",
"@hyperplay/overlay": "^0.0.7",
"@hyperplay/patcher": "^0.0.18-alpha.1",
"@hyperplay/providers": "^0.0.6",
"@hyperplay/proxy-server": "^0.0.11"
},
Expand Down
16 changes: 16 additions & 0 deletions src/backend/metrics/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,21 @@ export interface PatchingFailed {
sensitiveProperties?: never
}

export interface PatchingTooSlow {
event: 'Patching Too Slow'
properties: {
game_name: string
game_title: string
platform: ReturnType<typeof getPlatformName>
platform_arch: InstallPlatform
old_game_version: string
new_game_version: string
est_time_to_patch_sec: string
est_time_to_install_sec: string
}
sensitiveProperties?: never
}

export interface GameUninstallRequested {
event: 'Game Uninstall Requested'
properties: {
Expand Down Expand Up @@ -442,5 +457,6 @@ export type PossibleMetricPayloads =
| PatchingStarted
| PatchingSuccess
| PatchingFailed
| PatchingTooSlow

export type PossibleMetricEventNames = PossibleMetricPayloads['event']
90 changes: 90 additions & 0 deletions src/backend/storeManagers/hyperplay/games.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Copy link
Collaborator Author

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

const estTimeToInstallFullGameInSec =
(downloadSize / 1024) * downloadSpeedInKBPerSecond +
(installSize / 1024) * extractionSpeedInKBPerSecond

// @TODO: get this value from local check of patching speed
const patchingSpeedEstimateInKBPerSecond = 5 * 1024
Copy link
Collaborator Author

Choose a reason for hiding this comment

The 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?

Copy link
Contributor

Choose a reason for hiding this comment

The 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.
This way we can alter any of them to make the calculation more precise.

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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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({
Expand Down
19 changes: 19 additions & 0 deletions src/backend/storeManagers/hyperplay/types.ts
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
}
}
10 changes: 5 additions & 5 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1293,10 +1293,10 @@
"@hyperplay/utils" "^0.3.3"
mobx "^6.12.3"

"@hyperplay/patcher@^0.0.17":
version "0.0.17"
resolved "https://registry.yarnpkg.com/@hyperplay/patcher/-/patcher-0.0.17.tgz#0b62dffbb622712c592d271e49b34c70448489c8"
integrity sha512-29DWtEGlCgxw22u0/k5QtZhSLn/MmHdXCsilCe9w/cbaWguT69oYSSE/E2omhaPIntF3w2/Lt0pxoSpY1Qly1g==
"@hyperplay/patcher@^0.0.18-alpha.1":
version "0.0.18-alpha.1"
resolved "https://registry.yarnpkg.com/@hyperplay/patcher/-/patcher-0.0.18-alpha.1.tgz#f2d9a9fc0bf6a0720747fd63123856d8a92a7ccf"
integrity sha512-1LYjXpsP6khEs2ayEF6213rXalXU5MxkTW0NMGK92Ni0DR++ljtUL4SaZijGA6nXZMzmJHX8XvcNdrPlgEJuyQ==
dependencies:
"@valist/sdk" "^2.10.5"
ethers "^6.13.4"
Expand Down Expand Up @@ -1344,7 +1344,7 @@
dependencies:
bignumber.js "^9.1.2"

"@hyperplay/utils@^0.3.4":
"@hyperplay/utils@^0.3.3", "@hyperplay/utils@^0.3.4":
version "0.3.4"
resolved "https://registry.yarnpkg.com/@hyperplay/utils/-/utils-0.3.4.tgz#71d4abe910ec8550c865418118eb1c07df3b399d"
integrity sha512-ndkDnp+iV9BlLCKpbq0XVuuc/ORzaZ+69wFvVvAftdU6mbwbk4EfxAwTOpmXWkLaiGp0MZaybYMa5zYZkxkW5w==
Expand Down
Loading