5 Commits
1.0.3 ... 1.0.8

31 changed files with 797 additions and 162 deletions

View File

@@ -1,3 +1,46 @@
# [1.0.8]
### 修复
* 修复 Editor 广告位诊断在编译期触发 `CS0165` 未赋值变量错误的问题。
# [1.0.7]
### 修复
* 接入 `CC-Framework.Commercialization` 的 Editor 诊断接口Unity Editor 模拟点击广告时也会输出 TapADN 当前解析出的广告位 ID。
# [1.0.6]
### 修复
* 兼容 `CommonKeyValues` 中直接使用业务场景名作为 key、SpaceId 作为 value 的激励视频广告位配置,避免只识别 `tapadn.rewarded_scene_slot.<scene_id>` 时回退到默认激励广告位。
* 激励视频进入场景、加载和播放请求会输出当前解析到的 SpaceId 及来源,便于 Editor Console 和样例调试面板确认实际使用的广告位。
# [1.0.5]
### 调整
* Android native 聚合 SDK AAR 升级到 `4.2.7.3`
* Android Pangle 依赖升级到 `7.6.1.2`GDT 依赖升级到 `4.690.1560`,改由 Gradle 后处理注入 Maven 依赖。
* 移除旧本地 Pangle/GDT AAR避免与 Maven 依赖重复类。
### 文档
* 新增 `SDK_MAINTENANCE.md`记录官方源码改动清单、封装层职责、SDK 升级步骤和发布流程。
# [1.0.4]
### 新增
* 激励视频支持按游戏场景映射不同 TapADN SpaceId并在未配置或配置非法时回退默认激励广告位。
* 手动加载模式下激励视频缓存按 SpaceId 隔离,避免不同场景广告位串用 ready 缓存。
* 激励视频缓存默认 10 分钟未消费自动销毁,可通过 `tapadn.rewarded_cache_max_age_seconds` 覆盖。
### 调整
* 智能预加载归因缓存增加 SpaceId 维度,避免多广告位场景下 ready/归因状态混用。
# [1.0.3] # [1.0.3]
### 修复 ### 修复

View File

@@ -23,6 +23,8 @@ namespace Dirichlet.Mediation.Editor
private const string DIRICHLET_DEPS_END = "// Dirichlet Mediation Dependencies End"; private const string DIRICHLET_DEPS_END = "// Dirichlet Mediation Dependencies End";
private const string DIRICHLET_REPOS_START = "// Dirichlet Mediation Repositories Start"; private const string DIRICHLET_REPOS_START = "// Dirichlet Mediation Repositories Start";
private const string DIRICHLET_REPOS_END = "// Dirichlet Mediation Repositories End"; private const string DIRICHLET_REPOS_END = "// Dirichlet Mediation Repositories End";
private const string PangleSdkVersion = "7.6.1.2";
private const string GdtSdkVersion = "4.690.1560";
public int callbackOrder => 100; // Run after EDM4U (which uses lower values) public int callbackOrder => 100; // Run after EDM4U (which uses lower values)
@@ -157,6 +159,16 @@ namespace Dirichlet.Mediation.Editor
} }
// Maven dependencies (required for SDK functionality) // Maven dependencies (required for SDK functionality)
if (enableCsj)
{
depsBlock.AppendLine($" implementation('com.pangle.cn:ads-sdk-pro:{PangleSdkVersion}') {{");
depsBlock.AppendLine(" exclude group: 'com.android.support'");
depsBlock.AppendLine(" }");
}
if (enableGdt)
{
depsBlock.AppendLine($" implementation 'com.qq.e.union:union:{GdtSdkVersion}'");
}
depsBlock.AppendLine(" implementation 'com.android.support:recyclerview-v7:28.0.0'"); depsBlock.AppendLine(" implementation 'com.android.support:recyclerview-v7:28.0.0'");
depsBlock.AppendLine(" implementation 'com.github.bumptech.glide:glide:4.9.0'"); depsBlock.AppendLine(" implementation 'com.github.bumptech.glide:glide:4.9.0'");
depsBlock.AppendLine(" implementation 'com.android.support:support-v4:28.0.0'"); depsBlock.AppendLine(" implementation 'com.android.support:support-v4:28.0.0'");

View File

@@ -1,33 +0,0 @@
fileFormatVersion: 2
guid: 90b3b17e0788494398662e729c344ee2
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Android: Android
second:
enabled: 1
settings: {}
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,33 +0,0 @@
fileFormatVersion: 2
guid: 930136242e574a2b89110e6a25e49065
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Android: Android
second:
enabled: 1
settings: {}
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
userData:
assetBundleName:
assetBundleVariant:

115
Assets/SDK_MAINTENANCE.md Normal file
View File

