You've already forked Commercialization.tapadn
Add TapADN smart preload attribution
This commit is contained in:
@@ -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(
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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<string, TapadnSmartLoadSceneState> _states = new Dictionary<string, TapadnSmartLoadSceneState>(StringComparer.Ordinal);
|
||||
private static Dictionary<string, TapadnSmartLoadPolicyItem> _policies = new Dictionary<string, TapadnSmartLoadPolicyItem>(StringComparer.Ordinal);
|
||||
private static Dictionary<int, TapadnSmartLoadCacheState> _cacheStates = new Dictionary<int, TapadnSmartLoadCacheState>();
|
||||
private static bool _initialized;
|
||||
private static bool _enabled;
|
||||
|
||||
public static bool IsEnabled => _enabled;
|
||||
|
||||
public static string GetDebugStateDump()
|
||||
{
|
||||
if (!_initialized)
|
||||
{
|
||||
return "{}";
|
||||
}
|
||||
|
||||
var lines = new List<string>();
|
||||
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<string, TapadnSmartLoadSceneState>(StringComparer.Ordinal);
|
||||
_cacheStates = new Dictionary<int, TapadnSmartLoadCacheState>();
|
||||
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<string>();
|
||||
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<TextAsset>(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<TapadnSmartLoadPolicyItemDto>(),
|
||||
};
|
||||
}
|
||||
|
||||
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<TapadnSmartLoadConfig>(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<TapadnSmartLoadPolicyItemDto>();
|
||||
}
|
||||
|
||||
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<TapadnSmartLoadStateData>(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<TapadnSmartLoadSceneState>();
|
||||
|
||||
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<TapadnSmartLoadPolicyItemDto> 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<TapadnSmartLoadSceneState> 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c383420c72bb4c1f90c0e88f0e250b5d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user