From 578b78dd7ed6b26ebc877a4ef8a173ef47ca8ef9 Mon Sep 17 00:00:00 2001 From: PalmDevs Date: Thu, 31 Jul 2025 19:15:08 +0700 Subject: [PATCH] feat(lib/modules/finders/waits): cached-only waits --- lib/assets/src/preinit.ts | 4 ++ lib/discord/src/common/flux.ts | 2 +- lib/discord/src/start.ts | 3 ++ lib/modules/src/finders/wait.ts | 58 +++++++++++++++++++++- lib/react/src/index.ts | 5 ++ lib/react/src/native/patches.ts | 34 ++++++------- src/plugins/preinit/no-track/index.ts | 40 +++++++++------ src/plugins/start/error-boundary/index.tsx | 3 ++ src/plugins/start/settings/index.ts | 1 + 9 files changed, 112 insertions(+), 38 deletions(-) diff --git a/lib/assets/src/preinit.ts b/lib/assets/src/preinit.ts index 7462979..d6fbfc5 100644 --- a/lib/assets/src/preinit.ts +++ b/lib/assets/src/preinit.ts @@ -35,6 +35,8 @@ const byAssetSourceResolver = byDependencies([ invariantId, ]) +const cachedOnly = { cached: true } + // Tracking/caching assets const unsubAR = waitForModules( byProps('registerAsset'), @@ -82,6 +84,7 @@ const unsubAR = waitForModules( return (asset.id = result) } }, + cachedOnly, ) /** @@ -138,4 +141,5 @@ const unsubRAS = waitForModules( // @ts-expect-error rAS.addCustomSourceTransformer(({ asset }) => aOverrides.get(asset)) }, + cachedOnly, ) diff --git a/lib/discord/src/common/flux.ts b/lib/discord/src/common/flux.ts index 2827094..6e17f11 100644 --- a/lib/discord/src/common/flux.ts +++ b/lib/discord/src/common/flux.ts @@ -76,7 +76,7 @@ export function getStore( return noop } - return waitForModules(byStoreName(name), callback) + return waitForModules(byStoreName(name), callback, { cached: true }) } /// FILTERING diff --git a/lib/discord/src/start.ts b/lib/discord/src/start.ts index a10ee6e..fe58dee 100644 --- a/lib/discord/src/start.ts +++ b/lib/discord/src/start.ts @@ -37,4 +37,7 @@ const unsubSRC = waitForModules( }, ) }, + { + cached: true, + }, ) diff --git a/lib/modules/src/finders/wait.ts b/lib/modules/src/finders/wait.ts index cfcfa1b..ff80282 100644 --- a/lib/modules/src/finders/wait.ts +++ b/lib/modules/src/finders/wait.ts @@ -1,3 +1,5 @@ +import { noop } from '@revenge-mod/utils/callback' +import { getCachedFilterRegistry } from '../caches' import { mInitialized } from '../metro/patches' import { onAnyModuleInitialized, @@ -34,7 +36,18 @@ export type WaitForModulesOptions< ReturnNamespace extends boolean = boolean, All extends boolean = boolean, > = RunFilterReturnExportsOptions & - BaseWaitForModulesOptions + BaseWaitForModulesOptions & { + /** + * Use cached results **only** (if possible). + * If there is no cache result, this works as if you did not pass this option at all. + * + * By default, waits cache results but does not use them, because new modules may still be found. + * Use this option as an optimization if you are sure that you don't need to find new modules once results are cached. + * + * @default false + */ + cached?: boolean + } export type WaitForModulesResult< F extends Filter, @@ -84,6 +97,46 @@ export function waitForModules( callback: WaitForModulesCallback, options?: WaitForModulesOptions, ): WaitForModulesUnsubscribeFunction { + if (options?.cached) { + const reg = getCachedFilterRegistry(filter.key) + if (reg === null) return noop + + if (reg) { + if (__BUILD_FLAG_DEBUG_MODULE_WAITS__) + nativeLoggingHook( + `\u001b[32mUsing cached results for wait \u001b[33m${filter.key}\u001b[0m`, + 1, + ) + + const runCachedCallback = (id: Metro.ModuleID) => { + const flag = reg[id] + + DEBUG_logWaitMatched(filter.key, id, flag, true) + callback( + exportsFromFilterResultFlag( + flag, + getInitializedModuleExports(id), + options, + ), + id, + ) + } + + const cleanups: Array<() => void> = [] + + for (const sId of Object.keys(reg)) { + const id = Number(sId) + if (mInitialized.has(id)) continue + + cleanups.push(onModuleInitialized(id, runCachedCallback)) + } + + return () => { + for (const cleanup of cleanups) cleanup() + } + } + } + if (__BUILD_FLAG_DEBUG_MODULE_WAITS__) nativeLoggingHook( `\u001b[94mWaiting for module matching: \u001b[93m${filter.key}\u001b[0m`, @@ -173,9 +226,10 @@ function DEBUG_logWaitMatched( key: string, id: Metro.ModuleID, flag: FilterResultFlag, + cached?: boolean, ) { nativeLoggingHook( - `\u001b[32mWait matched: \u001b[33m${key}\u001b[0m (matched ${id}, ${FilterResultFlagToHumanReadable[flag]})`, + `\u001b[32mWait matched: \u001b[33m${key}\u001b[0m (matched ${id}, ${FilterResultFlagToHumanReadable[flag]}${cached ? ', \u001b[92mcached\u001b[0m' : ''})`, 1, ) } diff --git a/lib/react/src/index.ts b/lib/react/src/index.ts index eddd84f..0791f82 100644 --- a/lib/react/src/index.ts +++ b/lib/react/src/index.ts @@ -29,6 +29,11 @@ const unsubRN = waitForModules( ReactNativeModuleId = id ReactNative = exports + + require('./native/patches') + }, + { + cached: true, }, ) diff --git a/lib/react/src/native/patches.ts b/lib/react/src/native/patches.ts index f91c20c..b835a2b 100644 --- a/lib/react/src/native/patches.ts +++ b/lib/react/src/native/patches.ts @@ -1,5 +1,3 @@ -import { byProps } from '@revenge-mod/modules/finders/filters' -import { waitForModules } from '@revenge-mod/modules/finders/wait' import { after, before } from '@revenge-mod/patcher' import { ReactNative } from '..' import type { RunApplicationCallback } from '../types' @@ -7,22 +5,18 @@ import type { RunApplicationCallback } from '../types' export const sBeforeRunApplication = new Set() export const sAfterRunApplication = new Set() -const unsub = waitForModules(byProps('AppRegistry'), () => { - unsub() - - before(ReactNative.AppRegistry, 'runApplication', args => { - for (const cb of sBeforeRunApplication) - try { - cb() - } catch {} - return args - }) - - after(ReactNative.AppRegistry, 'runApplication', res => { - for (const cb of sAfterRunApplication) - try { - cb() - } catch {} - return res - }) +before(ReactNative.AppRegistry, 'runApplication', args => { + for (const cb of sBeforeRunApplication) + try { + cb() + } catch {} + return args +}) + +after(ReactNative.AppRegistry, 'runApplication', res => { + for (const cb of sAfterRunApplication) + try { + cb() + } catch {} + return res }) diff --git a/src/plugins/preinit/no-track/index.ts b/src/plugins/preinit/no-track/index.ts index e422ce8..b53478d 100644 --- a/src/plugins/preinit/no-track/index.ts +++ b/src/plugins/preinit/no-track/index.ts @@ -7,6 +7,10 @@ import { PluginFlags } from '@revenge-mod/plugins/constants' import { noop } from '@revenge-mod/utils/callback' import { getCurrentStack } from '@revenge-mod/utils/error' +const cachedOnly = { + cached: true, +} + // TODO(plugins/no-track): Block Sentry native-side registerPlugin( { @@ -42,6 +46,7 @@ registerPlugin( instead(SentryUtils, 'setTags', noop) instead(SentryUtils, 'setUser', noop) }, + cachedOnly, ) // modules/errors/native/SentryInitUtils.tsx @@ -52,6 +57,7 @@ registerPlugin( instead(SentryInitUtils, 'initSentry', noop) }, + cachedOnly, ) // Discord uses ReactNavigationInstrumentation to track navigation @@ -71,6 +77,7 @@ registerPlugin( }, ) }, + cachedOnly, ) if (__DEV__) @@ -86,17 +93,7 @@ registerPlugin( cleanup(unsubSU, unsubSIU, unsubSentryInst) }, - start({ - cleanup, - logger, - unscoped: { - discord: { - common: { - flux: { DispatcherModuleId }, - }, - }, - }, - }) { + init({ cleanup }) { // utils/AnalyticsUtils.tsx const unsubAU = waitForModules( byProps<{ @@ -109,7 +106,6 @@ registerPlugin( AnalyticsUtils => { unsubAU() - logger.info('Patching AnalyticsUtils...') instead(AnalyticsUtils.default, 'track', noop) instead(AnalyticsUtils, 'trackNetworkAction', noop) @@ -119,8 +115,22 @@ registerPlugin( for (const key of Object.keys(handlers)) instead(handlers, key, noop) }, + cachedOnly, ) + cleanup(unsubAU) + }, + start({ + cleanup, + logger, + unscoped: { + discord: { + common: { + flux: { DispatcherModuleId }, + }, + }, + }, + }) { // modules/app_analytics/useTrackImpression.tsx const unsubTI = waitForModules( byProps<{ @@ -130,11 +140,10 @@ registerPlugin( useTrackImpression => { unsubTI() - logger.info('Patching useTrackImpression...') - instead(useTrackImpression, 'trackImpression', noop) instead(useTrackImpression, 'default', noop) }, + cachedOnly, ) // actions/AnalyticsTrackActionCreators.tsx @@ -148,9 +157,10 @@ registerPlugin( instead(AnalyticsTrackActionCreators, 'track', noop) } }, + cachedOnly, ) - cleanup(unsubAU, unsubTI, unsubATAC) + cleanup(unsubTI, unsubATAC) }, stop({ plugin }) { plugin.flags |= PluginFlags.ReloadRequired diff --git a/src/plugins/start/error-boundary/index.tsx b/src/plugins/start/error-boundary/index.tsx index fbed989..96a2a9e 100644 --- a/src/plugins/start/error-boundary/index.tsx +++ b/src/plugins/start/error-boundary/index.tsx @@ -43,6 +43,9 @@ registerPlugin( }, ) }, + { + cached: true, + }, ) cleanup(unsubEB) diff --git a/src/plugins/start/settings/index.ts b/src/plugins/start/settings/index.ts index 038d93d..a62a792 100644 --- a/src/plugins/start/settings/index.ts +++ b/src/plugins/start/settings/index.ts @@ -44,6 +44,7 @@ const pluginSettings = registerPlugin( patchSettingsOverviewScreen(exports) }, { + cached: true, returnNamespace: true, }, )