@@ -0,0 +1,115 @@
# TapADN SDK 封装维护说明
本文记录 `Commercialization.tapadn` 对官方 Dirichlet/TapADN 聚合 SDK 的封装、改动和升级注意事项。维护或升级 SDK 时先读本文,再改文件。
## 当前 SDK 版本
官方链接:
* 资源下载页https://ssp.dirichlet.cn/docs/resource-download/
* 聚合 Unity 接入文档https://ssp.dirichlet.cn/docs/dirichlet-mediation-sdk/dirichlet-mediation-sdk-guide-unity/
* 聚合 Android 接入文档https://ssp.dirichlet.cn/docs/dirichlet-mediation-sdk/dirichlet-mediation-sdk-guide-android/
| 范围 | 当前版本 | 来源 | 说明 |
| --- | --- | --- | --- |
| Unity 聚合 SDK | 4.2.5.0 | 官方 `dirichlet_mediation_unity_4.2.5.0.unitypackage` | 官方资源页当前仍只提供 Unity 4.2.5.0。 |
| Android 聚合 native AAR | 4.2.7.3 | 官方 `dirichlet_ad_mediation_4.2.7.3.zip` | 已单独升级 Android native 侧。 |
| Android Pangle | 7.6.1.2 | 官方 Android 聚合接入文档 | 通过 Gradle 后处理注入 Maven 依赖。 |
| Android GDT | 4.690.1560 | 官方 Android 聚合接入文档 | 通过 Gradle 后处理注入 Maven 依赖。 |
| iOS 聚合 SDK Pod | 4.2.0.1 | 官方 iOS 聚合版本 | 默认值在 `DirichletMediationIOSPostProcessor`,可用环境变量覆盖。 |
## 模块结构
| 路径 | 归属 | 维护说明 |
| --- | --- | --- |
| `Assets/DirichletMediation` | 官方 Unity SDK 基础层 | 不再视为纯官方源码,部分文件已有本地改动。 |
| `Assets/Plugins/Android/DirichletMediation` | 官方 Android Unity bridge + native AAR | Java bridge 当前与官方 Unity 包一致AAR 已升级到 Android native 4.2.7.3。 |
| `Assets/Plugins/Android/libs` | Android 本地依赖 | 保留 `iadsdk-release-2.3.102.110.aar`Pangle/GDT 不再放本地 AAR改由 Maven 注入。 |
| `Assets/Tapadn_Adapter` | 本模块封装层 | 对接 `CC-Framework.Commercialization`,不要让业务层直接调用官方 SDK。 |
| `Assets/Samples~` | 可选调试样例 | 不是官方 sample是本模块验证 `ADManager` 流程的样例。 |
## 官方源码改动清单
以下文件来自官方 Unity SDK但本仓库已经修改过。升级官方 Unity SDK 时不能直接覆盖,必须三方合并:
| 文件 | 本地改动目的 |
| --- | --- |
| `Assets/DirichletMediation/Runtime/DirichletAdTypes.cs` | 补齐 Unity 侧 ad handle、auto-ad fallback、事件会话保活、`IsValid` / `Destroy` 包装等桥接行为。 |
| `Assets/DirichletMediation/Runtime/DirichletMediationSdk.cs` | 增强 Android/iOS/noop bridge、Unity 线程派发、iOS load/init callback receiver 和平台兼容逻辑。 |
| `Assets/DirichletMediation/Editor/DirichletGradlePostProcessor.cs` | 改为适配 Unity 导出的 Gradle 工程注入仓库、support/glide/okhttp、Pangle/GDT Maven 依赖,避免本地 AAR 重复类。 |
| `Assets/DirichletMediation/Editor/DirichletMediationIOSPostProcessor.cs` | 自动生成 Podfile处理 UnityFramework target、framework/header search paths、SKAdNetwork、ATT、GDT 动态 framework 嵌入和 CocoaPods 异常兜底。 |
以下官方文件当前与官方 Unity 4.2.5.0 包一致,升级时可优先直接替换后验证:
| 文件 | 说明 |
| --- | --- |
| `Assets/DirichletMediation/Editor/DirichletMediationDependencies.xml` | EDM4U 依赖声明。 |
| `Assets/Plugins/Android/DirichletMediation/src/main/java/com/dirichlet/unity/DirichletUnityBridge.java` | Android Java bridge。 |
| `Assets/Plugins/Android/AndroidManifest.xml` | 官方基础 Manifest。 |
| `Assets/Plugins/Android/proguard-user.txt` | 当前为空文件。 |
## 封装层做了什么
`Tapadn_Adapter` 的目标是让业务项目只面对 `ADManager` / `ADConfig` / `AD_Type`
1. `TapadnAdController``ADConfig``CommonKeyValues` 解析媒体 ID、MediaKey、广告位、渠道、debug、权限、预加载和展示超时配置。
2. `TapadnCommercialization` 提供创建 controller、初始化 `ADManager`、创建默认 `ADConfig`、批量写入激励场景广告位映射的便捷入口。
3. `TapadnAdRequestFactory` 统一构建 `DirichletAdRequest`,保证 SpaceId 是正整数,并注入 UserId、奖励名、奖励数量、展示尺寸。
4. `TapadnAwardVideoPlayer` 对接激励视频。手动 load/show 模式下,按 SpaceId 隔离缓存;展示关闭、展示失败、`IsValid == false`、或默认 600 秒未消费都会销毁缓存。
5. `TapadnInteractionPlayer` 对接插屏;`TapadnSplashPlayer` 对接开屏。两者保持单默认广告位语义。
6. `TapadnSmartLoadOrchestrator` 维护场景学习数据和智能预加载归因。当前是保守策略:默认不开启;开启后先观察用户点击广告的概率,达到阈值才自动预加载。
## 场景广告位规则
激励视频支持游戏场景映射不同 SpaceId
| 配置 key | 示例 |
| --- | --- |
| `tapadn.rewarded_scene_slot.<scene_id>` | `tapadn.rewarded_scene_slot.level_clear = 200101` |
| `tapadn.rewarded_scene_slots` | `level_clear=200101,daily_bonus=200102` |
| `tapadn.rewarded_scene_slots_json` | `{ "Mappings": [ { "Scene": "level_clear", "SlotId": "200101" } ] }` |
回退规则:
1. 未传场景、场景为空、场景未配置、配置 SpaceId 非法时,使用 `BaseAwardAdKeyValue.value`
2. 如果两个场景配置到同一个 SpaceId它们共享同一个广告位缓存。
3. 如果 A 场景 SpaceId 已加载但没展示,用户去 B 场景展示 B SpaceIdA 缓存不会被 B 消费A 会等回到 A 时复用,或超时/失效后销毁。
## Android 升级步骤
1. 查官方资源页和聚合 Android 发布记录,确认最新 Android 聚合版本、Pangle 版本、GDT 版本。
2. 下载 `dirichlet_ad_mediation_<version>.zip`,校验官方 MD5。
3. 替换 `Assets/Plugins/Android/DirichletMediation/libs/DirichletAD_*_<version>.aar`
4. 检查官方 Android 接入文档中的 Maven 依赖,更新 `DirichletGradlePostProcessor` 里的 `PangleSdkVersion``GdtSdkVersion`
5. 不要同时保留 Pangle/GDT 本地 AAR 和 Maven 依赖,否则容易出现 duplicate classes。当前策略是 Pangle/GDT 走 Maven。
6. 搜索旧版本号,更新 `GLOBAL_DESIGN.md``README.md``CHANGELOG.md``Assets/package.json`
7. 至少执行 `dotnet build Commercialization.tapadn.sln --no-restore``git diff --check`
8. Android 真机构建仍需在消费项目里跑项目自带构建流程验证 Manifest merge、Gradle dependency resolution 和广告展示回调。
## Unity SDK 升级步骤
1. 下载官方新的 `dirichlet_mediation_unity_<version>.unitypackage` 并校验 MD5。
2. 解包到临时目录,对比 `Assets/DirichletMediation``Assets/Plugins/Android`
3. 对“官方源码改动清单”里的 4 个文件做三方合并,不允许直接覆盖。
4. 官方 sample 不进入默认包;如需要保留调试能力,继续放到 `Samples~`
5. 检查 Android Java bridge API 是否和当前 C# bridge 匹配,重点是 load/show/destroy/isValid/auto-ad callback。
6. 检查 iOS Pod 名称和版本是否变化,同步 `DirichletMediationIOSPostProcessor` 的默认版本和 search path 规则。
7. 重新验证 Android/iOS editor compile真机展示验证至少覆盖初始化、激励 load/show/close/reward、插屏、开屏、无填充失败。
## 发布步骤
包根是 `Assets/package.json`,消费者应使用:
```json
"com.commercialization.tapadn": "http://private.lightyears.ltd:18650/foldcc/Commercialization.tapadn.git?path=/Assets#<version>"
```
发布命令:
```bash
git subtree split --prefix=Assets --branch upm
git tag <version>
git push origin master upm <version>
```
注意:当前版本 tag 打在完整仓库 `master` 上,因此消费者必须带 `?path=/Assets`

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: dcf571baef764df0aa6789990d47c36a
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -162,6 +162,7 @@ public sealed class TapadnIAAAdDebugSampleGui : MonoBehaviour
GUILayout.Label($"Last EventLog: {_lastEventLog}", _textStyle); GUILayout.Label($"Last EventLog: {_lastEventLog}", _textStyle);
GUILayout.Label($"Last SDK Version: {DisplayValue(TapadnAdController.LastSdkVersion)}", _textStyle); GUILayout.Label($"Last SDK Version: {DisplayValue(TapadnAdController.LastSdkVersion)}", _textStyle);
GUILayout.Label($"Last Init Error: {DisplayValue(TapadnAdController.LastInitError)}", _textStyle); GUILayout.Label($"Last Init Error: {DisplayValue(TapadnAdController.LastInitError)}", _textStyle);
GUILayout.Label($"Rewarded Selected SpaceId: {GetResolvedRewardedSlotForDisplay(rewardedScenario)}", _textStyle);
} }
else else
{ {
@@ -250,6 +251,7 @@ public sealed class TapadnIAAAdDebugSampleGui : MonoBehaviour
if (EnsureInitialized(label)) if (EnsureInitialized(label))
{ {
LogRewardedResolvedSlot(adType, scenario, "Enter scenario");
ADManager.Instance.EnterAdScenario(adType, scenario); ADManager.Instance.EnterAdScenario(adType, scenario);
AppendLog($"Enter scenario requested. type={adType}, scenario={scenario}"); AppendLog($"Enter scenario requested. type={adType}, scenario={scenario}");
} }
@@ -278,6 +280,7 @@ public sealed class TapadnIAAAdDebugSampleGui : MonoBehaviour
if (EnsureInitialized(label)) if (EnsureInitialized(label))
{ {
LogRewardedResolvedSlot(adType, scenario, "AsyncPlayAD");
ADManager.Instance.AsyncPlayAD(adType, scenario, result => ADManager.Instance.AsyncPlayAD(adType, scenario, result =>
{ {
AppendLog($"{adType} callback: {result}"); AppendLog($"{adType} callback: {result}");
@@ -424,6 +427,30 @@ public sealed class TapadnIAAAdDebugSampleGui : MonoBehaviour
} }
AppendLog($"Options => mediaId={options.MediaId}, channel={options.Channel}, sub={options.SubChannel}, debug={options.Debug}, rewardAuto={options.RewardedAutoLoad}, interAuto={options.InterstitialAutoLoad}, splashAuto={options.SplashAutoLoad}"); AppendLog($"Options => mediaId={options.MediaId}, channel={options.Channel}, sub={options.SubChannel}, debug={options.Debug}, rewardAuto={options.RewardedAutoLoad}, interAuto={options.InterstitialAutoLoad}, splashAuto={options.SplashAutoLoad}");
AppendLog($"Rewarded selected slot => {GetResolvedRewardedSlotForDisplay(rewardedScenario)}, configuredSceneSlots={options.RewardedSceneSlotIds?.Count ?? 0}");
}
private void LogRewardedResolvedSlot(AD_Type adType, string scenario, string actionName)
{
if (adType != AD_Type.AwardVideo)
{
return;
}
AppendLog($"{actionName} rewarded slot => {GetResolvedRewardedSlotForDisplay(scenario)}");
}
private string GetResolvedRewardedSlotForDisplay(string scenario)
{
var defaultSlotId = adConfig?.BaseAwardAdKeyValue?.value;
var options = TapadnAdController.CurrentOptions;
if (options == null)
{
return $"{DisplayValue(defaultSlotId)} (source=default, scenario={DisplayValue(scenario)})";
}
var slotId = options.ResolveRewardedSlotId(defaultSlotId, scenario, out var mapped);
return $"{DisplayValue(slotId)} (source={(mapped ? "scene" : "default")}, scenario={DisplayValue(scenario)})";
} }
private void OnRewardedBefore(string placementId, string scenario) private void OnRewardedBefore(string placementId, string scenario)

View File

@@ -3,7 +3,7 @@ using Dirichlet.Mediation;
using Runtime.ADAggregator; using Runtime.ADAggregator;
using UnityEngine; using UnityEngine;
public sealed class TapadnAdController : IAdController public sealed class TapadnAdController : IAdController, IAdEditorDiagnostics
{ {
public static TapadnControllerOptions CurrentOptions { get; private set; } public static TapadnControllerOptions CurrentOptions { get; private set; }
public static string LastSdkVersion { get; private set; } public static string LastSdkVersion { get; private set; }
@@ -73,4 +73,41 @@ public sealed class TapadnAdController : IAdController
{ {
_maskAction?.Invoke(isOpen); _maskAction?.Invoke(isOpen);
} }
public void LogEditorAdPlacement(ADConfig adConfig, AD_Type adType, string adScene, string action, object[] args)
{
var options = TapadnControllerOptions.Resolve(adConfig, args);
var normalizedScene = string.IsNullOrWhiteSpace(adScene) ? "__default__" : adScene.Trim();
var slotSource = "default";
var slotId = ResolveEditorSlotId(adConfig, options, adType, normalizedScene, out slotSource);
Debug.Log($"[TapADN] Editor ad {action}. type={adType}, scene={normalizedScene}, slot={DisplayEditorValue(slotId)}, source={slotSource}");
}
private static string ResolveEditorSlotId(ADConfig adConfig, TapadnControllerOptions options, AD_Type adType, string adScene, out string slotSource)
{
slotSource = "default";
switch (adType)
{
case AD_Type.AwardVideo:
var defaultRewardedSlotId = adConfig?.BaseAwardAdKeyValue?.value;
var mapped = false;
var rewardedSlotId = options == null
? defaultRewardedSlotId
: options.ResolveRewardedSlotId(defaultRewardedSlotId, adScene, out mapped);
slotSource = mapped ? "scene" : "default";
return rewardedSlotId;
case AD_Type.Interaction:
return adConfig?.BaseInteractionAdKeyValue?.value;
case AD_Type.Splash:
return adConfig?.BaseSplashAdKeyValue?.value;
default:
slotSource = "unsupported";
return null;
}
}
private static string DisplayEditorValue(string value)
{
return string.IsNullOrWhiteSpace(value) ? "<empty>" : value.Trim();
}
} }

View File

@@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using Dirichlet.Mediation; using Dirichlet.Mediation;
using Runtime.ADAggregator; using Runtime.ADAggregator;
using UnityEngine; using UnityEngine;
@@ -7,8 +8,11 @@ public sealed class TapadnAwardVideoPlayer : ADPlayer, IDirichletRewardVideoAuto
{ {
private const float RewardCloseSettleDelaySeconds = 0.25f; private const float RewardCloseSettleDelaySeconds = 0.25f;
private readonly Dictionary<string, RewardedSlotCache> _slotCaches = new Dictionary<string, RewardedSlotCache>(StringComparer.Ordinal);
private DirichletAdNative _adNative; private DirichletAdNative _adNative;
private DirichletRewardVideoAd _loadedAd; private string _defaultSlotId;
private string _activeSlotId;
private bool _rewardVerified; private bool _rewardVerified;
private bool _rewardVerifyReceived; private bool _rewardVerifyReceived;
private bool _closePendingRewardVerify; private bool _closePendingRewardVerify;
@@ -22,30 +26,47 @@ public sealed class TapadnAwardVideoPlayer : ADPlayer, IDirichletRewardVideoAuto
public override void OnInit() public override void OnInit()
{ {
_defaultSlotId = Key;
_adNative = DirichletAdManager.CreateAdNative(); _adNative = DirichletAdManager.CreateAdNative();
} }
public override bool IsReadly() public override bool IsReadly()
{ {
var slotId = ResolveCurrentSlotId();
if (UseAutoLoad()) if (UseAutoLoad())
{ {
return TapadnAdRequestFactory.TryParseSlotId(Key, out _); return TapadnAdRequestFactory.TryParseSlotId(slotId, out _);
} }
if (_loadedAd != null && _loadedAd.IsLoaded && _loadedAd.IsValid) var cache = GetCache(slotId);
if (IsCacheReady(cache))
{ {
curState = 2; curState = 2;
return true; return true;
} }
if (cache != null && cache.Loading)
{
curState = 1;
return false;
}
if (cache != null)
{
RemoveCache(slotId);
}
curState = 0;
return false; return false;
} }
public override void LoadAD() public override void LoadAD()
{ {
if (!TapadnAdRequestFactory.TryParseSlotId(Key, out _)) var slotId = ResolveCurrentSlotId(out var mapped);
Debug.Log($"[TapADN] Rewarded load requested. scene={NormalizeScenario(AdScene)}, slot={slotId}, source={GetSlotSource(mapped)}, auto={UseAutoLoad()}");
if (!TapadnAdRequestFactory.TryParseSlotId(slotId, out _))
{ {
Debug.LogError($"[TapADN] Invalid rewarded slot id: {Key}"); Debug.LogError($"[TapADN] Invalid rewarded slot id: {slotId}");
curState = 0; curState = 0;
return; return;
} }
@@ -55,7 +76,7 @@ public sealed class TapadnAwardVideoPlayer : ADPlayer, IDirichletRewardVideoAuto
curState = 2; curState = 2;
try try
{ {
_adNative.PreLoad(TapadnAdRequestFactory.BuildRewarded(Key, TapadnAdController.CurrentOptions), 3); _adNative.PreLoad(TapadnAdRequestFactory.BuildRewarded(slotId, TapadnAdController.CurrentOptions), 3);
} }
catch (Exception exception) catch (Exception exception)
{ {
@@ -64,33 +85,37 @@ public sealed class TapadnAwardVideoPlayer : ADPlayer, IDirichletRewardVideoAuto
return; return;
} }
if (curState == 1 || IsReadly()) var cache = GetCache(slotId);
if (cache != null && (cache.Loading || IsCacheReady(cache)))
{ {
curState = cache.Loading ? 1 : 2;
return; return;
} }
cache = new RewardedSlotCache { Loading = true };
_slotCaches[slotId] = cache;
curState = 1; curState = 1;
TapadnSmartLoadOrchestrator.OnLoadStarted(AD_Type.AwardVideo, AdScene); TapadnSmartLoadOrchestrator.OnLoadStarted(AD_Type.AwardVideo, AdScene, slotId);
_adNative.LoadRewardVideoAd( _adNative.LoadRewardVideoAd(
TapadnAdRequestFactory.BuildRewarded(Key, TapadnAdController.CurrentOptions), TapadnAdRequestFactory.BuildRewarded(slotId, TapadnAdController.CurrentOptions),
ad => ad =>
{ {
TapadnSmartLoadOrchestrator.OnLoadResult(AD_Type.AwardVideo, AdScene, true); TapadnSmartLoadOrchestrator.OnLoadResult(AD_Type.AwardVideo, AdScene, true, slotId);
_loadedAd?.Destroy(); DestroyCachedAd(cache);
_loadedAd = ad; cache.Ad = ad;
_loadedAd.Shown += OnManualShown; cache.Loading = false;
_loadedAd.Clicked += OnManualClicked; cache.LoadedUnix = GetNowUnixSeconds();
_loadedAd.ShowFailed += OnManualShowFailed; RegisterManualEvents(slotId, cache.Ad);
_loadedAd.RewardVerified += OnManualRewardVerify; ScheduleCacheExpiration(slotId, cache);
_loadedAd.Closed += OnManualClosed; curState = string.Equals(slotId, ResolveCurrentSlotId(), StringComparison.Ordinal) ? 2 : curState;
curState = 2; Debug.Log($"[TapADN] Rewarded loaded. scene={NormalizeScenario(AdScene)}, slot={slotId}");
Debug.Log($"[TapADN] Rewarded loaded. slot={Key}");
}, },
error => error =>
{ {
TapadnSmartLoadOrchestrator.OnLoadResult(AD_Type.AwardVideo, AdScene, false); TapadnSmartLoadOrchestrator.OnLoadResult(AD_Type.AwardVideo, AdScene, false, slotId);
curState = 0; RemoveCache(slotId);
Debug.LogError($"[TapADN] Rewarded load failed. code={error.Code}, message={error.Message}"); curState = string.Equals(slotId, ResolveCurrentSlotId(), StringComparison.Ordinal) ? 0 : curState;
Debug.LogError($"[TapADN] Rewarded load failed. slot={slotId}, code={error.Code}, message={error.Message}");
}); });
} }
@@ -104,16 +129,21 @@ public sealed class TapadnAwardVideoPlayer : ADPlayer, IDirichletRewardVideoAuto
_showSettled = false; _showSettled = false;
_rewardCloseSettleHandler?.Kill(); _rewardCloseSettleHandler?.Kill();
_rewardCloseSettleHandler = null; _rewardCloseSettleHandler = null;
_activeSlotId = ResolveCurrentSlotId(out var mapped);
Key = _activeSlotId;
curState = 0; curState = 0;
Debug.Log($"[TapADN] Rewarded show requested. scene={NormalizeScenario(AdScene)}, slot={_activeSlotId}, source={GetSlotSource(mapped)}, auto={UseAutoLoad()}");
if (UseAutoLoad()) if (UseAutoLoad())
{ {
_adNative.ShowRewardVideoAutoAd(TapadnAdRequestFactory.BuildRewarded(Key, TapadnAdController.CurrentOptions), this); _adNative.ShowRewardVideoAutoAd(TapadnAdRequestFactory.BuildRewarded(_activeSlotId, TapadnAdController.CurrentOptions), this);
return; return;
} }
if (_loadedAd == null || !_loadedAd.Show()) var cache = GetCache(_activeSlotId);
if (!IsCacheReady(cache) || cache.Ad == null || !cache.Ad.Show())
{ {
RemoveCache(_activeSlotId);
OnError(new DirichletError("show_failed", "ShowRewardVideoAd returned false")); OnError(new DirichletError("show_failed", "ShowRewardVideoAd returned false"));
return; return;
} }
@@ -130,14 +160,14 @@ public sealed class TapadnAwardVideoPlayer : ADPlayer, IDirichletRewardVideoAuto
_rewardCloseSettleHandler?.Kill(); _rewardCloseSettleHandler?.Kill();
_rewardCloseSettleHandler = null; _rewardCloseSettleHandler = null;
curState = 0; curState = 0;
TapadnSmartLoadOrchestrator.OnShowError(AD_Type.AwardVideo, AdScene); TapadnSmartLoadOrchestrator.OnShowError(AD_Type.AwardVideo, AdScene, _activeSlotId);
Debug.LogError($"[TapADN] Rewarded show failed. code={error?.Code}, message={error?.Message}"); Debug.LogError($"[TapADN] Rewarded show failed. slot={_activeSlotId}, code={error?.Code}, message={error?.Message}");
adListener.OnShowError(); adListener.OnShowError();
} }
public void OnAdShow() public void OnAdShow()
{ {
TapadnSmartLoadOrchestrator.OnShowStart(AD_Type.AwardVideo, AdScene); TapadnSmartLoadOrchestrator.OnShowStart(AD_Type.AwardVideo, AdScene, _activeSlotId);
NotifyShowStarted(); NotifyShowStarted();
} }
@@ -175,12 +205,41 @@ public sealed class TapadnAwardVideoPlayer : ADPlayer, IDirichletRewardVideoAuto
public override void OnPlayRequestStarted() public override void OnPlayRequestStarted()
{ {
TapadnSmartLoadOrchestrator.OnPlayRequestStarted(AD_Type.AwardVideo, AdScene, !UseAutoLoad() && IsReadly()); var slotId = ResolveCurrentSlotId(out var mapped);
Key = slotId;
Debug.Log($"[TapADN] Rewarded play request. scene={NormalizeScenario(AdScene)}, slot={slotId}, source={GetSlotSource(mapped)}, auto={UseAutoLoad()}");
TapadnSmartLoadOrchestrator.OnPlayRequestStarted(AD_Type.AwardVideo, AdScene, !UseAutoLoad() && IsReadly(), slotId);
} }
public override void EnterAdScenario(string scenario) public override void EnterAdScenario(string scenario)
{ {
TapadnSmartLoadOrchestrator.OnEnterAdScenario(AD_Type.AwardVideo, scenario, Key); AdScene = NormalizeScenario(scenario);
var slotId = ResolveCurrentSlotId(out var mapped);
Key = slotId;
Debug.Log($"[TapADN] Rewarded enter scene. scene={AdScene}, slot={slotId}, source={GetSlotSource(mapped)}");
TapadnSmartLoadOrchestrator.OnEnterAdScenario(AD_Type.AwardVideo, AdScene, slotId);
}
private string ResolveCurrentSlotId()
{
return ResolveCurrentSlotId(out _);
}
private string ResolveCurrentSlotId(out bool mapped)
{
if (TapadnAdController.CurrentOptions == null)
{
mapped = false;
return string.IsNullOrWhiteSpace(_defaultSlotId) ? _defaultSlotId : _defaultSlotId.Trim();
}
var slotId = TapadnAdController.CurrentOptions.ResolveRewardedSlotId(_defaultSlotId, AdScene, out mapped);
return string.IsNullOrWhiteSpace(slotId) ? _defaultSlotId : slotId.Trim();
}
private static string GetSlotSource(bool mapped)
{
return mapped ? "scene" : "default";
} }
private bool UseAutoLoad() private bool UseAutoLoad()
@@ -188,8 +247,66 @@ public sealed class TapadnAwardVideoPlayer : ADPlayer, IDirichletRewardVideoAuto
return TapadnAdController.CurrentOptions?.RewardedAutoLoad ?? true; return TapadnAdController.CurrentOptions?.RewardedAutoLoad ?? true;
} }
private void OnManualShown() 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(); OnAdShow();
} }
@@ -197,8 +314,9 @@ public sealed class TapadnAwardVideoPlayer : ADPlayer, IDirichletRewardVideoAuto
{ {
} }
private void OnManualShowFailed(DirichletError error) private void OnManualShowFailed(string slotId, DirichletError error)
{ {
RemoveCache(slotId);
OnError(error); OnError(error);
} }
@@ -207,13 +325,49 @@ public sealed class TapadnAwardVideoPlayer : ADPlayer, IDirichletRewardVideoAuto
OnRewardVerify(args); OnRewardVerify(args);
} }
private void OnManualClosed() private void OnManualClosed(string slotId)
{ {
_loadedAd?.Destroy(); RemoveCache(slotId);
_loadedAd = null;
OnAdClose(); 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() private void CompleteRewardedClose()
{ {
if (_showSettled) if (_showSettled)
@@ -228,4 +382,22 @@ public sealed class TapadnAwardVideoPlayer : ADPlayer, IDirichletRewardVideoAuto
adListener.OnRewardVerify(_rewardVerified, TapadnAdController.CurrentOptions?.RewardAmount ?? 1, TapadnAdController.CurrentOptions?.RewardName ?? string.Empty); adListener.OnRewardVerify(_rewardVerified, TapadnAdController.CurrentOptions?.RewardAmount ?? 1, TapadnAdController.CurrentOptions?.RewardName ?? string.Empty);
adListener.OnAdClose(); 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;
}
} }

View File

@@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using Runtime.ADAggregator; using Runtime.ADAggregator;
public static class TapadnCommercialization public static class TapadnCommercialization
@@ -31,4 +32,31 @@ public static class TapadnCommercialization
config.BaseSplashAdKeyValue = new AdKeyValue { key = "splash", value = splashSlotId }; config.BaseSplashAdKeyValue = new AdKeyValue { key = "splash", value = splashSlotId };
return config; return config;
} }
public static void SetRewardedSceneSlots(ADConfig config, IDictionary<string, string> sceneSlotIds)
{
if (config == null || sceneSlotIds == null || sceneSlotIds.Count == 0)
{
return;
}
if (config.CommonKeyValues == null)
{
config.CommonKeyValues = new List<AdKeyValue>();
}
foreach (var entry in sceneSlotIds)
{
if (string.IsNullOrWhiteSpace(entry.Key) || string.IsNullOrWhiteSpace(entry.Value))
{
continue;
}
config.CommonKeyValues.Add(new AdKeyValue
{
key = TapadnControllerOptions.RewardedSceneSlotPrefix + entry.Key.Trim(),
value = entry.Value.Trim()
});
}
}
} }

