feat(lib/assets): improve caching

This commit is contained in:
PalmDevs 2025-06-06 21:13:00 +07:00
parent 2c56aa87c1
commit 123bc83640
No known key found for this signature in database
GPG key ID: 6FAE8F8677ACA8BA
3 changed files with 46 additions and 38 deletions

View file

@ -1,6 +1,9 @@
import { createLogger } from '@revenge-mod/utils/logger'
import type { Metro } from '@revenge-mod/modules/types'
import type { Asset, AssetId } from './types'
export const _logger = createLogger('revenge.assets')
export const _customs = new Set<Asset>()
export const _overrides = new WeakMap<Asset, Asset>()
export const _metas = new WeakMap<

View file

@ -1,59 +1,52 @@
// TODO(lib/assets): do not depend on Discord's ClientInfoModule for versioning, use native interop
import { ClientInfoModule } from '@revenge-mod/discord/native'
import { getStorage } from '@revenge-mod/storage'
// import { debounce } from '@revenge-mod/utils/callbacks'
import { createLogger } from '@revenge-mod/utils/logger'
import { debounce } from '@revenge-mod/utils/callbacks'
import { mergeDeep } from '@revenge-mod/utils/objects'
import { proxify } from '@revenge-mod/utils/proxy'
import { _logger } from './_internal'
import type { Metro } from '@revenge-mod/modules/types'
import type { Asset } from './types'
const logger = createLogger('revenge.assets.caches')
const Version = 1
const CurrentKey = `${Version}.${ClientInfoModule.Build}`
const Key = `${Version}.${ClientInfoModule.Build}`
const CacheStorage = getStorage<Cache>('revenge/assets', {
default: { k: CurrentKey, c: {} },
// In-memory cache
export const cache: Cache = {}
const CacheStorage = getStorage<Cache>(`revenge/assets.${Key}`, {
default: cache,
directory: 'cache',
})
export const cache: Cache['c'] = proxify(
() => {
if (CacheStorage.cache) return CacheStorage.cache.c
},
{ hint: 'object' },
)!
_logger.log(`[${performance.now()}] CacheStorage instance created`)
// biome-ignore lint/complexity/useArrowFunction: Arrow functions are not supported
CacheStorage.get().then(async function (cache) {
CacheStorage.get().then(async function (cache_) {
// TODO(lib/assets/caches): This loads way too late and requires native interop to load earlier
logger.log(`Loaded cache version: ${cache.k}`)
if (cache.k !== CurrentKey) {
await CacheStorage.delete()
await CacheStorage.set({ k: CurrentKey, c: {} })
// If we are not using the default value, merge it into the in-memory cache, then point the storage cache to the in-memory cache.
if (cache_ !== cache) {
_logger.log(`[${performance.now()}] CacheStorage loaded`)
Object.assign(cache, cache_)
CacheStorage.cache = cache
}
})
export interface Cache {
k: string
c: {
[key: Asset['name']]: {
[key: Asset['type']]: Metro.ModuleID
}
[key: Asset['name']]: {
[key: Asset['type']]: Metro.ModuleID
}
}
// const save = debounce(() => CacheStorage.set({}), 1000)
const save = debounce(() => CacheStorage.set({}), 1000)
export async function cacheAsset(asset: Asset, moduleId: Metro.ModuleID) {
// Merge directly into the cache, only debouncing actual writes
mergeDeep(CacheStorage.cache!.c, {
mergeDeep(cache, {
[asset.name]: {
[asset.type]: moduleId,
},
})
// Commented out because caching currently does not work properly because it is loaded too late
// await save()
await save()
}

View file

@ -2,8 +2,8 @@ import { _initing, _uninits } from '@revenge-mod/modules/_/metro'
import { byName, byProps } from '@revenge-mod/modules/finders/filters'
import { waitForModules } from '@revenge-mod/modules/finders/wait'
import { getModuleDependencies } from '@revenge-mod/modules/metro/utils'
import { _metas, _overrides } from './_internal'
import { cacheAsset } from './caches'
import { _logger, _metas, _overrides } from './_internal'
import { cache, cacheAsset } from './caches'
import type { ReactNative } from '@revenge-mod/react/types'
import type { Asset, ReactNativeAsset } from './types'
@ -25,15 +25,27 @@ const unsubAR = waitForModules(
if (getModuleDependencies(id)!.length) {
unsubAR()
// TODO(assets/patches): conditionally run this if cache does not exist
// More fragile way, but also more performant:
// There is exactly one asset before the reexported asset registry :/
const firstAssetModuleId = id - 1
for (const mId of _uninits) {
if (mId < firstAssetModuleId) continue
_logger.log(`[${performance.now()}] Registry found: ${id}`)
const deps = getModuleDependencies(mId)!
if (deps.length === 1 && deps[0] === id) __r(mId)
// Make sure we don't try to recache assets if it is already cached.
let uncached = true
for (const _ in cache) {
uncached = false
break
}
if (uncached) {
_logger.log('Caching assets...')
// More fragile way, but also more performant:
// There is exactly one asset before the reexported asset registry :/
const firstAssetModuleId = id - 1
for (const mId of _uninits) {
if (mId < firstAssetModuleId) continue
const deps = getModuleDependencies(mId)!
if (deps.length === 1 && deps[0] === id) __r(mId)
}
}
// We already patched the original asset registry, so we don't need to patch again.