@@ -100,6 +100,7 @@ import { trackEvent } from 'backend/metrics/metrics'
100
100
import { getFlag } from 'backend/flags/flags'
101
101
import { ipfsGateway } from 'backend/vite_constants'
102
102
import { GlobalConfig } from 'backend/config'
103
+ import { PatchingError } from './types'
103
104
104
105
interface ProgressDownloadingItem {
105
106
DownloadItem : DownloadItem
@@ -1653,6 +1654,93 @@ export async function downloadGameIpdtManifest(
1653
1654
}
1654
1655
}
1655
1656
1657
+ async function checkIfPatchingIsFaster (
1658
+ oldManifestPath : string ,
1659
+ newManifestPath : string ,
1660
+ gameInfo : GameInfo
1661
+ ) {
1662
+ // read manifests
1663
+ const oldManifestJson = JSON . parse ( readFileSync ( oldManifestPath ) . toString ( ) )
1664
+ const newManifestJson = JSON . parse ( readFileSync ( newManifestPath ) . toString ( ) )
1665
+
1666
+ // compare manifests
1667
+
1668
+ const { compareManifests } = await import ( '@hyperplay/patcher' )
1669
+ const { estimatedPatchSizeInKB } = compareManifests (
1670
+ oldManifestJson . files ,
1671
+ newManifestJson . files
1672
+ )
1673
+
1674
+ // calc break point % where patching is faster
1675
+ if (
1676
+ gameInfo ?. install ?. platform &&
1677
+ gameInfo . channels &&
1678
+ gameInfo ?. install ?. channelName &&
1679
+ Object . hasOwn ( gameInfo . channels , gameInfo . install . channelName )
1680
+ ) {
1681
+ const channelName = gameInfo . install . channelName
1682
+ const [ releaseMeta ] = getReleaseMeta ( gameInfo , channelName )
1683
+ const platform = handleArchAndPlatform (
1684
+ gameInfo . install . platform ,
1685
+ releaseMeta
1686
+ )
1687
+ const downloadSize = parseInt (
1688
+ releaseMeta . platforms [ platform ] ?. downloadSize ?? '0'
1689
+ )
1690
+ const installSize = parseInt (
1691
+ releaseMeta . platforms [ platform ] ?. installSize ?? '0'
1692
+ )
1693
+ // @TODO : get these speed values from local checks of download/write speed
1694
+ const patchingSpeeds = getFlag ( 'patching-speeds' , {
1695
+ downloadSpeedInKBPerSecond : 25600 ,
1696
+ extractionSpeedInKBPerSecond : 51200 ,
1697
+ patchingSpeedEstimateInKBPerSecond : 5120
1698
+ } ) as {
1699
+ downloadSpeedInKBPerSecond : number
1700
+ extractionSpeedInKBPerSecond : number
1701
+ patchingSpeedEstimateInKBPerSecond : number
1702
+ }
1703
+ const downloadSpeedInKBPerSecond = patchingSpeeds . downloadSpeedInKBPerSecond
1704
+ const extractionSpeedInKBPerSecond =
1705
+ patchingSpeeds . extractionSpeedInKBPerSecond
1706
+ const estTimeToInstallFullGameInSec =
1707
+ ( downloadSize / 1024 ) * downloadSpeedInKBPerSecond +
1708
+ ( installSize / 1024 ) * extractionSpeedInKBPerSecond
1709
+
1710
+ // @TODO : get this value from local check of patching speed
1711
+ const patchingSpeedEstimateInKBPerSecond =
1712
+ patchingSpeeds . patchingSpeedEstimateInKBPerSecond
1713
+ const estTimeToPatchGameInSec =
1714
+ estimatedPatchSizeInKB / patchingSpeedEstimateInKBPerSecond
1715
+
1716
+ if ( estTimeToPatchGameInSec > estTimeToInstallFullGameInSec ) {
1717
+ const abortMessage = `Downloading full game instead of patching. \n
1718
+ Estimated time to install full game: ${ estTimeToInstallFullGameInSec } seconds. \n
1719
+ Estimated time to patch: ${ estTimeToPatchGameInSec }
1720
+ `
1721
+ logInfo ( abortMessage , LogPrefix . HyperPlay )
1722
+ const patchingError = new PatchingError (
1723
+ abortMessage ,
1724
+ 'slower-than-install' ,
1725
+ {
1726
+ event : 'Patching Too Slow' ,
1727
+ properties : {
1728
+ game_name : gameInfo . app_name ,
1729
+ game_title : gameInfo . title ,
1730
+ platform : getPlatformName ( platform ) ,
1731
+ platform_arch : platform ,
1732
+ est_time_to_install_sec : estTimeToInstallFullGameInSec . toString ( ) ,
1733
+ est_time_to_patch_sec : estTimeToPatchGameInSec . toString ( ) ,
1734
+ old_game_version : gameInfo . install . version ?? 'unknown' ,
1735
+ new_game_version : gameInfo . version ?? 'unknown'
1736
+ }
1737
+ }
1738
+ )
1739
+ throw patchingError
1740
+ }
1741
+ }
1742
+ }
1743
+
1656
1744
async function applyPatching (
1657
1745
gameInfo : GameInfo ,
1658
1746
newVersion : string ,
@@ -1717,6 +1805,9 @@ async function applyPatching(
1717
1805
const previousManifest = await getManifest ( appName , platform , version )
1718
1806
const currentManifest = await getManifest ( appName , platform , newVersion )
1719
1807
1808
+ // check if it is faster to patch or install and throw if install is faster
1809
+ await checkIfPatchingIsFaster ( previousManifest , currentManifest , gameInfo )
1810
+
1720
1811
logInfo (
1721
1812
`Patching ${ gameInfo . title } from ${ version } to ${ newVersion } ` ,
1722
1813
LogPrefix . HyperPlay
@@ -1859,6 +1950,16 @@ async function applyPatching(
1859
1950
1860
1951
return { status : 'done' }
1861
1952
} catch ( error ) {
1953
+ if ( error instanceof PatchingError ) {
1954
+ if ( error . reason === 'slower-than-install' ) {
1955
+ if ( error . eventToTrack ) {
1956
+ trackEvent ( error . eventToTrack )
1957
+ }
1958
+ // this will not track any error events or call captureException in the calling code. it will try to install
1959
+ return { status : 'error' }
1960
+ }
1961
+ }
1962
+
1862
1963
logError ( `Error while patching ${ error } ` , LogPrefix . HyperPlay )
1863
1964
1864
1965
trackEvent ( {
@@ -1883,7 +1984,12 @@ async function applyPatching(
1883
1984
newVersion
1884
1985
}
1885
1986
} )
1886
- rmSync ( datastoreDir , { recursive : true } )
1987
+
1988
+ // errors can be thrown before datastore dir created. rmSync on nonexistent dir blocks indefinitely
1989
+ if ( existsSync ( datastoreDir ) ) {
1990
+ rmSync ( datastoreDir , { recursive : true } )
1991
+ }
1992
+
1887
1993
return { status : 'error' , error : `Error while patching ${ error } ` }
1888
1994
}
1889
1995
}
0 commit comments