Files
Commercialization.tapadn/Tapadn_Adapter/Runtime/Scripts/TapadnAwardVideoPlayer.cs
2026-06-17 15:40:25 +08:00

384 lines
12 KiB
C#

using System;
using System.Collections.Generic;
using Dirichlet.Mediation;
using Runtime.ADAggregator;
using UnityEngine;
public sealed class TapadnAwardVideoPlayer : ADPlayer, IDirichletRewardVideoAutoAdListener
{
private const float RewardCloseSettleDelaySeconds = 0.25f;
private readonly Dictionary<string, RewardedSlotCache> _slotCaches = new Dictionary<string, RewardedSlotCache>(StringComparer.Ordinal);
private DirichletAdNative _adNative;
private string _defaultSlotId;
private string _activeSlotId;
private bool _rewardVerified;
private bool _rewardVerifyReceived;
private bool _closePendingRewardVerify;
private bool _showSettled;
private AdTimeHandler _rewardCloseSettleHandler;
public override int MaxLoadAttempts => TapadnAdController.CurrentOptions?.RewardedMaxLoadAttempts ?? base.MaxLoadAttempts;
public override float LoadRetryDelaySeconds => Math.Max(0f, (TapadnAdController.CurrentOptions?.RewardedLoadRetryDelayMs ?? 500) / 1000f);
public override float ShowPendingTimeoutSeconds => Math.Max(1f, (TapadnAdController.CurrentOptions?.RewardedShowTimeoutMs ?? 20000) / 1000f);
public override bool AutoPreloadOnInit => TapadnAdController.CurrentOptions?.RewardedPrewarmOnInit ?? false;
public override void OnInit()
{
_defaultSlotId = Key;
_adNative = DirichletAdManager.CreateAdNative();
}
public override bool IsReadly()
{
var slotId = ResolveCurrentSlotId();
if (UseAutoLoad())
{
return TapadnAdRequestFactory.TryParseSlotId(slotId, out _);
}
var cache = GetCache(slotId);
if (IsCacheReady(cache))
{
curState = 2;
return true;
}
if (cache != null && cache.Loading)
{
curState = 1;
return false;
}
if (cache != null)
{
RemoveCache(slotId);
}
curState = 0;
return false;
}
public override void LoadAD()
{
var slotId = ResolveCurrentSlotId();
if (!TapadnAdRequestFactory.TryParseSlotId(slotId, out _))
{
Debug.LogError($"[TapADN] Invalid rewarded slot id: {slotId}");
curState = 0;
return;
}
if (UseAutoLoad())
{
curState = 2;
try
{
_adNative.PreLoad(TapadnAdRequestFactory.BuildRewarded(slotId, TapadnAdController.CurrentOptions), 3);
}
catch (Exception exception)
{
Debug.LogWarning($"[TapADN] Rewarded preload skipped: {exception.Message}");
}
return;
}
var cache = GetCache(slotId);
if (cache != null && (cache.Loading || IsCacheReady(cache)))
{
curState = cache.Loading ? 1 : 2;
return;
}
cache = new RewardedSlotCache { Loading = true };
_slotCaches[slotId] = cache;
curState = 1;
TapadnSmartLoadOrchestrator.OnLoadStarted(AD_Type.AwardVideo, AdScene, slotId);
_adNative.LoadRewardVideoAd(
TapadnAdRequestFactory.BuildRewarded(slotId, TapadnAdController.CurrentOptions),
ad =>
{
TapadnSmartLoadOrchestrator.OnLoadResult(AD_Type.AwardVideo, AdScene, true, slotId);
DestroyCachedAd(cache);
cache.Ad = ad;
cache.Loading = false;
cache.LoadedUnix = GetNowUnixSeconds();
RegisterManualEvents(slotId, cache.Ad);
ScheduleCacheExpiration(slotId, cache);
curState = string.Equals(slotId, ResolveCurrentSlotId(), StringComparison.Ordinal) ? 2 : curState;
Debug.Log($"[TapADN] Rewarded loaded. scene={NormalizeScenario(AdScene)}, slot={slotId}");
},
error =>
{
TapadnSmartLoadOrchestrator.OnLoadResult(AD_Type.AwardVideo, AdScene, false, slotId);
RemoveCache(slotId);
curState = string.Equals(slotId, ResolveCurrentSlotId(), StringComparison.Ordinal) ? 0 : curState;
Debug.LogError($"[TapADN] Rewarded load failed. slot={slotId}, code={error.Code}, message={error.Message}");
});
}
public override void ShowAD(Action onClose, Action<bool> onVideoComplete)
{
adListener.onClose = onClose;
adListener.onVideoComplete = onVideoComplete;
_rewardVerified = false;
_rewardVerifyReceived = false;
_closePendingRewardVerify = false;
_showSettled = false;
_rewardCloseSettleHandler?.Kill();
_rewardCloseSettleHandler = null;
_activeSlotId = ResolveCurrentSlotId();
Key = _activeSlotId;
curState = 0;
if (UseAutoLoad())
{
_adNative.ShowRewardVideoAutoAd(TapadnAdRequestFactory.BuildRewarded(_activeSlotId, TapadnAdController.CurrentOptions), this);
return;
}
var cache = GetCache(_activeSlotId);
if (!IsCacheReady(cache) || cache.Ad == null || !cache.Ad.Show())
{
RemoveCache(_activeSlotId);
OnError(new DirichletError("show_failed", "ShowRewardVideoAd returned false"));
return;
}
}
public void OnError(DirichletError error)
{
if (_showSettled)
{
return;
}
_showSettled = true;
_rewardCloseSettleHandler?.Kill();
_rewardCloseSettleHandler = null;
curState = 0;
TapadnSmartLoadOrchestrator.OnShowError(AD_Type.AwardVideo, AdScene, _activeSlotId);
Debug.LogError($"[TapADN] Rewarded show failed. slot={_activeSlotId}, code={error?.Code}, message={error?.Message}");
adListener.OnShowError();
}
public void OnAdShow()
{
TapadnSmartLoadOrchestrator.OnShowStart(AD_Type.AwardVideo, AdScene, _activeSlotId);
NotifyShowStarted();
}
public void OnAdClose()
{
if (_showSettled)
{
return;
}
if (_rewardVerifyReceived)
{
CompleteRewardedClose();
return;
}
_closePendingRewardVerify = true;
_rewardCloseSettleHandler?.Kill();
_rewardCloseSettleHandler = ADManager.Instance.CreateTimer(RewardCloseSettleDelaySeconds, CompleteRewardedClose);
}
public void OnRewardVerify(DirichletRewardVerificationEventArgs args)
{
_rewardVerifyReceived = true;
_rewardVerified = args != null && args.IsVerified;
if (_closePendingRewardVerify)
{
CompleteRewardedClose();
}
}
public void OnAdClick()
{
}
public override void OnPlayRequestStarted()
{
var slotId = ResolveCurrentSlotId();
Key = slotId;
TapadnSmartLoadOrchestrator.OnPlayRequestStarted(AD_Type.AwardVideo, AdScene, !UseAutoLoad() && IsReadly(), slotId);
}
public override void EnterAdScenario(string scenario)
{
AdScene = NormalizeScenario(scenario);
var slotId = ResolveCurrentSlotId();
Key = slotId;
TapadnSmartLoadOrchestrator.OnEnterAdScenario(AD_Type.AwardVideo, AdScene, slotId);
}
private string ResolveCurrentSlotId()
{
var slotId = TapadnAdController.CurrentOptions?.ResolveRewardedSlotId(_defaultSlotId, AdScene) ?? _defaultSlotId;
return string.IsNullOrWhiteSpace(slotId) ? _defaultSlotId : slotId.Trim();
}
private bool UseAutoLoad()
{
return TapadnAdController.CurrentOptions?.RewardedAutoLoad ?? true;
}
private RewardedSlotCache GetCache(string slotId)
{
return !string.IsNullOrWhiteSpace(slotId) && _slotCaches.TryGetValue(slotId, out var cache) ? cache : null;
}
private bool IsCacheReady(RewardedSlotCache cache)
{
if (cache == null || cache.Loading || cache.Ad == null || !cache.Ad.IsLoaded || !cache.Ad.IsValid)
{
return false;
}
var maxAgeSeconds = TapadnAdController.CurrentOptions?.RewardedCacheMaxAgeSeconds ?? 0;
return maxAgeSeconds <= 0 || GetNowUnixSeconds() - cache.LoadedUnix <= maxAgeSeconds;
}
private void RegisterManualEvents(string slotId, DirichletRewardVideoAd ad)
{
if (ad == null)
{
return;
}
ad.Shown += () => OnManualShown(slotId);
ad.Clicked += OnManualClicked;
ad.ShowFailed += error => OnManualShowFailed(slotId, error);
ad.RewardVerified += OnManualRewardVerify;
ad.Closed += () => OnManualClosed(slotId);
}
private void ScheduleCacheExpiration(string slotId, RewardedSlotCache cache)
{
cache.ExpireHandler?.Kill();
cache.ExpireHandler = null;
var maxAgeSeconds = TapadnAdController.CurrentOptions?.RewardedCacheMaxAgeSeconds ?? 600;
if (maxAgeSeconds <= 0)
{
return;
}
cache.ExpireHandler = ADManager.Instance.CreateTimer(maxAgeSeconds, () =>
{
if (!ReferenceEquals(GetCache(slotId), cache))
{
return;
}
Debug.Log($"[TapADN] Rewarded cache expired. slot={slotId}, maxAgeSeconds={maxAgeSeconds}");
RemoveCache(slotId);
if (string.Equals(slotId, ResolveCurrentSlotId(), StringComparison.Ordinal))
{
curState = 0;
}
});
}
private void OnManualShown(string slotId)
{
_activeSlotId = slotId;
OnAdShow();
}
private void OnManualClicked()
{
}
private void OnManualShowFailed(string slotId, DirichletError error)
{
RemoveCache(slotId);
OnError(error);
}
private void OnManualRewardVerify(DirichletRewardVerificationEventArgs args)
{
OnRewardVerify(args);
}
private void OnManualClosed(string slotId)
{
RemoveCache(slotId);
OnAdClose();
}
private void RemoveCache(string slotId)
{
var cache = GetCache(slotId);
if (cache == null)
{
return;
}
DestroyCachedAd(cache);
_slotCaches.Remove(slotId);
}
private static void DestroyCachedAd(RewardedSlotCache cache)
{
if (cache?.Ad == null)
{
return;
}
try
{
cache.ExpireHandler?.Kill();
cache.ExpireHandler = null;
cache.Ad.Destroy();
}
catch (Exception exception)
{
Debug.LogWarning($"[TapADN] Rewarded Destroy failed: {exception.Message}");
}
finally
{
cache.Ad = null;
cache.Loading = false;
cache.LoadedUnix = 0;
}
}
private void CompleteRewardedClose()
{
if (_showSettled)
{
return;
}
_showSettled = true;
_closePendingRewardVerify = false;
_rewardCloseSettleHandler?.Kill();
_rewardCloseSettleHandler = null;
adListener.OnRewardVerify(_rewardVerified, TapadnAdController.CurrentOptions?.RewardAmount ?? 1, TapadnAdController.CurrentOptions?.RewardName ?? string.Empty);
adListener.OnAdClose();
}
private static string NormalizeScenario(string scenario)
{
return string.IsNullOrWhiteSpace(scenario) ? "__default__" : scenario.Trim();
}
private static long GetNowUnixSeconds()
{
return (long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds;
}
private sealed class RewardedSlotCache
{
public DirichletRewardVideoAd Ad;
public bool Loading;
public long LoadedUnix;
public AdTimeHandler ExpireHandler;
}
}