View File

@@ -9,6 +9,8 @@ using UnityEngine;
public sealed class TapadnControllerOptions public sealed class TapadnControllerOptions
{ {
private const string TapadnKeyPrefix = "tapadn.";
public const string MediaNameKey = "tapadn.media_name"; public const string MediaNameKey = "tapadn.media_name";
public const string MediaIdKey = "tapadn.media_id"; public const string MediaIdKey = "tapadn.media_id";
public const string MediaKeyKey = "tapadn.media_key"; public const string MediaKeyKey = "tapadn.media_key";
@@ -27,6 +29,10 @@ public sealed class TapadnControllerOptions
public const string RewardedMaxLoadAttemptsKey = "tapadn.rewarded_max_load_attempts"; public const string RewardedMaxLoadAttemptsKey = "tapadn.rewarded_max_load_attempts";
public const string RewardedLoadRetryDelayMsKey = "tapadn.rewarded_load_retry_delay_ms"; public const string RewardedLoadRetryDelayMsKey = "tapadn.rewarded_load_retry_delay_ms";
public const string RewardedShowTimeoutMsKey = "tapadn.rewarded_show_timeout_ms"; public const string RewardedShowTimeoutMsKey = "tapadn.rewarded_show_timeout_ms";
public const string RewardedSceneSlotPrefix = "tapadn.rewarded_scene_slot.";
public const string RewardedSceneSlotsKey = "tapadn.rewarded_scene_slots";
public const string RewardedSceneSlotsJsonKey = "tapadn.rewarded_scene_slots_json";
public const string RewardedCacheMaxAgeSecondsKey = "tapadn.rewarded_cache_max_age_seconds";
public const string RewardNameKey = "tapadn.reward_name"; public const string RewardNameKey = "tapadn.reward_name";
public const string RewardAmountKey = "tapadn.reward_amount"; public const string RewardAmountKey = "tapadn.reward_amount";
public const string InterstitialAutoLoadKey = "tapadn.interstitial_auto_load"; public const string InterstitialAutoLoadKey = "tapadn.interstitial_auto_load";
@@ -64,6 +70,8 @@ public sealed class TapadnControllerOptions
public int RewardedMaxLoadAttempts { get; set; } = 1; public int RewardedMaxLoadAttempts { get; set; } = 1;
public int RewardedLoadRetryDelayMs { get; set; } = 500; public int RewardedLoadRetryDelayMs { get; set; } = 500;
public int RewardedShowTimeoutMs { get; set; } = 20000; public int RewardedShowTimeoutMs { get; set; } = 20000;
public int RewardedCacheMaxAgeSeconds { get; set; } = 600;
public Dictionary<string, string> RewardedSceneSlotIds { get; private set; } = new Dictionary<string, string>(StringComparer.Ordinal);
public string RewardName { get; set; } = "reward"; public string RewardName { get; set; } = "reward";
public int RewardAmount { get; set; } = 1; public int RewardAmount { get; set; } = 1;
public bool InterstitialAutoLoad { get; set; } = false; public bool InterstitialAutoLoad { get; set; } = false;
@@ -205,6 +213,11 @@ public sealed class TapadnControllerOptions
RewardedMaxLoadAttempts = incoming.RewardedMaxLoadAttempts; RewardedMaxLoadAttempts = incoming.RewardedMaxLoadAttempts;
RewardedLoadRetryDelayMs = incoming.RewardedLoadRetryDelayMs; RewardedLoadRetryDelayMs = incoming.RewardedLoadRetryDelayMs;
RewardedShowTimeoutMs = incoming.RewardedShowTimeoutMs; RewardedShowTimeoutMs = incoming.RewardedShowTimeoutMs;
RewardedCacheMaxAgeSeconds = incoming.RewardedCacheMaxAgeSeconds;
if (incoming.RewardedSceneSlotIds != null && incoming.RewardedSceneSlotIds.Count > 0)
{
RewardedSceneSlotIds = CloneSceneSlotMap(incoming.RewardedSceneSlotIds);
}
RewardName = incoming.RewardName ?? RewardName; RewardName = incoming.RewardName ?? RewardName;
RewardAmount = incoming.RewardAmount; RewardAmount = incoming.RewardAmount;
InterstitialAutoLoad = incoming.InterstitialAutoLoad; InterstitialAutoLoad = incoming.InterstitialAutoLoad;
@@ -266,6 +279,8 @@ public sealed class TapadnControllerOptions
RewardedMaxLoadAttempts = GetInt(map, RewardedMaxLoadAttemptsKey) ?? RewardedMaxLoadAttempts; RewardedMaxLoadAttempts = GetInt(map, RewardedMaxLoadAttemptsKey) ?? RewardedMaxLoadAttempts;
RewardedLoadRetryDelayMs = GetInt(map, RewardedLoadRetryDelayMsKey) ?? RewardedLoadRetryDelayMs; RewardedLoadRetryDelayMs = GetInt(map, RewardedLoadRetryDelayMsKey) ?? RewardedLoadRetryDelayMs;
RewardedShowTimeoutMs = GetInt(map, RewardedShowTimeoutMsKey) ?? RewardedShowTimeoutMs; RewardedShowTimeoutMs = GetInt(map, RewardedShowTimeoutMsKey) ?? RewardedShowTimeoutMs;
RewardedCacheMaxAgeSeconds = Math.Max(0, GetInt(map, RewardedCacheMaxAgeSecondsKey) ?? RewardedCacheMaxAgeSeconds);
ApplyRewardedSceneSlots(map);
RewardName = GetString(map, RewardNameKey) ?? RewardName; RewardName = GetString(map, RewardNameKey) ?? RewardName;
RewardAmount = GetInt(map, RewardAmountKey) ?? RewardAmount; RewardAmount = GetInt(map, RewardAmountKey) ?? RewardAmount;
InterstitialAutoLoad = GetBool(map, InterstitialAutoLoadKey) ?? InterstitialAutoLoad; InterstitialAutoLoad = GetBool(map, InterstitialAutoLoadKey) ?? InterstitialAutoLoad;
@@ -286,6 +301,162 @@ public sealed class TapadnControllerOptions
ExpressHeight = GetInt(map, ExpressHeightKey) ?? ExpressHeight; ExpressHeight = GetInt(map, ExpressHeightKey) ?? ExpressHeight;
} }
public string ResolveRewardedSlotId(string defaultSlotId, string scenario)
{
return ResolveRewardedSlotId(defaultSlotId, scenario, out _);
}
public string ResolveRewardedSlotId(string defaultSlotId, string scenario, out bool mapped)
{
var normalizedScenario = NormalizeScenario(scenario);
if (RewardedSceneSlotIds != null &&
RewardedSceneSlotIds.TryGetValue(normalizedScenario, out var mappedSlotId) &&
TapadnAdRequestFactory.TryParseSlotId(mappedSlotId, out _))
{
mapped = true;
return mappedSlotId;
}
mapped = false;
return defaultSlotId;
}
private void ApplyRewardedSceneSlots(IDictionary<string, string> map)
{
ApplyLegacyRewardedSceneSlots(map);
foreach (var entry in map)
{
if (entry.Key == null || !entry.Key.StartsWith(RewardedSceneSlotPrefix, StringComparison.OrdinalIgnoreCase))
{
continue;
}
var scene = entry.Key.Substring(RewardedSceneSlotPrefix.Length);
AddRewardedSceneSlot(scene, entry.Value);
}
ParseSceneSlotPairs(GetString(map, RewardedSceneSlotsKey), AddRewardedSceneSlot);
ParseSceneSlotJson(GetString(map, RewardedSceneSlotsJsonKey), AddRewardedSceneSlot);
}
private void ApplyLegacyRewardedSceneSlots(IDictionary<string, string> map)
{
foreach (var entry in map)
{
if (!IsLegacyRewardedSceneSlotEntry(entry.Key, entry.Value))
{
continue;
}
AddRewardedSceneSlot(entry.Key, entry.Value);
}
}
private void AddRewardedSceneSlot(string scene, string slotId)
{
scene = NormalizeScenario(scene);
if (string.IsNullOrWhiteSpace(slotId) || !TapadnAdRequestFactory.TryParseSlotId(slotId.Trim(), out _))
{
UnityEngine.Debug.LogWarning($"[TapADN] Ignore invalid rewarded scene slot. scene={scene}, slot={slotId}");
return;
}
RewardedSceneSlotIds[scene] = slotId.Trim();
}
private static bool IsLegacyRewardedSceneSlotEntry(string key, string value)
{
var normalizedKey = key?.Trim();
if (string.IsNullOrWhiteSpace(normalizedKey) ||
string.IsNullOrWhiteSpace(value) ||
normalizedKey.StartsWith(TapadnKeyPrefix, StringComparison.OrdinalIgnoreCase))
{
return false;
}
switch (normalizedKey.ToLowerInvariant())
{
case "media_id":
case "media_name":
case "media_key":
case "channel":
case "sub_channel":
case "debug":
return false;
}
return TapadnAdRequestFactory.TryParseSlotId(value.Trim(), out _);
}
private static void ParseSceneSlotPairs(string value, Action<string, string> add)
{
if (string.IsNullOrWhiteSpace(value) || add == null)
{
return;
}
var pairs = value.Split(new[] { ',', ';', '\n', '\r', '|' }, StringSplitOptions.RemoveEmptyEntries);
foreach (var pair in pairs)
{
var separatorIndex = pair.IndexOf('=');
if (separatorIndex < 0)
{
separatorIndex = pair.IndexOf(':');
}
if (separatorIndex <= 0 || separatorIndex >= pair.Length - 1)
{
continue;
}
add(pair.Substring(0, separatorIndex), pair.Substring(separatorIndex + 1));
}
}
private static void ParseSceneSlotJson(string json, Action<string, string> add)
{
if (string.IsNullOrWhiteSpace(json) || add == null)
{
return;
}
try
{
var parsed = JsonUtility.FromJson<TapadnSceneSlotMapConfig>(json);
if (parsed?.Mappings == null)
{
return;
}
foreach (var mapping in parsed.Mappings)
{
if (mapping == null)
{
continue;
}
add(mapping.Scene, mapping.SlotId);
}
}
catch (Exception exception)
{
UnityEngine.Debug.LogWarning($"[TapADN] Parse rewarded scene slot JSON failed: {exception.Message}");
}
}
private static Dictionary<string, string> CloneSceneSlotMap(Dictionary<string, string> source)
{
return source == null
? new Dictionary<string, string>(StringComparer.Ordinal)
: new Dictionary<string, string>(source, StringComparer.Ordinal);
}
private static string NormalizeScenario(string scenario)
{
return string.IsNullOrWhiteSpace(scenario) ? "__default__" : scenario.Trim();
}
private static string GetString(IDictionary<string, string> map, params string[] keys) private static string GetString(IDictionary<string, string> map, params string[] keys)
{ {
foreach (var key in keys) foreach (var key in keys)
@@ -383,4 +554,17 @@ public sealed class TapadnControllerOptions
return value.ToString(); return value.ToString();
} }
[Serializable]
private sealed class TapadnSceneSlotMapConfig
{
public List<TapadnSceneSlotMapping> Mappings;
}
[Serializable]
private sealed class TapadnSceneSlotMapping
{
public string Scene;
public string SlotId;
}
} }

