import { ConfigValidationError } from '../../core/src/config.ts'; import type { PlatformPlacementConfig, RewardedAdsConfig, RewardedAdProvider, RewardedAdTargetPlatform, } from '../../core/src/types.ts'; export const ROOT_PACKAGE_NAME = 'cocos-rewarded-ads-kit'; export const ROOT_PACKAGE_VERSION = '1.0.8'; const PLATFORM_PACKAGE_MAP = { 'wechat-mini-game:wechat': `${ROOT_PACKAGE_NAME}/adapters/wechat-mini-game`, 'douyin-mini-game:douyin': `${ROOT_PACKAGE_NAME}/adapters/douyin-mini-game`, 'android:admob': `${ROOT_PACKAGE_NAME}/adapters/android-admob`, 'ios:admob': `${ROOT_PACKAGE_NAME}/adapters/ios-admob`, 'android:max': `${ROOT_PACKAGE_NAME}/adapters/android-max`, 'ios:max': `${ROOT_PACKAGE_NAME}/adapters/ios-max`, 'android:topon': `${ROOT_PACKAGE_NAME}/adapters/android-topon`, 'ios:topon': `${ROOT_PACKAGE_NAME}/adapters/ios-topon`, } as const; export interface NativeTemplateTarget { templatePath: string; outputPath: string; } export interface NativeProviderInjectionPlan { platform: 'android' | 'ios'; provider: 'admob' | 'max' | 'topon'; gradleDependencies: string[]; podDependencies: string[]; templateTargets: NativeTemplateTarget[]; } function getPlatformPlacements(options: { targetPlatform: RewardedAdTargetPlatform; config: RewardedAdsConfig; }) { const placements: Array<{ placement: string; config: PlatformPlacementConfig }> = []; for (const [placement, placementConfig] of Object.entries(options.config.placements ?? {})) { const currentPlatformConfig = placementConfig?.[options.targetPlatform]; if (currentPlatformConfig) { placements.push({ placement, config: currentPlatformConfig, }); } } return placements; } export function selectAdapterPackageForPlatform(options: { targetPlatform: string; provider: string; }) { const key = `${options.targetPlatform}:${options.provider}` as keyof typeof PLATFORM_PACKAGE_MAP; const packageName = PLATFORM_PACKAGE_MAP[key]; if (!packageName) { throw new Error(`Unsupported rewarded adapter selection: ${options.targetPlatform}/${options.provider}`); } return packageName; } export function resolveBuildPlatformSelection(options: { targetPlatform: RewardedAdTargetPlatform; config: RewardedAdsConfig; }) { const placements = getPlatformPlacements(options); const providers = new Set(); for (const entry of placements) { providers.add(entry.config.provider); } if (providers.size === 0) { throw new ConfigValidationError( `No rewarded placements configured for target platform ${options.targetPlatform}`, ); } if (providers.size > 1) { throw new ConfigValidationError( `Target platform ${options.targetPlatform} must use a single rewarded provider in v1`, ); } const provider = Array.from(providers)[0]; return { provider, packageName: selectAdapterPackageForPlatform({ targetPlatform: options.targetPlatform, provider, }), }; } export function generateActiveAdapterSource(packageName: string) { return `export { createAdapter } from '${packageName}';\n`; } export function generateCurrentPlatformConfigSource(options: { targetPlatform: RewardedAdTargetPlatform; config: RewardedAdsConfig; }) { const placements = getPlatformPlacements(options); const scopedPlacements: RewardedAdsConfig['placements'] = {}; for (const entry of placements) { scopedPlacements[entry.placement] = { [options.targetPlatform]: entry.config, }; } const scopedConfig: RewardedAdsConfig = { version: 1, defaults: { preloadOnInit: Array.isArray(options.config.defaults?.preloadOnInit) ? options.config.defaults?.preloadOnInit.filter((placement) => scopedPlacements[placement]) : [], }, placements: scopedPlacements, }; return [ 'export const generatedRewardedAdsConfig = ', `${JSON.stringify(scopedConfig, null, 2)};\n`, ].join(''); } export function generateRuntimeBindingSource(options: { packageName: string; configModulePath: string; runtimePackageName?: string; }) { const runtimePackageName = options.runtimePackageName ?? `${ROOT_PACKAGE_NAME}/runtime`; return `import { createAdapter } from '${options.packageName}'; import { generatedRewardedAdsConfig } from '${options.configModulePath}'; import { setGeneratedRewardedAdapterFactory, setGeneratedRewardedAdsConfig, } from '${runtimePackageName}'; setGeneratedRewardedAdsConfig(generatedRewardedAdsConfig); setGeneratedRewardedAdapterFactory(createAdapter); `; } export function generateBootstrapRuntimeSource(options?: { runtimePackageName?: string; }) { const runtimePackageName = options?.runtimePackageName ?? `${ROOT_PACKAGE_NAME}/runtime`; return `import './install-generated-bindings.ts'; import { createRewardedAdsRuntime } from '${runtimePackageName}'; export function createPreparedRewardedAdsRuntime(options) { return createRewardedAdsRuntime(options); } `; } export function createNativeProviderInjectionPlan(options: { targetPlatform: 'android' | 'ios'; provider: 'admob' | 'max' | 'topon'; }): NativeProviderInjectionPlan { if (options.targetPlatform === 'android' && options.provider === 'admob') { return { platform: 'android', provider: 'admob', gradleDependencies: [ 'implementation "com.google.android.gms:play-services-ads:+"', 'implementation "com.google.android.ump:user-messaging-platform:+"', ], podDependencies: [], templateTargets: [ { templatePath: 'templates/native/android/admob/PrivacyConsentBridge.kt.template', outputPath: 'native/engine/android/app/src/com/tinivo/rewardedads/PrivacyConsentBridge.kt', }, { templatePath: 'templates/native/android/admob/RewardedAdsAdMobBridge.kt.template', outputPath: 'native/engine/android/app/src/com/tinivo/rewardedads/RewardedAdsAdMobBridge.kt', }, ], }; } if (options.targetPlatform === 'android' && options.provider === 'max') { return { platform: 'android', provider: 'max', gradleDependencies: [ 'implementation "com.applovin:applovin-sdk:+"', 'implementation "com.applovin.mediation:google-adapter:+"', ], podDependencies: [], templateTargets: [ { templatePath: 'templates/native/android/max/RewardedAdsMaxBridge.kt.template', outputPath: 'native/engine/android/app/src/com/tinivo/rewardedads/RewardedAdsMaxBridge.kt', }, ], }; } if (options.targetPlatform === 'android' && options.provider === 'topon') { return { platform: 'android', provider: 'topon', gradleDependencies: [ 'implementation "com.anythink.sdk:core:+"', 'implementation "com.anythink.sdk:rewardvideo:+"', ], podDependencies: [], templateTargets: [ { templatePath: 'templates/native/android/topon/RewardedAdsTopOnBridge.kt.template', outputPath: 'native/engine/android/app/src/com/tinivo/rewardedads/RewardedAdsTopOnBridge.kt', }, ], }; } if (options.targetPlatform === 'ios' && options.provider === 'admob') { return { platform: 'ios', provider: 'admob', gradleDependencies: [], podDependencies: [], templateTargets: [ { templatePath: 'templates/native/ios/admob/RewardedAdsAdMobBridge.h.template', outputPath: 'native/engine/ios/RewardedAdsAdMobBridge.h', }, { templatePath: 'templates/native/ios/admob/RewardedAdsAdMobBridge.mm.template', outputPath: 'native/engine/ios/RewardedAdsAdMobBridge.mm', }, ], }; } if (options.targetPlatform === 'ios' && options.provider === 'max') { return { platform: 'ios', provider: 'max', gradleDependencies: [], podDependencies: [ "pod 'AppLovinSDK'", ], templateTargets: [ { templatePath: 'templates/native/ios/max/RewardedAdsMaxBridge.h.template', outputPath: 'native/engine/ios/RewardedAdsMaxBridge.h', }, { templatePath: 'templates/native/ios/max/RewardedAdsMaxBridge.mm.template', outputPath: 'native/engine/ios/RewardedAdsMaxBridge.mm', }, ], }; } if (options.targetPlatform === 'ios' && options.provider === 'topon') { return { platform: 'ios', provider: 'topon', gradleDependencies: [], podDependencies: [ "pod 'TopOnSDK'", "pod 'AnyThinkRewardedVideo'", ], templateTargets: [ { templatePath: 'templates/native/ios/topon/RewardedAdsTopOnBridge.h.template', outputPath: 'native/engine/ios/RewardedAdsTopOnBridge.h', }, { templatePath: 'templates/native/ios/topon/RewardedAdsTopOnBridge.mm.template', outputPath: 'native/engine/ios/RewardedAdsTopOnBridge.mm', }, ], }; } throw new Error(`Unsupported native injection plan: ${options.targetPlatform}/${options.provider}`); } export function generateCocosExtensionPackageJson(options: { extensionName: string; }) { return { package_version: 2, version: ROOT_PACKAGE_VERSION, name: options.extensionName, description: 'Rewarded ads build extension for Cocos Creator', type: 'commonjs', main: './dist/main.js', contributions: { builder: './dist/builder.js', }, }; } export function generateCocosExtensionMainSource() { return `'use strict'; function load() {} function unload() {} module.exports = { load, unload, }; `; } export function generateCocosExtensionBuilderSource() { return `'use strict'; function load() {} function unload() {} const configs = { '*': { hooks: './hooks', }, }; module.exports = { load, unload, configs, }; `; } export function generateCocosExtensionHooksSource(options: { extensionName: string; buildPluginPackageName?: string; }) { const buildPluginPackageName = options.buildPluginPackageName ?? `${ROOT_PACKAGE_NAME}/build-plugin`; return `'use strict'; let hooksPromise; function resolveHooks() { if (!hooksPromise) { hooksPromise = import('${buildPluginPackageName}') .then(({ createCocosBuildExtensionHooks }) => createCocosBuildExtensionHooks({ extensionName: '${options.extensionName}', })); } return hooksPromise; } const throwError = true; async function load(...args) { const hooks = await resolveHooks(); return hooks.load(...args); } async function unload(...args) { const hooks = await resolveHooks(); return hooks.unload(...args); } async function onBeforeBuild(...args) { const hooks = await resolveHooks(); return hooks.onBeforeBuild(...args); } async function onAfterBuild(...args) { const hooks = await resolveHooks(); return hooks.onAfterBuild(...args); } module.exports = { throwError, load, unload, onBeforeBuild, onAfterBuild, }; `; }