From 3341169f9bead76cf5a7c7b03708fc61b8468460 Mon Sep 17 00:00:00 2001 From: CORE-FOLDCCCore <1813547935@qq.com> Date: Fri, 5 Jun 2026 21:44:35 +0800 Subject: [PATCH] Add TapADN smart preload attribution --- .../Configs/IAAAdDebugSampleConfig.asset | 13 +- .../Runtime/TapadnIAAAdDebugSampleGui.cs | 8 + Tapadn_Adapter/Runtime/Resources.meta | 8 + .../TapadnSmartLoadPolicy_Default.json | 40 + .../TapadnSmartLoadPolicy_Default.json.meta | 7 + .../Runtime/Scripts/TapadnAdController.cs | 1 + .../Runtime/Scripts/TapadnAwardVideoPlayer.cs | 17 +- .../Scripts/TapadnControllerOptions.cs | 22 +- .../Scripts/TapadnInteractionPlayer.cs | 16 + .../Scripts/TapadnSmartLoadOrchestrator.cs | 981 ++++++++++++++++++ .../TapadnSmartLoadOrchestrator.cs.meta | 11 + .../Runtime/Scripts/TapadnSplashPlayer.cs | 16 + 12 files changed, 1133 insertions(+), 7 deletions(-) create mode 100644 Tapadn_Adapter/Runtime/Resources.meta create mode 100644 Tapadn_Adapter/Runtime/Resources/TapadnSmartLoadPolicy_Default.json create mode 100644 Tapadn_Adapter/Runtime/Resources/TapadnSmartLoadPolicy_Default.json.meta create mode 100644 Tapadn_Adapter/Runtime/Scripts/TapadnSmartLoadOrchestrator.cs create mode 100644 Tapadn_Adapter/Runtime/Scripts/TapadnSmartLoadOrchestrator.cs.meta diff --git a/Samples~/IAAAdDebugSample/Configs/IAAAdDebugSampleConfig.asset b/Samples~/IAAAdDebugSample/Configs/IAAAdDebugSampleConfig.asset index 66608cb..72c6060 100644 --- a/Samples~/IAAAdDebugSample/Configs/IAAAdDebugSampleConfig.asset +++ b/Samples~/IAAAdDebugSample/Configs/IAAAdDebugSampleConfig.asset @@ -33,7 +33,7 @@ MonoBehaviour: - key: tapadn.sub_channel value: debug - key: tapadn.rewarded_auto_load - value: true + value: false - key: tapadn.rewarded_prewarm_on_init value: false - key: tapadn.rewarded_max_load_attempts @@ -47,7 +47,7 @@ MonoBehaviour: - key: tapadn.reward_amount value: 1 - key: tapadn.interstitial_auto_load - value: true + value: false - key: tapadn.interstitial_prewarm_on_init value: false - key: tapadn.interstitial_max_load_attempts @@ -57,7 +57,7 @@ MonoBehaviour: - key: tapadn.interstitial_show_timeout_ms value: 20000 - key: tapadn.splash_auto_load - value: true + value: false - key: tapadn.splash_prewarm_on_init value: false - key: tapadn.splash_max_load_attempts @@ -66,3 +66,10 @@ MonoBehaviour: value: 500 - key: tapadn.splash_show_timeout_ms value: 20000 + - key: tapadn.smart_preload_enabled + value: true + - key: tapadn.smart_preload_config_asset_path + value: TapadnSmartLoadPolicy_Default + - key: tapadn.smart_preload_config_json + value: >- + {"GlobalDefault":{"AdType":-1,"Scene":"__default__","BaseProbability":0.08,"PreloadThreshold":0.75,"CooldownSeconds":120,"MinSamplesForConfidence":8,"DecayHalfLifeHours":72},"ScenePolicies":[{"AdType":1,"Scene":"splash_debug","BaseProbability":0.25,"PreloadThreshold":0.7,"CooldownSeconds":120,"MinSamplesForConfidence":6,"DecayHalfLifeHours":48},{"AdType":0,"Scene":"reward_debug","BaseProbability":0.6,"PreloadThreshold":0.5,"CooldownSeconds":60,"MinSamplesForConfidence":4,"DecayHalfLifeHours":48},{"AdType":2,"Scene":"interstitial_debug","BaseProbability":0.45,"PreloadThreshold":0.65,"CooldownSeconds":90,"MinSamplesForConfidence":5,"DecayHalfLifeHours":48}]} diff --git a/Samples~/IAAAdDebugSample/Runtime/TapadnIAAAdDebugSampleGui.cs b/Samples~/IAAAdDebugSample/Runtime/TapadnIAAAdDebugSampleGui.cs index dfe3f77..4920d82 100644 --- a/Samples~/IAAAdDebugSample/Runtime/TapadnIAAAdDebugSampleGui.cs +++ b/Samples~/IAAAdDebugSample/Runtime/TapadnIAAAdDebugSampleGui.cs @@ -183,6 +183,8 @@ public sealed class TapadnIAAAdDebugSampleGui : MonoBehaviour GUILayout.Label( $"Channel/SubChannel/Debug: {DisplayValue(options.Channel)}/{DisplayValue(options.SubChannel)}/{options.Debug}", _textStyle); + GUILayout.Label($"Smart Preload: enabled={options.SmartPreloadEnabled}, asset={DisplayValue(options.SmartPreloadConfigAssetPath)}", _textStyle); + GUILayout.Label($"Smart Policy Snapshot: {TapadnSmartLoadOrchestrator.GetDebugStateDump()}", _textStyle); } if (showVerboseState && GUILayout.Button("Refresh Status Snapshot", _buttonStyle, GUILayout.Height(64f))) @@ -191,6 +193,12 @@ public sealed class TapadnIAAAdDebugSampleGui : MonoBehaviour AppendLog("Manual status snapshot refreshed."); } + if (GUILayout.Button("Reset SmartLoad Learning State", _buttonStyle, GUILayout.Height(64f))) + { + TapadnSmartLoadOrchestrator.ResetLearningState(); + AppendLog("TapADN SmartLoad learning state reset."); + } + GUILayout.Space(10f); } diff --git a/Tapadn_Adapter/Runtime/Resources.meta b/Tapadn_Adapter/Runtime/Resources.meta new file mode 100644 index 0000000..eb52a0b --- /dev/null +++ b/Tapadn_Adapter/Runtime/Resources.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0f4f3adf5f7b4b2a8e4f3ff8f3a58c8e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tapadn_Adapter/Runtime/Resources/TapadnSmartLoadPolicy_Default.json b/Tapadn_Adapter/Runtime/Resources/TapadnSmartLoadPolicy_Default.json new file mode 100644 index 0000000..a9fd90d --- /dev/null +++ b/Tapadn_Adapter/Runtime/Resources/TapadnSmartLoadPolicy_Default.json @@ -0,0 +1,40 @@ +{ + "GlobalDefault": { + "AdType": -1, + "Scene": "__default__", + "BaseProbability": 0.08, + "PreloadThreshold": 0.75, + "CooldownSeconds": 120, + "MinSamplesForConfidence": 8, + "DecayHalfLifeHours": 72 + }, + "ScenePolicies": [ + { + "AdType": 0, + "Scene": "reward_debug", + "BaseProbability": 0.6, + "PreloadThreshold": 0.55, + "CooldownSeconds": 90, + "MinSamplesForConfidence": 6, + "DecayHalfLifeHours": 48 + }, + { + "AdType": 2, + "Scene": "interstitial_debug", + "BaseProbability": 0.45, + "PreloadThreshold": 0.65, + "CooldownSeconds": 90, + "MinSamplesForConfidence": 5, + "DecayHalfLifeHours": 48 + }, + { + "AdType": 1, + "Scene": "splash_debug", + "BaseProbability": 0.25, + "PreloadThreshold": 0.7, + "CooldownSeconds": 120, + "MinSamplesForConfidence": 6, + "DecayHalfLifeHours": 48 + } + ] +} diff --git a/Tapadn_Adapter/Runtime/Resources/TapadnSmartLoadPolicy_Default.json.meta b/Tapadn_Adapter/Runtime/Resources/TapadnSmartLoadPolicy_Default.json.meta new file mode 100644 index 0000000..e39aef5 --- /dev/null +++ b/Tapadn_Adapter/Runtime/Resources/TapadnSmartLoadPolicy_Default.json.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: b586678bfef849f2a293548b54f7790a +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tapadn_Adapter/Runtime/Scripts/TapadnAdController.cs b/Tapadn_Adapter/Runtime/Scripts/TapadnAdController.cs index 2ff8d1c..f7aa563 100644 --- a/Tapadn_Adapter/Runtime/Scripts/TapadnAdController.cs +++ b/Tapadn_Adapter/Runtime/Scripts/TapadnAdController.cs @@ -19,6 +19,7 @@ public sealed class TapadnAdController : IAdController _adConfig = adConfig; _options = TapadnControllerOptions.Resolve(adConfig, args); CurrentOptions = _options; + TapadnSmartLoadOrchestrator.Initialize(_options); var sdkConfig = _options.BuildSdkConfig(); DirichletSdk.Init( diff --git a/Tapadn_Adapter/Runtime/Scripts/TapadnAwardVideoPlayer.cs b/Tapadn_Adapter/Runtime/Scripts/TapadnAwardVideoPlayer.cs index 4bb80c5..20183aa 100644 --- a/Tapadn_Adapter/Runtime/Scripts/TapadnAwardVideoPlayer.cs +++ b/Tapadn_Adapter/Runtime/Scripts/TapadnAwardVideoPlayer.cs @@ -70,10 +70,12 @@ public sealed class TapadnAwardVideoPlayer : ADPlayer, IDirichletRewardVideoAuto } curState = 1; + TapadnSmartLoadOrchestrator.OnLoadStarted(AD_Type.AwardVideo, AdScene); _adNative.LoadRewardVideoAd( TapadnAdRequestFactory.BuildRewarded(Key, TapadnAdController.CurrentOptions), ad => { + TapadnSmartLoadOrchestrator.OnLoadResult(AD_Type.AwardVideo, AdScene, true); _loadedAd?.Destroy(); _loadedAd = ad; _loadedAd.Shown += OnManualShown; @@ -86,6 +88,7 @@ public sealed class TapadnAwardVideoPlayer : ADPlayer, IDirichletRewardVideoAuto }, error => { + TapadnSmartLoadOrchestrator.OnLoadResult(AD_Type.AwardVideo, AdScene, false); curState = 0; Debug.LogError($"[TapADN] Rewarded load failed. code={error.Code}, message={error.Message}"); }); @@ -127,12 +130,14 @@ public sealed class TapadnAwardVideoPlayer : ADPlayer, IDirichletRewardVideoAuto _rewardCloseSettleHandler?.Kill(); _rewardCloseSettleHandler = null; curState = 0; + TapadnSmartLoadOrchestrator.OnShowError(AD_Type.AwardVideo, AdScene); Debug.LogError($"[TapADN] Rewarded show failed. code={error?.Code}, message={error?.Message}"); adListener.OnShowError(); } public void OnAdShow() { + TapadnSmartLoadOrchestrator.OnShowStart(AD_Type.AwardVideo, AdScene); NotifyShowStarted(); } @@ -168,6 +173,16 @@ public sealed class TapadnAwardVideoPlayer : ADPlayer, IDirichletRewardVideoAuto { } + public override void OnPlayRequestStarted() + { + TapadnSmartLoadOrchestrator.OnPlayRequestStarted(AD_Type.AwardVideo, AdScene, !UseAutoLoad() && IsReadly()); + } + + public override void EnterAdScenario(string scenario) + { + TapadnSmartLoadOrchestrator.OnEnterAdScenario(AD_Type.AwardVideo, scenario, Key); + } + private bool UseAutoLoad() { return TapadnAdController.CurrentOptions?.RewardedAutoLoad ?? true; @@ -175,7 +190,7 @@ public sealed class TapadnAwardVideoPlayer : ADPlayer, IDirichletRewardVideoAuto private void OnManualShown() { - NotifyShowStarted(); + OnAdShow(); } private void OnManualClicked() diff --git a/Tapadn_Adapter/Runtime/Scripts/TapadnControllerOptions.cs b/Tapadn_Adapter/Runtime/Scripts/TapadnControllerOptions.cs index 49915a4..0534b44 100644 --- a/Tapadn_Adapter/Runtime/Scripts/TapadnControllerOptions.cs +++ b/Tapadn_Adapter/Runtime/Scripts/TapadnControllerOptions.cs @@ -39,6 +39,10 @@ public sealed class TapadnControllerOptions public const string SplashMaxLoadAttemptsKey = "tapadn.splash_max_load_attempts"; public const string SplashLoadRetryDelayMsKey = "tapadn.splash_load_retry_delay_ms"; public const string SplashShowTimeoutMsKey = "tapadn.splash_show_timeout_ms"; + public const string SmartPreloadEnabledKey = "tapadn.smart_preload_enabled"; + public const string SmartPreloadConfigJsonKey = "tapadn.smart_preload_config_json"; + public const string SmartPreloadConfigAssetPathKey = "tapadn.smart_preload_config_asset_path"; + public const string SmartPreloadRemoteConfigJsonKey = "tapadn.smart_preload_remote_json"; public const string ExpressWidthKey = "tapadn.express_width"; public const string ExpressHeightKey = "tapadn.express_height"; @@ -55,23 +59,27 @@ public sealed class TapadnControllerOptions public string ATags { get; set; } public bool AllowIDFAAccess { get; set; } = true; public bool RequestPermissionOnInit { get; set; } - public bool RewardedAutoLoad { get; set; } = true; + public bool RewardedAutoLoad { get; set; } = false; public bool RewardedPrewarmOnInit { get; set; } public int RewardedMaxLoadAttempts { get; set; } = 1; public int RewardedLoadRetryDelayMs { get; set; } = 500; public int RewardedShowTimeoutMs { get; set; } = 20000; public string RewardName { get; set; } = "reward"; public int RewardAmount { get; set; } = 1; - public bool InterstitialAutoLoad { get; set; } = true; + public bool InterstitialAutoLoad { get; set; } = false; public bool InterstitialPrewarmOnInit { get; set; } public int InterstitialMaxLoadAttempts { get; set; } = 1; public int InterstitialLoadRetryDelayMs { get; set; } = 500; public int InterstitialShowTimeoutMs { get; set; } = 20000; - public bool SplashAutoLoad { get; set; } = true; + public bool SplashAutoLoad { get; set; } = false; public bool SplashPrewarmOnInit { get; set; } public int SplashMaxLoadAttempts { get; set; } = 1; public int SplashLoadRetryDelayMs { get; set; } = 500; public int SplashShowTimeoutMs { get; set; } = 20000; + public bool SmartPreloadEnabled { get; set; } = false; + public string SmartPreloadConfigAssetPath { get; set; } = "TapadnSmartLoadPolicy_Default"; + public string SmartPreloadConfigJson { get; set; } + public string SmartPreloadRemoteConfigJson { get; set; } public int? ExpressWidth { get; set; } public int? ExpressHeight { get; set; } @@ -209,6 +217,10 @@ public sealed class TapadnControllerOptions SplashMaxLoadAttempts = incoming.SplashMaxLoadAttempts; SplashLoadRetryDelayMs = incoming.SplashLoadRetryDelayMs; SplashShowTimeoutMs = incoming.SplashShowTimeoutMs; + SmartPreloadEnabled = incoming.SmartPreloadEnabled; + SmartPreloadConfigAssetPath = incoming.SmartPreloadConfigAssetPath ?? SmartPreloadConfigAssetPath; + SmartPreloadConfigJson = incoming.SmartPreloadConfigJson; + SmartPreloadRemoteConfigJson = incoming.SmartPreloadRemoteConfigJson; ExpressWidth = incoming.ExpressWidth ?? ExpressWidth; ExpressHeight = incoming.ExpressHeight ?? ExpressHeight; } @@ -266,6 +278,10 @@ public sealed class TapadnControllerOptions SplashMaxLoadAttempts = GetInt(map, SplashMaxLoadAttemptsKey) ?? SplashMaxLoadAttempts; SplashLoadRetryDelayMs = GetInt(map, SplashLoadRetryDelayMsKey) ?? SplashLoadRetryDelayMs; SplashShowTimeoutMs = GetInt(map, SplashShowTimeoutMsKey) ?? SplashShowTimeoutMs; + SmartPreloadEnabled = GetBool(map, SmartPreloadEnabledKey) ?? SmartPreloadEnabled; + SmartPreloadConfigAssetPath = GetString(map, SmartPreloadConfigAssetPathKey) ?? SmartPreloadConfigAssetPath; + SmartPreloadConfigJson = GetString(map, SmartPreloadConfigJsonKey) ?? SmartPreloadConfigJson; + SmartPreloadRemoteConfigJson = GetString(map, SmartPreloadRemoteConfigJsonKey) ?? SmartPreloadRemoteConfigJson; ExpressWidth = GetInt(map, ExpressWidthKey) ?? ExpressWidth; ExpressHeight = GetInt(map, ExpressHeightKey) ?? ExpressHeight; } diff --git a/Tapadn_Adapter/Runtime/Scripts/TapadnInteractionPlayer.cs b/Tapadn_Adapter/Runtime/Scripts/TapadnInteractionPlayer.cs index 43bec6a..1d516c0 100644 --- a/Tapadn_Adapter/Runtime/Scripts/TapadnInteractionPlayer.cs +++ b/Tapadn_Adapter/Runtime/Scripts/TapadnInteractionPlayer.cs @@ -56,10 +56,12 @@ public sealed class TapadnInteractionPlayer : ADPlayer, IDirichletInterstitialAu } curState = 1; + TapadnSmartLoadOrchestrator.OnLoadStarted(AD_Type.Interaction, AdScene); _adNative.LoadInterstitialAd( TapadnAdRequestFactory.BuildInterstitial(Key, TapadnAdController.CurrentOptions), ad => { + TapadnSmartLoadOrchestrator.OnLoadResult(AD_Type.Interaction, AdScene, true); _loadedAd?.Destroy(); _loadedAd = ad; _loadedAd.Shown += OnManualShown; @@ -71,6 +73,7 @@ public sealed class TapadnInteractionPlayer : ADPlayer, IDirichletInterstitialAu }, error => { + TapadnSmartLoadOrchestrator.OnLoadResult(AD_Type.Interaction, AdScene, false); curState = 0; Debug.LogError($"[TapADN] Interstitial load failed. code={error.Code}, message={error.Message}"); }); @@ -103,6 +106,7 @@ public sealed class TapadnInteractionPlayer : ADPlayer, IDirichletInterstitialAu } _showSettled = true; + TapadnSmartLoadOrchestrator.OnShowError(AD_Type.Interaction, AdScene); curState = 0; Debug.LogError($"[TapADN] Interstitial show failed. code={error?.Code}, message={error?.Message}"); adListener.OnShowError(); @@ -110,6 +114,7 @@ public sealed class TapadnInteractionPlayer : ADPlayer, IDirichletInterstitialAu public void OnAdShow() { + TapadnSmartLoadOrchestrator.OnShowStart(AD_Type.Interaction, AdScene); NotifyShowStarted(); } @@ -129,6 +134,16 @@ public sealed class TapadnInteractionPlayer : ADPlayer, IDirichletInterstitialAu { } + public override void OnPlayRequestStarted() + { + TapadnSmartLoadOrchestrator.OnPlayRequestStarted(AD_Type.Interaction, AdScene, !UseAutoLoad() && IsReadly()); + } + + public override void EnterAdScenario(string scenario) + { + TapadnSmartLoadOrchestrator.OnEnterAdScenario(AD_Type.Interaction, scenario, Key); + } + private bool UseAutoLoad() { return TapadnAdController.CurrentOptions?.InterstitialAutoLoad ?? true; @@ -136,6 +151,7 @@ public sealed class TapadnInteractionPlayer : ADPlayer, IDirichletInterstitialAu private void OnManualShown() { + TapadnSmartLoadOrchestrator.OnShowStart(AD_Type.Interaction, AdScene); NotifyShowStarted(); } diff --git a/Tapadn_Adapter/Runtime/Scripts/TapadnSmartLoadOrchestrator.cs b/Tapadn_Adapter/Runtime/Scripts/TapadnSmartLoadOrchestrator.cs new file mode 100644 index 0000000..1e27c6d --- /dev/null +++ b/Tapadn_Adapter/Runtime/Scripts/TapadnSmartLoadOrchestrator.cs @@ -0,0 +1,981 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Runtime.ADAggregator; +using UnityEngine; + +// 智能预加载实验:基于“场景出现次数/该场景实际播放请求次数”进行置信决策, +// 并允许通过策略配置进行服务端默认值覆盖。 +public static class TapadnSmartLoadOrchestrator +{ + private const string StatsPrefsKey = "TapadnSmartLoadStats.v1"; + private const string DefaultScene = "__default__"; + private const string PolicyDefaultScene = "__default__"; + + private static TapadnSmartLoadConfig _runtimeConfig; + private static Dictionary _states = new Dictionary(StringComparer.Ordinal); + private static Dictionary _policies = new Dictionary(StringComparer.Ordinal); + private static Dictionary _cacheStates = new Dictionary(); + private static bool _initialized; + private static bool _enabled; + + public static bool IsEnabled => _enabled; + + public static string GetDebugStateDump() + { + if (!_initialized) + { + return "{}"; + } + + var lines = new List(); + foreach (var entry in _states.Values) + { + if (entry == null) + { + continue; + } + + var adType = SafeToAdType(entry.AdType); + if (!adType.HasValue) + { + continue; + } + + var policy = ResolvePolicy(adType.Value, entry.Scenario); + var prob = EstimateShowProbability(adType.Value, entry.Scenario); + lines.Add( + $"[{entry.AdType}]{entry.Scenario} e={entry.EnterCount} pReq={entry.PlayRequestCount} preReq={entry.PreloadRequestCount} preSuc={entry.PreloadSuccessCount} preFail={entry.PreloadFailureCount} showReq={entry.ShowRequestCount} showOk={entry.ShowStartCount} showFail={entry.ShowFailureCount} imm={entry.ImmediateHitCount} smartHit={entry.SmartCacheHitCount} smartUsed={entry.SmartPreloadConsumedCount} score={prob:F3} policy={policy?.PreloadThreshold:F2}"); + } + + return $"enabled={_enabled},states={lines.Count},policies={_policies.Count},cache={GetCacheDebugDump()},snapshot={string.Join(" ; ", lines)}"; + } + + public static string ExportSnapshotCsv() + { + if (!_initialized) + { + return GetSnapshotCsvHeader(); + } + + var sb = new StringBuilder(); + sb.AppendLine(GetSnapshotCsvHeader()); + foreach (var entry in _states.Values) + { + if (entry == null) + { + continue; + } + + sb.AppendLine(string.Join(",", + entry.AdType, + EncodeCsv(NormalizeScenario(entry.Scenario)), + entry.EnterCount, + entry.PlayRequestCount, + entry.PreloadRequestCount, + entry.PreloadSuccessCount, + entry.PreloadFailureCount, + entry.ShowRequestCount, + entry.ShowStartCount, + entry.ShowFailureCount, + entry.ImmediateHitCount, + entry.SmartCacheHitCount, + entry.SmartCacheCrossSceneHitCount, + entry.UnattributedCacheHitCount, + entry.SmartPreloadConsumedCount, + entry.SmartPreloadConsumedSameSceneCount, + entry.SmartPreloadConsumedOtherSceneCount, + entry.SmartPreloadShowFailureCount, + entry.SmartPreloadExpiredCount, + EncodeCsv(NormalizeScenario(entry.LastSmartPreloadConsumedByScene)), + entry.LastSmartPreloadConsumedUnix, + entry.LastPreloadUnix, + entry.LastUpdatedUnix, + EstimateShowProbability((AD_Type)entry.AdType, entry.Scenario))); + } + + return sb.ToString(); + } + + public static void ResetLearningState() + { + if (!_initialized) + { + return; + } + + _states.Clear(); + _cacheStates.Clear(); + PlayerPrefs.DeleteKey(StatsPrefsKey); + PlayerPrefs.Save(); + } + + public static void Initialize(TapadnControllerOptions options) + { + _runtimeConfig = BuildConfig(options); + _enabled = options?.SmartPreloadEnabled ?? false; + _initialized = true; + _states = new Dictionary(StringComparer.Ordinal); + _cacheStates = new Dictionary(); + LoadStates(); + EnsureDefaultPolicies(_runtimeConfig); + } + + public static void OnEnterAdScenario(AD_Type adType, string scenario, string _slotId) + { + if (!_initialized || !_enabled) + { + return; + } + + var normalizedScenario = NormalizeScenario(scenario); + var record = GetOrCreateState(adType, normalizedScenario); + record.EnterCount = Math.Max(0, record.EnterCount) + 1; + record.LastUpdatedUnix = GetNowUnixSeconds(); + record.ShowRequestCount = Math.Max(0, record.ShowRequestCount); + SaveStates(); + + TryPreload(adType, normalizedScenario); + } + + public static void OnPlayRequestStarted(AD_Type adType, string scenario, bool cacheReadyAtRequest) + { + if (!_initialized || !_enabled) + { + return; + } + + var normalizedScenario = NormalizeScenario(scenario); + var record = GetOrCreateState(adType, normalizedScenario); + record.PlayRequestCount = Math.Max(0, record.PlayRequestCount) + 1; + record.ShowRequestCount = Math.Max(0, record.ShowRequestCount) + 1; + record.LastUpdatedUnix = GetNowUnixSeconds(); + if (cacheReadyAtRequest) + { + MarkImmediateHit(adType, normalizedScenario); + } + else + { + MarkCacheExpiredIfStale(adType); + } + + SaveStates(); + } + + public static void OnLoadRequested(AD_Type adType, string scenario) + { + if (!_initialized || !_enabled) + { + return; + } + + PrepareSmartLoadRequest(adType, scenario); + } + + public static void OnLoadStarted(AD_Type adType, string scenario) + { + if (!_initialized || !_enabled) + { + return; + } + + BeginLoadRequest(adType, scenario); + SaveStates(); + } + + public static void OnLoadResult(AD_Type adType, string scenario, bool success) + { + if (!_initialized || !_enabled) + { + return; + } + + CompleteLoadRequest(adType, scenario, success); + SaveStates(); + } + + public static void OnShowStart(AD_Type adType, string scenario) + { + if (!_initialized || !_enabled) + { + return; + } + + var record = GetOrCreateState(adType, NormalizeScenario(scenario)); + record.ShowStartCount = Math.Max(0, record.ShowStartCount) + 1; + record.LastUpdatedUnix = GetNowUnixSeconds(); + MarkCacheConsumed(adType, scenario); + SaveStates(); + } + + public static void OnShowError(AD_Type adType, string scenario) + { + if (!_initialized || !_enabled) + { + return; + } + + var record = GetOrCreateState(adType, NormalizeScenario(scenario)); + record.ShowFailureCount = Math.Max(0, record.ShowFailureCount) + 1; + record.LastUpdatedUnix = GetNowUnixSeconds(); + MarkCacheShowFailed(adType, scenario); + SaveStates(); + } + + private static void TryPreload(AD_Type adType, string scenario) + { + if (!_initialized || !_enabled) + { + return; + } + + if (!ADManager.Instance.CheckNetwork()) + { + return; + } + + var policy = ResolvePolicy(adType, scenario); + if (policy == null) + { + return; + } + + if (!NeedPreload(adType, scenario, policy)) + { + return; + } + + var now = GetNowUnixSeconds(); + var state = GetOrCreateState(adType, scenario); + if (now - state.LastPreloadUnix < policy.CooldownSeconds) + { + return; + } + + if (ADManager.Instance.IsRealy(adType)) + { + return; + } + + MarkCacheExpiredIfStale(adType); + OnLoadRequested(adType, scenario); + ADManager.Instance.LoadAD(adType); + var smartLoadStarted = HasPendingSmartLoadForScene(adType, scenario); + ClearPreparedLoadIfNotStarted(adType); + if (!smartLoadStarted) + { + return; + } + + state.LastPreloadUnix = now; + state.LastUpdatedUnix = now; + SaveStates(); + Debug.Log($"[TapADN SmartLoad] Preload triggered: type={adType}, scenario={scenario}, score={EstimateShowProbability(adType, scenario):F3}"); + } + + private static bool NeedPreload(AD_Type adType, string scenario, TapadnSmartLoadPolicyItem policy) + { + var score = EstimateShowProbability(adType, scenario); + return score >= policy.PreloadThreshold; + } + + private static float EstimateShowProbability(AD_Type adType, string scenario) + { + var policy = ResolvePolicy(adType, scenario); + if (policy == null) + { + return 0f; + } + + var state = GetOrCreateState(adType, scenario); + if (state.EnterCount <= 0) + { + return policy.BaseProbability; + } + + var observedRate = Mathf.Clamp01(state.PlayRequestCount / (float)state.EnterCount); + var confidence = Mathf.Clamp01(state.EnterCount / (float)Mathf.Max(1, policy.MinSamplesForConfidence)); + var ageHours = Mathf.Max(0f, (GetNowUnixSeconds() - state.LastUpdatedUnix) / 3600f); + var decay = Mathf.Pow(0.5f, ageHours / Mathf.Max(1f, policy.DecayHalfLifeHours)); + var trust = Mathf.Clamp01(confidence * decay); + + return Mathf.Lerp(policy.BaseProbability, observedRate, trust); + } + + private static void PrepareSmartLoadRequest(AD_Type adType, string scenario) + { + var state = GetOrCreateCacheState(adType); + if (state.PendingSmartLoad) + { + return; + } + + state.PreparedSmartLoad = true; + state.PreparedOriginScene = NormalizeScenario(scenario); + state.PreparedOriginScore = EstimateShowProbability(adType, scenario); + state.PreparedRequestUnix = GetNowUnixSeconds(); + } + + private static void BeginLoadRequest(AD_Type adType, string scenario) + { + var state = GetOrCreateCacheState(adType); + var now = GetNowUnixSeconds(); + if (state.PreparedSmartLoad) + { + var originScene = NormalizeScenario(state.PreparedOriginScene); + var record = GetOrCreateState(adType, originScene); + record.PreloadRequestCount = Math.Max(0, record.PreloadRequestCount) + 1; + record.LastUpdatedUnix = now; + state.PendingSmartLoad = true; + state.PendingOriginScene = originScene; + state.PendingOriginScore = state.PreparedOriginScore; + state.PendingRequestUnix = now; + } + else + { + state.PendingSmartLoad = false; + state.PendingOriginScene = NormalizeScenario(scenario); + state.PendingOriginScore = EstimateShowProbability(adType, scenario); + state.PendingRequestUnix = now; + } + + state.HasReadyCache = false; + state.PreparedSmartLoad = false; + state.PreparedOriginScene = null; + state.PreparedOriginScore = 0f; + state.PreparedRequestUnix = 0; + } + + private static void ClearPreparedLoadIfNotStarted(AD_Type adType) + { + var state = GetOrCreateCacheState(adType); + if (state.PendingSmartLoad) + { + return; + } + + state.PreparedSmartLoad = false; + state.PreparedOriginScene = null; + state.PreparedOriginScore = 0f; + state.PreparedRequestUnix = 0; + } + + private static bool HasPendingSmartLoadForScene(AD_Type adType, string scenario) + { + var state = GetOrCreateCacheState(adType); + return state.PendingSmartLoad && + string.Equals(NormalizeScenario(state.PendingOriginScene), NormalizeScenario(scenario), StringComparison.Ordinal); + } + + private static void CompleteLoadRequest(AD_Type adType, string scenario, bool success) + { + var cacheState = GetOrCreateCacheState(adType); + var normalizedScenario = NormalizeScenario(scenario); + var originScene = cacheState.PendingSmartLoad ? NormalizeScenario(cacheState.PendingOriginScene) : normalizedScenario; + var originRecord = GetOrCreateState(adType, originScene); + + if (cacheState.PendingSmartLoad) + { + if (success) + { + originRecord.PreloadSuccessCount = Math.Max(0, originRecord.PreloadSuccessCount) + 1; + } + else + { + originRecord.PreloadFailureCount = Math.Max(0, originRecord.PreloadFailureCount) + 1; + } + } + + originRecord.LastUpdatedUnix = GetNowUnixSeconds(); + if (success) + { + cacheState.HasReadyCache = true; + cacheState.ReadyFromSmart = cacheState.PendingSmartLoad; + cacheState.ReadyOriginScene = originScene; + cacheState.ReadyOriginScore = cacheState.PendingSmartLoad ? cacheState.PendingOriginScore : EstimateShowProbability(adType, originScene); + cacheState.ReadyLoadedUnix = GetNowUnixSeconds(); + cacheState.ReadyRequestUnix = cacheState.PendingRequestUnix; + cacheState.Consumed = false; + } + else if (cacheState.PendingSmartLoad) + { + ClearReadyCache(cacheState); + } + + cacheState.PendingSmartLoad = false; + cacheState.PendingOriginScene = null; + cacheState.PendingOriginScore = 0f; + cacheState.PendingRequestUnix = 0; + } + + private static void MarkImmediateHit(AD_Type adType, string scenario) + { + var consumedScene = NormalizeScenario(scenario); + var currentSceneRecord = GetOrCreateState(adType, consumedScene); + currentSceneRecord.ImmediateHitCount = Math.Max(0, currentSceneRecord.ImmediateHitCount) + 1; + currentSceneRecord.LastUpdatedUnix = GetNowUnixSeconds(); + + var cacheState = GetOrCreateCacheState(adType); + if (!cacheState.HasReadyCache) + { + currentSceneRecord.UnattributedCacheHitCount = Math.Max(0, currentSceneRecord.UnattributedCacheHitCount) + 1; + } + } + + private static void MarkCacheConsumed(AD_Type adType, string scenario) + { + var cacheState = GetOrCreateCacheState(adType); + if (!cacheState.HasReadyCache || cacheState.Consumed) + { + return; + } + + var consumedScene = NormalizeScenario(scenario); + var originScene = NormalizeScenario(cacheState.ReadyOriginScene); + var currentSceneRecord = GetOrCreateState(adType, consumedScene); + var originSceneRecord = GetOrCreateState(adType, originScene); + + if (cacheState.ReadyFromSmart) + { + currentSceneRecord.SmartCacheHitCount = Math.Max(0, currentSceneRecord.SmartCacheHitCount) + 1; + originSceneRecord.SmartPreloadConsumedCount = Math.Max(0, originSceneRecord.SmartPreloadConsumedCount) + 1; + originSceneRecord.LastSmartPreloadConsumedByScene = consumedScene; + originSceneRecord.LastSmartPreloadConsumedUnix = GetNowUnixSeconds(); + + if (string.Equals(originScene, consumedScene, StringComparison.Ordinal)) + { + originSceneRecord.SmartPreloadConsumedSameSceneCount = Math.Max(0, originSceneRecord.SmartPreloadConsumedSameSceneCount) + 1; + } + else + { + currentSceneRecord.SmartCacheCrossSceneHitCount = Math.Max(0, currentSceneRecord.SmartCacheCrossSceneHitCount) + 1; + originSceneRecord.SmartPreloadConsumedOtherSceneCount = Math.Max(0, originSceneRecord.SmartPreloadConsumedOtherSceneCount) + 1; + } + } + currentSceneRecord.LastUpdatedUnix = GetNowUnixSeconds(); + originSceneRecord.LastUpdatedUnix = GetNowUnixSeconds(); + cacheState.Consumed = true; + ClearReadyCache(cacheState); + } + + private static void MarkCacheShowFailed(AD_Type adType, string scenario) + { + var cacheState = GetOrCreateCacheState(adType); + if (!cacheState.HasReadyCache) + { + return; + } + + var originScene = NormalizeScenario(cacheState.ReadyOriginScene); + if (cacheState.ReadyFromSmart) + { + var originSceneRecord = GetOrCreateState(adType, originScene); + originSceneRecord.SmartPreloadShowFailureCount = Math.Max(0, originSceneRecord.SmartPreloadShowFailureCount) + 1; + originSceneRecord.LastSmartPreloadConsumedByScene = NormalizeScenario(scenario); + originSceneRecord.LastSmartPreloadConsumedUnix = GetNowUnixSeconds(); + originSceneRecord.LastUpdatedUnix = GetNowUnixSeconds(); + } + + ClearReadyCache(cacheState); + } + + private static void MarkCacheExpiredIfStale(AD_Type adType) + { + var cacheState = GetOrCreateCacheState(adType); + if (!cacheState.HasReadyCache) + { + return; + } + + if (ADManager.Instance.IsRealy(adType)) + { + return; + } + + if (cacheState.ReadyFromSmart) + { + var originSceneRecord = GetOrCreateState(adType, NormalizeScenario(cacheState.ReadyOriginScene)); + originSceneRecord.SmartPreloadExpiredCount = Math.Max(0, originSceneRecord.SmartPreloadExpiredCount) + 1; + originSceneRecord.LastUpdatedUnix = GetNowUnixSeconds(); + } + + ClearReadyCache(cacheState); + } + + private static TapadnSmartLoadCacheState GetOrCreateCacheState(AD_Type adType) + { + var key = (int)adType; + if (_cacheStates.TryGetValue(key, out var state) && state != null) + { + return state; + } + + state = new TapadnSmartLoadCacheState(); + _cacheStates[key] = state; + return state; + } + + private static void ClearReadyCache(TapadnSmartLoadCacheState state) + { + if (state == null) + { + return; + } + + state.HasReadyCache = false; + state.ReadyFromSmart = false; + state.ReadyOriginScene = null; + state.ReadyOriginScore = 0f; + state.ReadyLoadedUnix = 0; + state.ReadyRequestUnix = 0; + state.Consumed = false; + } + + private static string GetCacheDebugDump() + { + if (_cacheStates == null || _cacheStates.Count == 0) + { + return "empty"; + } + + var lines = new List(); + foreach (var entry in _cacheStates) + { + var state = entry.Value; + if (state == null) + { + continue; + } + + lines.Add($"[{entry.Key}]prepared={state.PreparedSmartLoad}:{NormalizeScenario(state.PreparedOriginScene)} pending={state.PendingSmartLoad}:{NormalizeScenario(state.PendingOriginScene)} ready={state.HasReadyCache}:{state.ReadyFromSmart}:{NormalizeScenario(state.ReadyOriginScene)} score={state.ReadyOriginScore:F3}"); + } + + return string.Join(" | ", lines); + } + + private static string GetSnapshotCsvHeader() + { + return "ad_type,scenario,enter_count,play_request_count,preload_request,preload_success,preload_fail,show_request,show_start,show_fail,immediate_hit,smart_cache_hit,smart_cache_cross_scene_hit,unattributed_cache_hit,smart_preload_consumed,smart_preload_consumed_same_scene,smart_preload_consumed_other_scene,smart_preload_show_fail,smart_preload_expired,last_smart_preload_consumed_by_scene,last_smart_preload_consumed_unix,last_preload_unix,last_updated_unix,last_score"; + } + + private static TapadnSmartLoadPolicyItem ResolvePolicy(AD_Type adType, string scenario) + { + if (_runtimeConfig == null) + { + _runtimeConfig = BuildDefaultConfig(); + } + + var normalizedScenario = NormalizeScenario(scenario); + var exactKey = ComposeKey(adType, normalizedScenario); + if (_policies.TryGetValue(exactKey, out var exactPolicy)) + { + return exactPolicy; + } + + var defaultScenarioKey = ComposeKey(adType, PolicyDefaultScene); + if (_policies.TryGetValue(defaultScenarioKey, out var defaultPolicy)) + { + return defaultPolicy; + } + + return _runtimeConfig?.GlobalDefault != null ? _runtimeConfig.GlobalDefault : BuildDefaultConfig().GlobalDefault; + } + + private static void EnsureDefaultPolicies(TapadnSmartLoadConfig config) + { + _policies.Clear(); + if (config == null || config.ScenePolicies == null) + { + config = BuildDefaultConfig(); + } + + if (config.GlobalDefault == null) + { + config.GlobalDefault = BuildDefaultPolicy(); + } + + foreach (var policy in config.ScenePolicies) + { + if (policy == null || string.IsNullOrWhiteSpace(policy.Scene)) + { + continue; + } + + if (policy.AdType < 0 || policy.AdType > 2) + { + continue; + } + + var key = ComposeKey((AD_Type)policy.AdType, NormalizeScenario(policy.Scene)); + _policies[key] = new TapadnSmartLoadPolicyItem + { + AdType = policy.AdType, + Scene = NormalizeScenario(policy.Scene), + BaseProbability = Mathf.Clamp01(policy.BaseProbability), + PreloadThreshold = Mathf.Clamp(policy.PreloadThreshold, 0f, 1f), + CooldownSeconds = Math.Max(0, policy.CooldownSeconds), + MinSamplesForConfidence = Math.Max(1, policy.MinSamplesForConfidence), + DecayHalfLifeHours = Math.Max(1f, policy.DecayHalfLifeHours), + }; + } + + foreach (AD_Type adType in Enum.GetValues(typeof(AD_Type))) + { + var key = ComposeKey(adType, PolicyDefaultScene); + if (!_policies.ContainsKey(key)) + { + _policies[key] = ClonePolicy(config.GlobalDefault, adType); + } + } + } + + private static TapadnSmartLoadPolicyItem ClonePolicy(TapadnSmartLoadPolicyItem source, AD_Type adType) + { + if (source == null) + { + source = BuildDefaultPolicy(); + } + + return new TapadnSmartLoadPolicyItem + { + AdType = (int)adType, + Scene = PolicyDefaultScene, + BaseProbability = source.BaseProbability, + PreloadThreshold = source.PreloadThreshold, + CooldownSeconds = source.CooldownSeconds, + MinSamplesForConfidence = source.MinSamplesForConfidence, + DecayHalfLifeHours = source.DecayHalfLifeHours + }; + } + + private static TapadnSmartLoadConfig BuildConfig(TapadnControllerOptions options) + { + var config = BuildDefaultConfig(); + MergePolicyJson(config, ReadPolicyAssetJson(options)); + MergePolicyJson(config, options?.SmartPreloadConfigJson); + MergePolicyJson(config, options?.SmartPreloadRemoteConfigJson); + return config; + } + + private static string ReadPolicyAssetJson(TapadnControllerOptions options) + { + if (options == null || string.IsNullOrWhiteSpace(options.SmartPreloadConfigAssetPath)) + { + return null; + } + + try + { + var asset = Resources.Load(options.SmartPreloadConfigAssetPath); + return asset != null ? asset.text : null; + } + catch (Exception exception) + { + Debug.LogWarning($"[TapADN SmartLoad] Load policy asset failed: {exception.Message}"); + return null; + } + } + + private static TapadnSmartLoadConfig BuildDefaultConfig() + { + return new TapadnSmartLoadConfig + { + GlobalDefault = BuildDefaultPolicy(), + ScenePolicies = new List(), + }; + } + + private static TapadnSmartLoadPolicyItem BuildDefaultPolicy() + { + return new TapadnSmartLoadPolicyItem + { + AdType = -1, + Scene = PolicyDefaultScene, + BaseProbability = 0.08f, + PreloadThreshold = 0.75f, + CooldownSeconds = 120, + MinSamplesForConfidence = 8, + DecayHalfLifeHours = 72f + }; + } + + private static void MergePolicyJson(TapadnSmartLoadConfig target, string json) + { + if (string.IsNullOrWhiteSpace(json)) + { + return; + } + + try + { + var parsed = JsonUtility.FromJson(json); + if (parsed == null) + { + return; + } + + if (parsed.GlobalDefault != null) + { + target.GlobalDefault = NormalizePolicy(parsed.GlobalDefault); + } + + if (parsed.ScenePolicies == null || parsed.ScenePolicies.Count == 0) + { + return; + } + + if (target.ScenePolicies == null) + { + target.ScenePolicies = new List(); + } + + foreach (var policy in parsed.ScenePolicies) + { + if (policy == null) + { + continue; + } + + if (policy.AdType < 0 || policy.AdType > 2) + { + continue; + } + + if (string.IsNullOrWhiteSpace(policy.Scene)) + { + continue; + } + + target.ScenePolicies.Add(policy); + } + } + catch (Exception exception) + { + Debug.LogWarning($"[TapADN SmartLoad] Merge policy JSON failed: {exception.Message}"); + } + } + + private static string EncodeCsv(string value) + { + if (string.IsNullOrEmpty(value)) + { + return "0"; + } + + if (value.Contains(",") || value.Contains("\"") || value.Contains("\n")) + { + return $"\"{value.Replace("\"", "\"\"")}\""; + } + + return value; + } + + private static TapadnSmartLoadPolicyItem NormalizePolicy(TapadnSmartLoadPolicyItem policy) + { + return new TapadnSmartLoadPolicyItem + { + AdType = policy.AdType, + Scene = NormalizeScenario(policy.Scene), + BaseProbability = Mathf.Clamp01(policy.BaseProbability), + PreloadThreshold = Mathf.Clamp(policy.PreloadThreshold, 0f, 1f), + CooldownSeconds = Math.Max(0, policy.CooldownSeconds), + MinSamplesForConfidence = Math.Max(1, policy.MinSamplesForConfidence), + DecayHalfLifeHours = Math.Max(1f, policy.DecayHalfLifeHours), + }; + } + + private static TapadnSmartLoadSceneState GetOrCreateState(AD_Type adType, string scenario) + { + var key = ComposeKey(adType, NormalizeScenario(scenario)); + if (_states.TryGetValue(key, out var state)) + { + return state; + } + + state = new TapadnSmartLoadSceneState + { + AdType = (int)adType, + Scenario = NormalizeScenario(scenario), + EnterCount = 0, + PlayRequestCount = 0, + LastUpdatedUnix = GetNowUnixSeconds() + }; + _states[key] = state; + return state; + } + + private static void LoadStates() + { + try + { + var raw = PlayerPrefs.GetString(StatsPrefsKey, string.Empty); + if (string.IsNullOrWhiteSpace(raw)) + { + return; + } + + var data = JsonUtility.FromJson(raw); + if (data == null || data.Entries == null) + { + return; + } + + foreach (var entry in data.Entries) + { + if (entry == null || entry.AdType < 0) + { + continue; + } + + var adType = SafeToAdType(entry.AdType); + var scenario = NormalizeScenario(entry.Scenario); + if (adType == null) + { + continue; + } + + var key = ComposeKey(adType.Value, scenario); + _states[key] = entry; + } + } + catch (Exception exception) + { + Debug.LogWarning($"[TapADN SmartLoad] Load state failed: {exception.Message}"); + } + } + + private static AD_Type? SafeToAdType(int value) + { + if (value < 0 || value > 2) + { + return null; + } + + return (AD_Type)value; + } + + private static void SaveStates() + { + var data = new TapadnSmartLoadStateData(); + data.Entries = new List(); + + foreach (var entry in _states.Values) + { + data.Entries.Add(entry); + } + + var raw = JsonUtility.ToJson(data); + if (string.IsNullOrWhiteSpace(raw)) + { + return; + } + + PlayerPrefs.SetString(StatsPrefsKey, raw); + PlayerPrefs.Save(); + } + + private static string ComposeKey(AD_Type adType, string scenario) + { + return ((int)adType) + "|" + NormalizeScenario(scenario); + } + + private static string NormalizeScenario(string scenario) + { + if (string.IsNullOrWhiteSpace(scenario)) + { + return DefaultScene; + } + + return scenario.Trim(); + } + + private static long GetNowUnixSeconds() + { + return (long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds; + } + + [Serializable] + private sealed class TapadnSmartLoadConfig + { + public TapadnSmartLoadPolicyItem GlobalDefault; + public List ScenePolicies; + } + + [Serializable] + private sealed class TapadnSmartLoadPolicyItem + { + public int AdType; + public string Scene; + public float BaseProbability; + public float PreloadThreshold; + public int CooldownSeconds; + public int MinSamplesForConfidence; + public float DecayHalfLifeHours; + } + + [Serializable] + private sealed class TapadnSmartLoadPolicyItemDto + { + public int AdType; + public string Scene; + public float BaseProbability = 0.08f; + public float PreloadThreshold = 0.75f; + public int CooldownSeconds = 120; + public int MinSamplesForConfidence = 8; + public float DecayHalfLifeHours = 72f; + } + + [Serializable] + private sealed class TapadnSmartLoadStateData + { + public int Version = 1; + public List Entries; + } + + private sealed class TapadnSmartLoadCacheState + { + public bool PreparedSmartLoad; + public string PreparedOriginScene; + public float PreparedOriginScore; + public long PreparedRequestUnix; + public bool PendingSmartLoad; + public string PendingOriginScene; + public float PendingOriginScore; + public long PendingRequestUnix; + public bool HasReadyCache; + public bool ReadyFromSmart; + public string ReadyOriginScene; + public float ReadyOriginScore; + public long ReadyLoadedUnix; + public long ReadyRequestUnix; + public bool Consumed; + } + + [Serializable] + private sealed class TapadnSmartLoadSceneState + { + public int AdType; + public string Scenario; + public int EnterCount; + public int PlayRequestCount; + public int PreloadRequestCount; + public int PreloadSuccessCount; + public int PreloadFailureCount; + public int ShowRequestCount; + public int ShowStartCount; + public int ShowFailureCount; + public int ImmediateHitCount; + public int SmartCacheHitCount; + public int SmartCacheCrossSceneHitCount; + public int UnattributedCacheHitCount; + public int SmartPreloadConsumedCount; + public int SmartPreloadConsumedSameSceneCount; + public int SmartPreloadConsumedOtherSceneCount; + public int SmartPreloadShowFailureCount; + public int SmartPreloadExpiredCount; + public string LastSmartPreloadConsumedByScene; + public long LastSmartPreloadConsumedUnix; + public long LastUpdatedUnix; + public long LastPreloadUnix; + } +} diff --git a/Tapadn_Adapter/Runtime/Scripts/TapadnSmartLoadOrchestrator.cs.meta b/Tapadn_Adapter/Runtime/Scripts/TapadnSmartLoadOrchestrator.cs.meta new file mode 100644 index 0000000..aabb9db --- /dev/null +++ b/Tapadn_Adapter/Runtime/Scripts/TapadnSmartLoadOrchestrator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c383420c72bb4c1f90c0e88f0e250b5d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Tapadn_Adapter/Runtime/Scripts/TapadnSplashPlayer.cs b/Tapadn_Adapter/Runtime/Scripts/TapadnSplashPlayer.cs index 85c3b24..55e03c8 100644 --- a/Tapadn_Adapter/Runtime/Scripts/TapadnSplashPlayer.cs +++ b/Tapadn_Adapter/Runtime/Scripts/TapadnSplashPlayer.cs @@ -56,10 +56,12 @@ public sealed class TapadnSplashPlayer : ADPlayer, IDirichletSplashAutoAdListene } curState = 1; + TapadnSmartLoadOrchestrator.OnLoadStarted(AD_Type.Splash, AdScene); _adNative.LoadSplashAd( TapadnAdRequestFactory.BuildSplash(Key, TapadnAdController.CurrentOptions), ad => { + TapadnSmartLoadOrchestrator.OnLoadResult(AD_Type.Splash, AdScene, true); _loadedAd?.Destroy(); _loadedAd = ad; _loadedAd.Shown += OnManualShown; @@ -71,6 +73,7 @@ public sealed class TapadnSplashPlayer : ADPlayer, IDirichletSplashAutoAdListene }, error => { + TapadnSmartLoadOrchestrator.OnLoadResult(AD_Type.Splash, AdScene, false); curState = 0; Debug.LogError($"[TapADN] Splash load failed. code={error.Code}, message={error.Message}"); }); @@ -103,6 +106,7 @@ public sealed class TapadnSplashPlayer : ADPlayer, IDirichletSplashAutoAdListene } _showSettled = true; + TapadnSmartLoadOrchestrator.OnShowError(AD_Type.Splash, AdScene); curState = 0; Debug.LogError($"[TapADN] Splash show failed. code={error?.Code}, message={error?.Message}"); adListener.OnShowError(); @@ -110,6 +114,7 @@ public sealed class TapadnSplashPlayer : ADPlayer, IDirichletSplashAutoAdListene public void OnAdShow() { + TapadnSmartLoadOrchestrator.OnShowStart(AD_Type.Splash, AdScene); NotifyShowStarted(); } @@ -129,6 +134,16 @@ public sealed class TapadnSplashPlayer : ADPlayer, IDirichletSplashAutoAdListene { } + public override void OnPlayRequestStarted() + { + TapadnSmartLoadOrchestrator.OnPlayRequestStarted(AD_Type.Splash, AdScene, !UseAutoLoad() && IsReadly()); + } + + public override void EnterAdScenario(string scenario) + { + TapadnSmartLoadOrchestrator.OnEnterAdScenario(AD_Type.Splash, scenario, Key); + } + private bool UseAutoLoad() { return TapadnAdController.CurrentOptions?.SplashAutoLoad ?? true; @@ -136,6 +151,7 @@ public sealed class TapadnSplashPlayer : ADPlayer, IDirichletSplashAutoAdListene private void OnManualShown() { + TapadnSmartLoadOrchestrator.OnShowStart(AD_Type.Splash, AdScene); NotifyShowStarted(); }