View File

@@ -56,12 +56,12 @@ public sealed class TapadnInteractionPlayer : ADPlayer, IDirichletInterstitialAu
} }
curState = 1; curState = 1;
TapadnSmartLoadOrchestrator.OnLoadStarted(AD_Type.Interaction, AdScene); TapadnSmartLoadOrchestrator.OnLoadStarted(AD_Type.Interaction, AdScene, Key);
_adNative.LoadInterstitialAd( _adNative.LoadInterstitialAd(
TapadnAdRequestFactory.BuildInterstitial(Key, TapadnAdController.CurrentOptions), TapadnAdRequestFactory.BuildInterstitial(Key, TapadnAdController.CurrentOptions),
ad => ad =>
{ {
TapadnSmartLoadOrchestrator.OnLoadResult(AD_Type.Interaction, AdScene, true); TapadnSmartLoadOrchestrator.OnLoadResult(AD_Type.Interaction, AdScene, true, Key);
_loadedAd?.Destroy(); _loadedAd?.Destroy();
_loadedAd = ad; _loadedAd = ad;
_loadedAd.Shown += OnManualShown; _loadedAd.Shown += OnManualShown;
@@ -73,7 +73,7 @@ public sealed class TapadnInteractionPlayer : ADPlayer, IDirichletInterstitialAu
}, },
error => error =>
{ {
TapadnSmartLoadOrchestrator.OnLoadResult(AD_Type.Interaction, AdScene, false); TapadnSmartLoadOrchestrator.OnLoadResult(AD_Type.Interaction, AdScene, false, Key);
curState = 0; curState = 0;
Debug.LogError($"[TapADN] Interstitial load failed. code={error.Code}, message={error.Message}"); Debug.LogError($"[TapADN] Interstitial load failed. code={error.Code}, message={error.Message}");
}); });
@@ -106,7 +106,7 @@ public sealed class TapadnInteractionPlayer : ADPlayer, IDirichletInterstitialAu
} }
_showSettled = true; _showSettled = true;
TapadnSmartLoadOrchestrator.OnShowError(AD_Type.Interaction, AdScene); TapadnSmartLoadOrchestrator.OnShowError(AD_Type.Interaction, AdScene, Key);
curState = 0; curState = 0;
Debug.LogError($"[TapADN] Interstitial show failed. code={error?.Code}, message={error?.Message}"); Debug.LogError($"[TapADN] Interstitial show failed. code={error?.Code}, message={error?.Message}");
adListener.OnShowError(); adListener.OnShowError();
@@ -114,7 +114,7 @@ public sealed class TapadnInteractionPlayer : ADPlayer, IDirichletInterstitialAu
public void OnAdShow() public void OnAdShow()
{ {
TapadnSmartLoadOrchestrator.OnShowStart(AD_Type.Interaction, AdScene); TapadnSmartLoadOrchestrator.OnShowStart(AD_Type.Interaction, AdScene, Key);
NotifyShowStarted(); NotifyShowStarted();
} }
@@ -136,12 +136,13 @@ public sealed class TapadnInteractionPlayer : ADPlayer, IDirichletInterstitialAu
public override void OnPlayRequestStarted() public override void OnPlayRequestStarted()
{ {
TapadnSmartLoadOrchestrator.OnPlayRequestStarted(AD_Type.Interaction, AdScene, !UseAutoLoad() && IsReadly()); TapadnSmartLoadOrchestrator.OnPlayRequestStarted(AD_Type.Interaction, AdScene, !UseAutoLoad() && IsReadly(), Key);
} }
public override void EnterAdScenario(string scenario) public override void EnterAdScenario(string scenario)
{ {
TapadnSmartLoadOrchestrator.OnEnterAdScenario(AD_Type.Interaction, scenario, Key); AdScene = string.IsNullOrWhiteSpace(scenario) ? "__default__" : scenario.Trim();
TapadnSmartLoadOrchestrator.OnEnterAdScenario(AD_Type.Interaction, AdScene, Key);
} }
private bool UseAutoLoad() private bool UseAutoLoad()
@@ -151,7 +152,7 @@ public sealed class TapadnInteractionPlayer : ADPlayer, IDirichletInterstitialAu
private void OnManualShown() private void OnManualShown()
{ {
TapadnSmartLoadOrchestrator.OnShowStart(AD_Type.Interaction, AdScene); TapadnSmartLoadOrchestrator.OnShowStart(AD_Type.Interaction, AdScene, Key);
NotifyShowStarted(); NotifyShowStarted();
} }

View File

@@ -15,7 +15,7 @@ public static class TapadnSmartLoadOrchestrator
private static TapadnSmartLoadConfig _runtimeConfig; private static TapadnSmartLoadConfig _runtimeConfig;
private static Dictionary<string, TapadnSmartLoadSceneState> _states = new Dictionary<string, TapadnSmartLoadSceneState>(StringComparer.Ordinal); 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<string, TapadnSmartLoadPolicyItem> _policies = new Dictionary<string, TapadnSmartLoadPolicyItem>(StringComparer.Ordinal);
private static Dictionary<int, TapadnSmartLoadCacheState> _cacheStates = new Dictionary<int, TapadnSmartLoadCacheState>(); private static Dictionary<string, TapadnSmartLoadCacheState> _cacheStates = new Dictionary<string, TapadnSmartLoadCacheState>(StringComparer.Ordinal);
private static bool _initialized; private static bool _initialized;
private static bool _enabled; private static bool _enabled;
@@ -116,7 +116,7 @@ public static class TapadnSmartLoadOrchestrator
_enabled = options?.SmartPreloadEnabled ?? false; _enabled = options?.SmartPreloadEnabled ?? false;
_initialized = true; _initialized = true;
_states = new Dictionary<string, TapadnSmartLoadSceneState>(StringComparer.Ordinal); _states = new Dictionary<string, TapadnSmartLoadSceneState>(StringComparer.Ordinal);
_cacheStates = new Dictionary<int, TapadnSmartLoadCacheState>(); _cacheStates = new Dictionary<string, TapadnSmartLoadCacheState>(StringComparer.Ordinal);
LoadStates(); LoadStates();
EnsureDefaultPolicies(_runtimeConfig); EnsureDefaultPolicies(_runtimeConfig);
} }
@@ -135,10 +135,10 @@ public static class TapadnSmartLoadOrchestrator
record.ShowRequestCount = Math.Max(0, record.ShowRequestCount); record.ShowRequestCount = Math.Max(0, record.ShowRequestCount);
SaveStates(); SaveStates();
TryPreload(adType, normalizedScenario); TryPreload(adType, normalizedScenario, _slotId);
} }
public static void OnPlayRequestStarted(AD_Type adType, string scenario, bool cacheReadyAtRequest) public static void OnPlayRequestStarted(AD_Type adType, string scenario, bool cacheReadyAtRequest, string slotId = null)
{ {
if (!_initialized || !_enabled) if (!_initialized || !_enabled)
{ {
@@ -152,49 +152,49 @@ public static class TapadnSmartLoadOrchestrator
record.LastUpdatedUnix = GetNowUnixSeconds(); record.LastUpdatedUnix = GetNowUnixSeconds();
if (cacheReadyAtRequest) if (cacheReadyAtRequest)
{ {
MarkImmediateHit(adType, normalizedScenario); MarkImmediateHit(adType, normalizedScenario, slotId);
} }
else else
{ {
MarkCacheExpiredIfStale(adType); MarkCacheExpiredIfStale(adType, slotId);
} }
SaveStates(); SaveStates();
} }
public static void OnLoadRequested(AD_Type adType, string scenario) public static void OnLoadRequested(AD_Type adType, string scenario, string slotId = null)
{ {
if (!_initialized || !_enabled) if (!_initialized || !_enabled)
{ {
return; return;
} }
PrepareSmartLoadRequest(adType, scenario); PrepareSmartLoadRequest(adType, scenario, slotId);
} }
public static void OnLoadStarted(AD_Type adType, string scenario) public static void OnLoadStarted(AD_Type adType, string scenario, string slotId = null)
{ {
if (!_initialized || !_enabled) if (!_initialized || !_enabled)
{ {
return; return;
} }
BeginLoadRequest(adType, scenario); BeginLoadRequest(adType, scenario, slotId);
SaveStates(); SaveStates();
} }
public static void OnLoadResult(AD_Type adType, string scenario, bool success) public static void OnLoadResult(AD_Type adType, string scenario, bool success, string slotId = null)
{ {
if (!_initialized || !_enabled) if (!_initialized || !_enabled)
{ {
return; return;
} }
CompleteLoadRequest(adType, scenario, success); CompleteLoadRequest(adType, scenario, success, slotId);
SaveStates(); SaveStates();
} }
public static void OnShowStart(AD_Type adType, string scenario) public static void OnShowStart(AD_Type adType, string scenario, string slotId = null)
{ {
if (!_initialized || !_enabled) if (!_initialized || !_enabled)
{ {
@@ -204,11 +204,11 @@ public static class TapadnSmartLoadOrchestrator
var record = GetOrCreateState(adType, NormalizeScenario(scenario)); var record = GetOrCreateState(adType, NormalizeScenario(scenario));
record.ShowStartCount = Math.Max(0, record.ShowStartCount) + 1; record.ShowStartCount = Math.Max(0, record.ShowStartCount) + 1;
record.LastUpdatedUnix = GetNowUnixSeconds(); record.LastUpdatedUnix = GetNowUnixSeconds();
MarkCacheConsumed(adType, scenario); MarkCacheConsumed(adType, scenario, slotId);
SaveStates(); SaveStates();
} }
public static void OnShowError(AD_Type adType, string scenario) public static void OnShowError(AD_Type adType, string scenario, string slotId = null)
{ {
if (!_initialized || !_enabled) if (!_initialized || !_enabled)
{ {
@@ -218,11 +218,11 @@ public static class TapadnSmartLoadOrchestrator
var record = GetOrCreateState(adType, NormalizeScenario(scenario)); var record = GetOrCreateState(adType, NormalizeScenario(scenario));
record.ShowFailureCount = Math.Max(0, record.ShowFailureCount) + 1; record.ShowFailureCount = Math.Max(0, record.ShowFailureCount) + 1;
record.LastUpdatedUnix = GetNowUnixSeconds(); record.LastUpdatedUnix = GetNowUnixSeconds();
MarkCacheShowFailed(adType, scenario); MarkCacheShowFailed(adType, scenario, slotId);
SaveStates(); SaveStates();
} }
private static void TryPreload(AD_Type adType, string scenario) private static void TryPreload(AD_Type adType, string scenario, string slotId)
{ {
if (!_initialized || !_enabled) if (!_initialized || !_enabled)
{ {
@@ -257,11 +257,11 @@ public static class TapadnSmartLoadOrchestrator
return; return;
} }
MarkCacheExpiredIfStale(adType); MarkCacheExpiredIfStale(adType, slotId);
OnLoadRequested(adType, scenario); OnLoadRequested(adType, scenario, slotId);
ADManager.Instance.LoadAD(adType); ADManager.Instance.LoadAD(adType);
var smartLoadStarted = HasPendingSmartLoadForScene(adType, scenario); var smartLoadStarted = HasPendingSmartLoadForScene(adType, scenario, slotId);
ClearPreparedLoadIfNotStarted(adType); ClearPreparedLoadIfNotStarted(adType, slotId);
if (!smartLoadStarted) if (!smartLoadStarted)
{ {
return; return;
@@ -302,9 +302,9 @@ public static class TapadnSmartLoadOrchestrator
return Mathf.Lerp(policy.BaseProbability, observedRate, trust); return Mathf.Lerp(policy.BaseProbability, observedRate, trust);
} }
private static void PrepareSmartLoadRequest(AD_Type adType, string scenario) private static void PrepareSmartLoadRequest(AD_Type adType, string scenario, string slotId)
{ {
var state = GetOrCreateCacheState(adType); var state = GetOrCreateCacheState(adType, slotId);
if (state.PendingSmartLoad) if (state.PendingSmartLoad)
{ {
return; return;
@@ -316,9 +316,9 @@ public static class TapadnSmartLoadOrchestrator
state.PreparedRequestUnix = GetNowUnixSeconds(); state.PreparedRequestUnix = GetNowUnixSeconds();
} }
private static void BeginLoadRequest(AD_Type adType, string scenario) private static void BeginLoadRequest(AD_Type adType, string scenario, string slotId)
{ {
var state = GetOrCreateCacheState(adType); var state = GetOrCreateCacheState(adType, slotId);
var now = GetNowUnixSeconds(); var now = GetNowUnixSeconds();
if (state.PreparedSmartLoad) if (state.PreparedSmartLoad)
{ {
@@ -346,9 +346,9 @@ public static class TapadnSmartLoadOrchestrator
state.PreparedRequestUnix = 0; state.PreparedRequestUnix = 0;
} }
private static void ClearPreparedLoadIfNotStarted(AD_Type adType) private static void ClearPreparedLoadIfNotStarted(AD_Type adType, string slotId)
{ {
var state = GetOrCreateCacheState(adType); var state = GetOrCreateCacheState(adType, slotId);
if (state.PendingSmartLoad) if (state.PendingSmartLoad)
{ {
return; return;
@@ -360,16 +360,16 @@ public static class TapadnSmartLoadOrchestrator
state.PreparedRequestUnix = 0; state.PreparedRequestUnix = 0;
} }
private static bool HasPendingSmartLoadForScene(AD_Type adType, string scenario) private static bool HasPendingSmartLoadForScene(AD_Type adType, string scenario, string slotId)
{ {
var state = GetOrCreateCacheState(adType); var state = GetOrCreateCacheState(adType, slotId);
return state.PendingSmartLoad && return state.PendingSmartLoad &&
string.Equals(NormalizeScenario(state.PendingOriginScene), NormalizeScenario(scenario), StringComparison.Ordinal); string.Equals(NormalizeScenario(state.PendingOriginScene), NormalizeScenario(scenario), StringComparison.Ordinal);
} }
private static void CompleteLoadRequest(AD_Type adType, string scenario, bool success) private static void CompleteLoadRequest(AD_Type adType, string scenario, bool success, string slotId)
{ {
var cacheState = GetOrCreateCacheState(adType); var cacheState = GetOrCreateCacheState(adType, slotId);
var normalizedScenario = NormalizeScenario(scenario); var normalizedScenario = NormalizeScenario(scenario);
var originScene = cacheState.PendingSmartLoad ? NormalizeScenario(cacheState.PendingOriginScene) : normalizedScenario; var originScene = cacheState.PendingSmartLoad ? NormalizeScenario(cacheState.PendingOriginScene) : normalizedScenario;
var originRecord = GetOrCreateState(adType, originScene); var originRecord = GetOrCreateState(adType, originScene);
@@ -408,23 +408,23 @@ public static class TapadnSmartLoadOrchestrator
cacheState.PendingRequestUnix = 0; cacheState.PendingRequestUnix = 0;
} }
private static void MarkImmediateHit(AD_Type adType, string scenario) private static void MarkImmediateHit(AD_Type adType, string scenario, string slotId)
{ {
var consumedScene = NormalizeScenario(scenario); var consumedScene = NormalizeScenario(scenario);
var currentSceneRecord = GetOrCreateState(adType, consumedScene); var currentSceneRecord = GetOrCreateState(adType, consumedScene);
currentSceneRecord.ImmediateHitCount = Math.Max(0, currentSceneRecord.ImmediateHitCount) + 1; currentSceneRecord.ImmediateHitCount = Math.Max(0, currentSceneRecord.ImmediateHitCount) + 1;
currentSceneRecord.LastUpdatedUnix = GetNowUnixSeconds(); currentSceneRecord.LastUpdatedUnix = GetNowUnixSeconds();
var cacheState = GetOrCreateCacheState(adType); var cacheState = GetOrCreateCacheState(adType, slotId);
if (!cacheState.HasReadyCache) if (!cacheState.HasReadyCache)
{ {
currentSceneRecord.UnattributedCacheHitCount = Math.Max(0, currentSceneRecord.UnattributedCacheHitCount) + 1; currentSceneRecord.UnattributedCacheHitCount = Math.Max(0, currentSceneRecord.UnattributedCacheHitCount) + 1;
} }
} }
private static void MarkCacheConsumed(AD_Type adType, string scenario) private static void MarkCacheConsumed(AD_Type adType, string scenario, string slotId)
{ {
var cacheState = GetOrCreateCacheState(adType); var cacheState = GetOrCreateCacheState(adType, slotId);
if (!cacheState.HasReadyCache || cacheState.Consumed) if (!cacheState.HasReadyCache || cacheState.Consumed)
{ {
return; return;
@@ -458,9 +458,9 @@ public static class TapadnSmartLoadOrchestrator
ClearReadyCache(cacheState); ClearReadyCache(cacheState);
} }
private static void MarkCacheShowFailed(AD_Type adType, string scenario) private static void MarkCacheShowFailed(AD_Type adType, string scenario, string slotId)
{ {
var cacheState = GetOrCreateCacheState(adType); var cacheState = GetOrCreateCacheState(adType, slotId);
if (!cacheState.HasReadyCache) if (!cacheState.HasReadyCache)
{ {
return; return;
@@ -479,9 +479,9 @@ public static class TapadnSmartLoadOrchestrator
ClearReadyCache(cacheState); ClearReadyCache(cacheState);
} }
private static void MarkCacheExpiredIfStale(AD_Type adType) private static void MarkCacheExpiredIfStale(AD_Type adType, string slotId)
{ {
var cacheState = GetOrCreateCacheState(adType); var cacheState = GetOrCreateCacheState(adType, slotId);
if (!cacheState.HasReadyCache) if (!cacheState.HasReadyCache)
{ {
return; return;
@@ -502,9 +502,9 @@ public static class TapadnSmartLoadOrchestrator
ClearReadyCache(cacheState); ClearReadyCache(cacheState);
} }
private static TapadnSmartLoadCacheState GetOrCreateCacheState(AD_Type adType) private static TapadnSmartLoadCacheState GetOrCreateCacheState(AD_Type adType, string slotId)
{ {
var key = (int)adType; var key = ComposeCacheKey(adType, slotId);
if (_cacheStates.TryGetValue(key, out var state) && state != null) if (_cacheStates.TryGetValue(key, out var state) && state != null)
{ {
return state; return state;
@@ -879,6 +879,16 @@ public static class TapadnSmartLoadOrchestrator
return ((int)adType) + "|" + NormalizeScenario(scenario); return ((int)adType) + "|" + NormalizeScenario(scenario);
} }
private static string ComposeCacheKey(AD_Type adType, string slotId)
{
return ((int)adType) + "|" + NormalizeSlotId(slotId);
}
private static string NormalizeSlotId(string slotId)
{
return string.IsNullOrWhiteSpace(slotId) ? "__default_slot__" : slotId.Trim();
}
private static string NormalizeScenario(string scenario) private static string NormalizeScenario(string scenario)
{ {
if (string.IsNullOrWhiteSpace(scenario)) if (string.IsNullOrWhiteSpace(scenario))

View File

@@ -56,12 +56,12 @@ public sealed class TapadnSplashPlayer : ADPlayer, IDirichletSplashAutoAdListene
} }
curState = 1; curState = 1;
TapadnSmartLoadOrchestrator.OnLoadStarted(AD_Type.Splash, AdScene); TapadnSmartLoadOrchestrator.OnLoadStarted(AD_Type.Splash, AdScene, Key);
_adNative.LoadSplashAd( _adNative.LoadSplashAd(
TapadnAdRequestFactory.BuildSplash(Key, TapadnAdController.CurrentOptions), TapadnAdRequestFactory.BuildSplash(Key, TapadnAdController.CurrentOptions),
ad => ad =>
{ {
TapadnSmartLoadOrchestrator.OnLoadResult(AD_Type.Splash, AdScene, true); TapadnSmartLoadOrchestrator.OnLoadResult(AD_Type.Splash, AdScene, true, Key);
_loadedAd?.Destroy(); _loadedAd?.Destroy();
_loadedAd = ad; _loadedAd = ad;
_loadedAd.Shown += OnManualShown; _loadedAd.Shown += OnManualShown;
@@ -73,7 +73,7 @@ public sealed class TapadnSplashPlayer : ADPlayer, IDirichletSplashAutoAdListene
}, },
error => error =>
{ {
TapadnSmartLoadOrchestrator.OnLoadResult(AD_Type.Splash, AdScene, false); TapadnSmartLoadOrchestrator.OnLoadResult(AD_Type.Splash, AdScene, false, Key);
curState = 0; curState = 0;
Debug.LogError($"[TapADN] Splash load failed. code={error.Code}, message={error.Message}"); Debug.LogError($"[TapADN] Splash load failed. code={error.Code}, message={error.Message}");
}); });
@@ -106,7 +106,7 @@ public sealed class TapadnSplashPlayer : ADPlayer, IDirichletSplashAutoAdListene
} }
_showSettled = true; _showSettled = true;
TapadnSmartLoadOrchestrator.OnShowError(AD_Type.Splash, AdScene); TapadnSmartLoadOrchestrator.OnShowError(AD_Type.Splash, AdScene, Key);
curState = 0; curState = 0;
Debug.LogError($"[TapADN] Splash show failed. code={error?.Code}, message={error?.Message}"); Debug.LogError($"[TapADN] Splash show failed. code={error?.Code}, message={error?.Message}");
adListener.OnShowError(); adListener.OnShowError();
@@ -114,7 +114,7 @@ public sealed class TapadnSplashPlayer : ADPlayer, IDirichletSplashAutoAdListene
public void OnAdShow() public void OnAdShow()
{ {
TapadnSmartLoadOrchestrator.OnShowStart(AD_Type.Splash, AdScene); TapadnSmartLoadOrchestrator.OnShowStart(AD_Type.Splash, AdScene, Key);
NotifyShowStarted(); NotifyShowStarted();
} }
@@ -136,12 +136,13 @@ public sealed class TapadnSplashPlayer : ADPlayer, IDirichletSplashAutoAdListene
public override void OnPlayRequestStarted() public override void OnPlayRequestStarted()
{ {
TapadnSmartLoadOrchestrator.OnPlayRequestStarted(AD_Type.Splash, AdScene, !UseAutoLoad() && IsReadly()); TapadnSmartLoadOrchestrator.OnPlayRequestStarted(AD_Type.Splash, AdScene, !UseAutoLoad() && IsReadly(), Key);
} }
public override void EnterAdScenario(string scenario) public override void EnterAdScenario(string scenario)
{ {
TapadnSmartLoadOrchestrator.OnEnterAdScenario(AD_Type.Splash, scenario, Key); AdScene = string.IsNullOrWhiteSpace(scenario) ? "__default__" : scenario.Trim();
TapadnSmartLoadOrchestrator.OnEnterAdScenario(AD_Type.Splash, AdScene, Key);
} }
private bool UseAutoLoad() private bool UseAutoLoad()
@@ -151,7 +152,7 @@ public sealed class TapadnSplashPlayer : ADPlayer, IDirichletSplashAutoAdListene
private void OnManualShown() private void OnManualShown()
{ {
TapadnSmartLoadOrchestrator.OnShowStart(AD_Type.Splash, AdScene); TapadnSmartLoadOrchestrator.OnShowStart(AD_Type.Splash, AdScene, Key);
NotifyShowStarted(); NotifyShowStarted();
} }

View File

@@ -2,7 +2,7 @@
"name": "com.commercialization.tapadn", "name": "com.commercialization.tapadn",
"displayName": "Commercialization.tapadn", "displayName": "Commercialization.tapadn",
"description": "TapADN / Dirichlet mediation implementation for CC-Framework.Commercialization.", "description": "TapADN / Dirichlet mediation implementation for CC-Framework.Commercialization.",
"version": "1.0.3", "version": "1.0.8",
"unity": "2022.3", "unity": "2022.3",
"license": "MIT", "license": "MIT",
"repository": { "repository": {
@@ -15,7 +15,7 @@
"url": "https://gitee.com/foldcc" "url": "https://gitee.com/foldcc"
}, },
"dependencies": { "dependencies": {
"com.foldcc.cc-framework.commercialization": "http://private.lightyears.ltd:18650/foldcc/CC-Framework.Commercialization.git#1.0.15" "com.foldcc.cc-framework.commercialization": "http://private.lightyears.ltd:18650/foldcc/CC-Framework.Commercialization.git#1.0.16"
}, },
"samples": [ "samples": [
{ {

View File

@@ -12,7 +12,7 @@
## 目录 ## 目录
* `Assets/DirichletMediation`: 官方聚合 Unity SDK `4.2.5.0`,已删除官方 Sample。 * `Assets/DirichletMediation`: 官方聚合 Unity SDK 基础层 `4.2.5.0`,已删除官方 SampleAndroid native AAR 单独升级到 `4.2.7.3`
* `Assets/Plugins/Android`: 官方 Android AAR、Manifest、ProGuard、本地微信 OpenSDK AAR。 * `Assets/Plugins/Android`: 官方 Android AAR、Manifest、ProGuard、本地微信 OpenSDK AAR。
* `Assets/Plugins/iOS`: iOS Objective-C++ bridge。 * `Assets/Plugins/iOS`: iOS Objective-C++ bridge。
* `Assets/Tapadn_Adapter/Runtime/Scripts`: 商业化抽象层适配。 * `Assets/Tapadn_Adapter/Runtime/Scripts`: 商业化抽象层适配。

View File

@@ -9,7 +9,7 @@
```json ```json
{ {
"com.foldcc.cc-framework.commercialization": "http://private.lightyears.ltd:18650/foldcc/CC-Framework.Commercialization.git#1.0.15", "com.foldcc.cc-framework.commercialization": "http://private.lightyears.ltd:18650/foldcc/CC-Framework.Commercialization.git#1.0.15",
"com.commercialization.tapadn": "http://private.lightyears.ltd:18650/foldcc/Commercialization.tapadn.git#1.0.3" "com.commercialization.tapadn": "http://private.lightyears.ltd:18650/foldcc/Commercialization.tapadn.git?path=/Assets#1.0.5"
} }
``` ```
@@ -73,6 +73,10 @@ ADManager.Instance.Init(callback, userId, adConfig, new TapadnAdController());
* `tapadn.rewarded_max_load_attempts` * `tapadn.rewarded_max_load_attempts`
* `tapadn.rewarded_load_retry_delay_ms` * `tapadn.rewarded_load_retry_delay_ms`
* `tapadn.rewarded_show_timeout_ms` * `tapadn.rewarded_show_timeout_ms`
* `tapadn.rewarded_scene_slot.<scene_id>`(激励视频场景广告位映射,值为对应 SpaceId
* `tapadn.rewarded_scene_slots`(激励视频场景广告位批量映射,格式 `scene_a=10001,scene_b=10002`
* `tapadn.rewarded_scene_slots_json`(激励视频场景广告位 JSON格式见下方
* `tapadn.rewarded_cache_max_age_seconds`(可选本地缓存年龄上限;默认 `600` 秒,设为 `0` 表示只使用 SDK `IsValid` 判断)
* `tapadn.interstitial_auto_load` * `tapadn.interstitial_auto_load`
* `tapadn.interstitial_prewarm_on_init` * `tapadn.interstitial_prewarm_on_init`
* `tapadn.interstitial_max_load_attempts` * `tapadn.interstitial_max_load_attempts`
@@ -92,6 +96,63 @@ ADManager.Instance.Init(callback, userId, adConfig, new TapadnAdController());
默认激励、插屏、开屏都使用手动 load/show如无特殊策略验证需求不建议开启 auto-ad。若要做 auto-ad AB 测试,再将对应 `*_auto_load` 设为 `true` 默认激励、插屏、开屏都使用手动 load/show如无特殊策略验证需求不建议开启 auto-ad。若要做 auto-ad AB 测试,再将对应 `*_auto_load` 设为 `true`
### 激励视频场景广告位
激励视频支持按游戏场景路由不同 TapADN SpaceId。项目层继续调用 `ADManager.EnterAdScenario(AD_Type.AwardVideo, sceneId)``ADManager.AsyncPlayAD(AD_Type.AwardVideo, sceneId, callback)`TapADN adapter 会按 `sceneId` 查表:
* 命中 `tapadn.rewarded_scene_slot.<scene_id>`、批量配置,或 `CommonKeyValues``scene_id=SpaceId` 的兼容配置时,使用该场景 SpaceId。
* 未传场景、场景为空、场景未配置、配置的 SpaceId 非法时,回退到 `BaseAwardAdKeyValue.value` 默认激励视频广告位。
* 手动 load/show 模式下,缓存按 SpaceId 隔离A 场景加载的激励视频不会被 B 场景误认为 ready。
* Editor/样例调试时,进入场景、加载和播放请求会在 Console 输出当前解析到的 SpaceId 及来源,格式类似 `slot=200101, source=scene`
单项配置示例:
```csharp
adConfig.CommonKeyValues.Add(new AdKeyValue
{
key = "tapadn.rewarded_scene_slot.level_clear",
value = "200101"
});
```
兼容已有业务配置示例:
```csharp
adConfig.CommonKeyValues.Add(new AdKeyValue
{
key = "PlantUnlock",
value = "200101"
});
```
批量字符串配置示例:
```csharp
adConfig.CommonKeyValues.Add(new AdKeyValue
{
key = "tapadn.rewarded_scene_slots",
value = "level_clear=200101,daily_bonus=200102"
});
```
JSON 配置示例:
```json
{
"Mappings": [
{ "Scene": "level_clear", "SlotId": "200101" },
{ "Scene": "daily_bonus", "SlotId": "200102" }
]
}
```
生命周期策略按官方文档保持保守:
* `DirichletAdNative` 仍由每个 TapADN player 统一持有,不在每个场景重复创建。
* 手动加载返回的 `DirichletRewardVideoAd` 按 SpaceId 缓存展示关闭、展示失败、SDK `IsValid == false` 或超过 `tapadn.rewarded_cache_max_age_seconds` 后销毁。
* 官方文档没有给出固定过期秒数;本模块默认 10 分钟未消费主动销毁,同时每次 ready/show 前仍调用 SDK `IsValid`
* Android auto-ad 仍交给官方 `ShowRewardVideoAutoAd` / `PreLoad` 的 native 缓存管理iOS/Editor fallback 仍是 load 成功后立即 show不承诺 native 缓存语义。
### 智能预加载(实验) ### 智能预加载(实验)
默认会按“场景进入次数 + 场景播放请求次数”维护一个小样本统计: 默认会按“场景进入次数 + 场景播放请求次数”维护一个小样本统计:
@@ -138,11 +199,14 @@ ADManager.Instance.Init(callback, userId, adConfig, new TapadnAdController());
包内包含官方 `DirichletMediation` SDK、Android AAR、iOS bridge、EDM4U 依赖声明和构建后处理。 包内包含官方 `DirichletMediation` SDK、Android AAR、iOS bridge、EDM4U 依赖声明和构建后处理。
维护和升级说明见 `SDK_MAINTENANCE.md`其中记录了官方源码改动清单、Android/iOS/Unity SDK 升级步骤和发布流程。
构建后处理会自动补齐: 构建后处理会自动补齐:
* TapADN 所需权限。 * TapADN 所需权限。
* TapADN `TapADFileProvider``tapad_ad_file_path.xml` * TapADN `TapADFileProvider``tapad_ad_file_path.xml`
* 微信 OpenSDK `WXEntryActivity``queries`、本地 `wechat-sdk-android-6.8.34.aar` * 微信 OpenSDK `WXEntryActivity``queries`、本地 `wechat-sdk-android-6.8.34.aar`
* Pangle `com.pangle.cn:ads-sdk-pro:7.6.1.2` 和 GDT `com.qq.e.union:union:4.690.1560` Maven 依赖。
* `android.useAndroidX=true``android.enableJetifier=true` * `android.useAndroidX=true``android.enableJetifier=true`
包内不默认暴露可视化编辑面板;调试样例通过 `Samples~` 作为可选导入内容。 包内不默认暴露可视化编辑面板;调试样例通过 `Samples~` 作为可选导入内容。