You've already forked Commercialization.tapadn
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2293d2803a | |||
| b6370d5bf8 | |||
| 3f527fa94d | |||
| 9c99482b2d | |||
| 5810501618 |
@@ -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]
|
||||
|
||||
### 修复
|
||||
|
||||
@@ -23,6 +23,8 @@ namespace Dirichlet.Mediation.Editor
|
||||
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_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)
|
||||
|
||||
@@ -157,6 +159,16 @@ namespace Dirichlet.Mediation.Editor
|
||||
}
|
||||
|
||||
// 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.github.bumptech.glide:glide:4.9.0'");
|
||||
depsBlock.AppendLine(" implementation 'com.android.support:support-v4:28.0.0'");
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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:
|
||||
|
||||
Binary file not shown.
@@ -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
115
Assets/SDK_MAINTENANCE.md
Normal 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 SpaceId,A 缓存不会被 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`。
|
||||
7
Assets/SDK_MAINTENANCE.md.meta
Normal file
7
Assets/SDK_MAINTENANCE.md.meta
Normal file
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dcf571baef764df0aa6789990d47c36a
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -162,6 +162,7 @@ public sealed class TapadnIAAAdDebugSampleGui : MonoBehaviour
|
||||
GUILayout.Label($"Last EventLog: {_lastEventLog}", _textStyle);
|
||||
GUILayout.Label($"Last SDK Version: {DisplayValue(TapadnAdController.LastSdkVersion)}", _textStyle);
|
||||
GUILayout.Label($"Last Init Error: {DisplayValue(TapadnAdController.LastInitError)}", _textStyle);
|
||||
GUILayout.Label($"Rewarded Selected SpaceId: {GetResolvedRewardedSlotForDisplay(rewardedScenario)}", _textStyle);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -250,6 +251,7 @@ public sealed class TapadnIAAAdDebugSampleGui : MonoBehaviour
|
||||
|
||||
if (EnsureInitialized(label))
|
||||
{
|
||||
LogRewardedResolvedSlot(adType, scenario, "Enter scenario");
|
||||
ADManager.Instance.EnterAdScenario(adType, scenario);
|
||||
AppendLog($"Enter scenario requested. type={adType}, scenario={scenario}");
|
||||
}
|
||||
@@ -278,6 +280,7 @@ public sealed class TapadnIAAAdDebugSampleGui : MonoBehaviour
|
||||
|
||||
if (EnsureInitialized(label))
|
||||
{
|
||||
LogRewardedResolvedSlot(adType, scenario, "AsyncPlayAD");
|
||||
ADManager.Instance.AsyncPlayAD(adType, scenario, 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($"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)
|
||||
|
||||
@@ -3,7 +3,7 @@ using Dirichlet.Mediation;
|
||||
using Runtime.ADAggregator;
|
||||
using UnityEngine;
|
||||
|
||||
public sealed class TapadnAdController : IAdController
|
||||
public sealed class TapadnAdController : IAdController, IAdEditorDiagnostics
|
||||
{
|
||||
public static TapadnControllerOptions CurrentOptions { get; private set; }
|
||||
public static string LastSdkVersion { get; private set; }
|
||||
@@ -73,4 +73,41 @@ public sealed class TapadnAdController : IAdController
|
||||
{
|
||||
_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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Dirichlet.Mediation;
|
||||
using Runtime.ADAggregator;
|
||||
using UnityEngine;
|
||||
@@ -7,8 +8,11 @@ public sealed class TapadnAwardVideoPlayer : ADPlayer, IDirichletRewardVideoAuto
|
||||
{
|
||||
private const float RewardCloseSettleDelaySeconds = 0.25f;
|
||||
|
||||
private readonly Dictionary<string, RewardedSlotCache> _slotCaches = new Dictionary<string, RewardedSlotCache>(StringComparer.Ordinal);
|
||||
|
||||
private DirichletAdNative _adNative;
|
||||
private DirichletRewardVideoAd _loadedAd;
|
||||
private string _defaultSlotId;
|
||||
private string _activeSlotId;
|
||||
private bool _rewardVerified;
|
||||
private bool _rewardVerifyReceived;
|
||||
private bool _closePendingRewardVerify;
|
||||
@@ -22,30 +26,47 @@ public sealed class TapadnAwardVideoPlayer : ADPlayer, IDirichletRewardVideoAuto
|
||||
|
||||
public override void OnInit()
|
||||
{
|
||||
_defaultSlotId = Key;
|
||||
_adNative = DirichletAdManager.CreateAdNative();
|
||||
}
|
||||
|
||||
public override bool IsReadly()
|
||||
{
|
||||
var slotId = ResolveCurrentSlotId();
|
||||
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;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cache != null && cache.Loading)
|
||||
{
|
||||
curState = 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cache != null)
|
||||
{
|
||||
RemoveCache(slotId);
|
||||
}
|
||||
|
||||
curState = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
return;
|
||||
}
|
||||
@@ -55,7 +76,7 @@ public sealed class TapadnAwardVideoPlayer : ADPlayer, IDirichletRewardVideoAuto
|
||||
curState = 2;
|
||||
try
|
||||
{
|
||||
_adNative.PreLoad(TapadnAdRequestFactory.BuildRewarded(Key, TapadnAdController.CurrentOptions), 3);
|
||||
_adNative.PreLoad(TapadnAdRequestFactory.BuildRewarded(slotId, TapadnAdController.CurrentOptions), 3);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
@@ -64,33 +85,37 @@ public sealed class TapadnAwardVideoPlayer : ADPlayer, IDirichletRewardVideoAuto
|
||||
return;
|
||||
}
|
||||
|
||||
if (curState == 1 || IsReadly())
|
||||
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);
|
||||
TapadnSmartLoadOrchestrator.OnLoadStarted(AD_Type.AwardVideo, AdScene, slotId);
|
||||
_adNative.LoadRewardVideoAd(
|
||||
TapadnAdRequestFactory.BuildRewarded(Key, TapadnAdController.CurrentOptions),
|
||||
TapadnAdRequestFactory.BuildRewarded(slotId, TapadnAdController.CurrentOptions),
|
||||
ad =>
|
||||
{
|
||||
TapadnSmartLoadOrchestrator.OnLoadResult(AD_Type.AwardVideo, AdScene, true);
|
||||
_loadedAd?.Destroy();
|
||||
_loadedAd = ad;
|
||||
_loadedAd.Shown += OnManualShown;
|
||||
_loadedAd.Clicked += OnManualClicked;
|
||||
_loadedAd.ShowFailed += OnManualShowFailed;
|
||||
_loadedAd.RewardVerified += OnManualRewardVerify;
|
||||
_loadedAd.Closed += OnManualClosed;
|
||||
curState = 2;
|
||||
Debug.Log($"[TapADN] Rewarded loaded. slot={Key}");
|
||||
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);
|
||||
curState = 0;
|
||||
Debug.LogError($"[TapADN] Rewarded load failed. code={error.Code}, message={error.Message}");
|
||||
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}");
|
||||
});
|
||||
}
|
||||
|
||||
@@ -104,16 +129,21 @@ public sealed class TapadnAwardVideoPlayer : ADPlayer, IDirichletRewardVideoAuto
|
||||
_showSettled = false;
|
||||
_rewardCloseSettleHandler?.Kill();
|
||||
_rewardCloseSettleHandler = null;
|
||||
_activeSlotId = ResolveCurrentSlotId(out var mapped);
|
||||
Key = _activeSlotId;
|
||||
curState = 0;
|
||||
Debug.Log($"[TapADN] Rewarded show requested. scene={NormalizeScenario(AdScene)}, slot={_activeSlotId}, source={GetSlotSource(mapped)}, auto={UseAutoLoad()}");
|
||||
|
||||
if (UseAutoLoad())
|
||||
{
|
||||
_adNative.ShowRewardVideoAutoAd(TapadnAdRequestFactory.BuildRewarded(Key, TapadnAdController.CurrentOptions), this);
|
||||
_adNative.ShowRewardVideoAutoAd(TapadnAdRequestFactory.BuildRewarded(_activeSlotId, TapadnAdController.CurrentOptions), this);
|
||||
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"));
|
||||
return;
|
||||
}
|
||||
@@ -130,14 +160,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}");
|
||||
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);
|
||||
TapadnSmartLoadOrchestrator.OnShowStart(AD_Type.AwardVideo, AdScene, _activeSlotId);
|
||||
NotifyShowStarted();
|
||||
}
|
||||
|
||||
@@ -175,12 +205,41 @@ public sealed class TapadnAwardVideoPlayer : ADPlayer, IDirichletRewardVideoAuto
|
||||
|
||||
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)
|
||||
{
|
||||
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()
|
||||
@@ -188,8 +247,66 @@ public sealed class TapadnAwardVideoPlayer : ADPlayer, IDirichletRewardVideoAuto
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -207,13 +325,49 @@ public sealed class TapadnAwardVideoPlayer : ADPlayer, IDirichletRewardVideoAuto
|
||||
OnRewardVerify(args);
|
||||
}
|
||||
|
||||
private void OnManualClosed()
|
||||
private void OnManualClosed(string slotId)
|
||||
{
|
||||
_loadedAd?.Destroy();
|
||||
_loadedAd = null;
|
||||
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)
|
||||
@@ -228,4 +382,22 @@ public sealed class TapadnAwardVideoPlayer : ADPlayer, IDirichletRewardVideoAuto
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Runtime.ADAggregator;
|
||||
|
||||
public static class TapadnCommercialization
|
||||
@@ -31,4 +32,31 @@ public static class TapadnCommercialization
|
||||
config.BaseSplashAdKeyValue = new AdKeyValue { key = "splash", value = splashSlotId };
|
||||
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()
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ using UnityEngine;
|
||||
|
||||
public sealed class TapadnControllerOptions
|
||||
{
|
||||
private const string TapadnKeyPrefix = "tapadn.";
|
||||
|
||||
public const string MediaNameKey = "tapadn.media_name";
|
||||
public const string MediaIdKey = "tapadn.media_id";
|
||||
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 RewardedLoadRetryDelayMsKey = "tapadn.rewarded_load_retry_delay_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 RewardAmountKey = "tapadn.reward_amount";
|
||||
public const string InterstitialAutoLoadKey = "tapadn.interstitial_auto_load";
|
||||
@@ -64,6 +70,8 @@ public sealed class TapadnControllerOptions
|
||||
public int RewardedMaxLoadAttempts { get; set; } = 1;
|
||||
public int RewardedLoadRetryDelayMs { get; set; } = 500;
|
||||
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 int RewardAmount { get; set; } = 1;
|
||||
public bool InterstitialAutoLoad { get; set; } = false;
|
||||
@@ -205,6 +213,11 @@ public sealed class TapadnControllerOptions
|
||||
RewardedMaxLoadAttempts = incoming.RewardedMaxLoadAttempts;
|
||||
RewardedLoadRetryDelayMs = incoming.RewardedLoadRetryDelayMs;
|
||||
RewardedShowTimeoutMs = incoming.RewardedShowTimeoutMs;
|
||||
RewardedCacheMaxAgeSeconds = incoming.RewardedCacheMaxAgeSeconds;
|
||||
if (incoming.RewardedSceneSlotIds != null && incoming.RewardedSceneSlotIds.Count > 0)
|
||||
{
|
||||
RewardedSceneSlotIds = CloneSceneSlotMap(incoming.RewardedSceneSlotIds);
|
||||
}
|
||||
RewardName = incoming.RewardName ?? RewardName;
|
||||
RewardAmount = incoming.RewardAmount;
|
||||
InterstitialAutoLoad = incoming.InterstitialAutoLoad;
|
||||
@@ -266,6 +279,8 @@ public sealed class TapadnControllerOptions
|
||||
RewardedMaxLoadAttempts = GetInt(map, RewardedMaxLoadAttemptsKey) ?? RewardedMaxLoadAttempts;
|
||||
RewardedLoadRetryDelayMs = GetInt(map, RewardedLoadRetryDelayMsKey) ?? RewardedLoadRetryDelayMs;
|
||||
RewardedShowTimeoutMs = GetInt(map, RewardedShowTimeoutMsKey) ?? RewardedShowTimeoutMs;
|
||||
RewardedCacheMaxAgeSeconds = Math.Max(0, GetInt(map, RewardedCacheMaxAgeSecondsKey) ?? RewardedCacheMaxAgeSeconds);
|
||||
ApplyRewardedSceneSlots(map);
|
||||
RewardName = GetString(map, RewardNameKey) ?? RewardName;
|
||||
RewardAmount = GetInt(map, RewardAmountKey) ?? RewardAmount;
|
||||
InterstitialAutoLoad = GetBool(map, InterstitialAutoLoadKey) ?? InterstitialAutoLoad;
|
||||
@@ -286,6 +301,162 @@ public sealed class TapadnControllerOptions
|
||||
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)
|
||||
{
|
||||
foreach (var key in keys)
|
||||
@@ -383,4 +554,17 @@ public sealed class TapadnControllerOptions
|
||||
|
||||
return value.ToString();
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
private sealed class TapadnSceneSlotMapConfig
|
||||
{
|
||||
public List<TapadnSceneSlotMapping> Mappings;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
private sealed class TapadnSceneSlotMapping
|
||||
{
|
||||
public string Scene;
|
||||
public string SlotId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,12 +56,12 @@ public sealed class TapadnInteractionPlayer : ADPlayer, IDirichletInterstitialAu
|
||||
}
|
||||
|
||||
curState = 1;
|
||||
TapadnSmartLoadOrchestrator.OnLoadStarted(AD_Type.Interaction, AdScene);
|
||||
TapadnSmartLoadOrchestrator.OnLoadStarted(AD_Type.Interaction, AdScene, Key);
|
||||
_adNative.LoadInterstitialAd(
|
||||
TapadnAdRequestFactory.BuildInterstitial(Key, TapadnAdController.CurrentOptions),
|
||||
ad =>
|
||||
{
|
||||
TapadnSmartLoadOrchestrator.OnLoadResult(AD_Type.Interaction, AdScene, true);
|
||||
TapadnSmartLoadOrchestrator.OnLoadResult(AD_Type.Interaction, AdScene, true, Key);
|
||||
_loadedAd?.Destroy();
|
||||
_loadedAd = ad;
|
||||
_loadedAd.Shown += OnManualShown;
|
||||
@@ -73,7 +73,7 @@ public sealed class TapadnInteractionPlayer : ADPlayer, IDirichletInterstitialAu
|
||||
},
|
||||
error =>
|
||||
{
|
||||
TapadnSmartLoadOrchestrator.OnLoadResult(AD_Type.Interaction, AdScene, false);
|
||||
TapadnSmartLoadOrchestrator.OnLoadResult(AD_Type.Interaction, AdScene, false, Key);
|
||||
curState = 0;
|
||||
Debug.LogError($"[TapADN] Interstitial load failed. code={error.Code}, message={error.Message}");
|
||||
});
|
||||
@@ -106,7 +106,7 @@ public sealed class TapadnInteractionPlayer : ADPlayer, IDirichletInterstitialAu
|
||||
}
|
||||
|
||||
_showSettled = true;
|
||||
TapadnSmartLoadOrchestrator.OnShowError(AD_Type.Interaction, AdScene);
|
||||
TapadnSmartLoadOrchestrator.OnShowError(AD_Type.Interaction, AdScene, Key);
|
||||
curState = 0;
|
||||
Debug.LogError($"[TapADN] Interstitial show failed. code={error?.Code}, message={error?.Message}");
|
||||
adListener.OnShowError();
|
||||
@@ -114,7 +114,7 @@ public sealed class TapadnInteractionPlayer : ADPlayer, IDirichletInterstitialAu
|
||||
|
||||
public void OnAdShow()
|
||||
{
|
||||
TapadnSmartLoadOrchestrator.OnShowStart(AD_Type.Interaction, AdScene);
|
||||
TapadnSmartLoadOrchestrator.OnShowStart(AD_Type.Interaction, AdScene, Key);
|
||||
NotifyShowStarted();
|
||||
}
|
||||
|
||||
@@ -136,12 +136,13 @@ public sealed class TapadnInteractionPlayer : ADPlayer, IDirichletInterstitialAu
|
||||
|
||||
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)
|
||||
{
|
||||
TapadnSmartLoadOrchestrator.OnEnterAdScenario(AD_Type.Interaction, scenario, Key);
|
||||
AdScene = string.IsNullOrWhiteSpace(scenario) ? "__default__" : scenario.Trim();
|
||||
TapadnSmartLoadOrchestrator.OnEnterAdScenario(AD_Type.Interaction, AdScene, Key);
|
||||
}
|
||||
|
||||
private bool UseAutoLoad()
|
||||
@@ -151,7 +152,7 @@ public sealed class TapadnInteractionPlayer : ADPlayer, IDirichletInterstitialAu
|
||||
|
||||
private void OnManualShown()
|
||||
{
|
||||
TapadnSmartLoadOrchestrator.OnShowStart(AD_Type.Interaction, AdScene);
|
||||
TapadnSmartLoadOrchestrator.OnShowStart(AD_Type.Interaction, AdScene, Key);
|
||||
NotifyShowStarted();
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ public static class TapadnSmartLoadOrchestrator
|
||||
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 Dictionary<string, TapadnSmartLoadCacheState> _cacheStates = new Dictionary<string, TapadnSmartLoadCacheState>(StringComparer.Ordinal);
|
||||
private static bool _initialized;
|
||||
private static bool _enabled;
|
||||
|
||||
@@ -116,7 +116,7 @@ public static class TapadnSmartLoadOrchestrator
|
||||
_enabled = options?.SmartPreloadEnabled ?? false;
|
||||
_initialized = true;
|
||||
_states = new Dictionary<string, TapadnSmartLoadSceneState>(StringComparer.Ordinal);
|
||||
_cacheStates = new Dictionary<int, TapadnSmartLoadCacheState>();
|
||||
_cacheStates = new Dictionary<string, TapadnSmartLoadCacheState>(StringComparer.Ordinal);
|
||||
LoadStates();
|
||||
EnsureDefaultPolicies(_runtimeConfig);
|
||||
}
|
||||
@@ -135,10 +135,10 @@ public static class TapadnSmartLoadOrchestrator
|
||||
record.ShowRequestCount = Math.Max(0, record.ShowRequestCount);
|
||||
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)
|
||||
{
|
||||
@@ -152,49 +152,49 @@ public static class TapadnSmartLoadOrchestrator
|
||||
record.LastUpdatedUnix = GetNowUnixSeconds();
|
||||
if (cacheReadyAtRequest)
|
||||
{
|
||||
MarkImmediateHit(adType, normalizedScenario);
|
||||
MarkImmediateHit(adType, normalizedScenario, slotId);
|
||||
}
|
||||
else
|
||||
{
|
||||
MarkCacheExpiredIfStale(adType);
|
||||
MarkCacheExpiredIfStale(adType, slotId);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
BeginLoadRequest(adType, scenario);
|
||||
BeginLoadRequest(adType, scenario, slotId);
|
||||
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)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CompleteLoadRequest(adType, scenario, success);
|
||||
CompleteLoadRequest(adType, scenario, success, slotId);
|
||||
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)
|
||||
{
|
||||
@@ -204,11 +204,11 @@ public static class TapadnSmartLoadOrchestrator
|
||||
var record = GetOrCreateState(adType, NormalizeScenario(scenario));
|
||||
record.ShowStartCount = Math.Max(0, record.ShowStartCount) + 1;
|
||||
record.LastUpdatedUnix = GetNowUnixSeconds();
|
||||
MarkCacheConsumed(adType, scenario);
|
||||
MarkCacheConsumed(adType, scenario, slotId);
|
||||
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)
|
||||
{
|
||||
@@ -218,11 +218,11 @@ public static class TapadnSmartLoadOrchestrator
|
||||
var record = GetOrCreateState(adType, NormalizeScenario(scenario));
|
||||
record.ShowFailureCount = Math.Max(0, record.ShowFailureCount) + 1;
|
||||
record.LastUpdatedUnix = GetNowUnixSeconds();
|
||||
MarkCacheShowFailed(adType, scenario);
|
||||
MarkCacheShowFailed(adType, scenario, slotId);
|
||||
SaveStates();
|
||||
}
|
||||
|
||||
private static void TryPreload(AD_Type adType, string scenario)
|
||||
private static void TryPreload(AD_Type adType, string scenario, string slotId)
|
||||
{
|
||||
if (!_initialized || !_enabled)
|
||||
{
|
||||
@@ -257,11 +257,11 @@ public static class TapadnSmartLoadOrchestrator
|
||||
return;
|
||||
}
|
||||
|
||||
MarkCacheExpiredIfStale(adType);
|
||||
OnLoadRequested(adType, scenario);
|
||||
MarkCacheExpiredIfStale(adType, slotId);
|
||||
OnLoadRequested(adType, scenario, slotId);
|
||||
ADManager.Instance.LoadAD(adType);
|
||||
var smartLoadStarted = HasPendingSmartLoadForScene(adType, scenario);
|
||||
ClearPreparedLoadIfNotStarted(adType);
|
||||
var smartLoadStarted = HasPendingSmartLoadForScene(adType, scenario, slotId);
|
||||
ClearPreparedLoadIfNotStarted(adType, slotId);
|
||||
if (!smartLoadStarted)
|
||||
{
|
||||
return;
|
||||
@@ -302,9 +302,9 @@ public static class TapadnSmartLoadOrchestrator
|
||||
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)
|
||||
{
|
||||
return;
|
||||
@@ -316,9 +316,9 @@ public static class TapadnSmartLoadOrchestrator
|
||||
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();
|
||||
if (state.PreparedSmartLoad)
|
||||
{
|
||||
@@ -346,9 +346,9 @@ public static class TapadnSmartLoadOrchestrator
|
||||
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)
|
||||
{
|
||||
return;
|
||||
@@ -360,16 +360,16 @@ public static class TapadnSmartLoadOrchestrator
|
||||
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 &&
|
||||
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 originScene = cacheState.PendingSmartLoad ? NormalizeScenario(cacheState.PendingOriginScene) : normalizedScenario;
|
||||
var originRecord = GetOrCreateState(adType, originScene);
|
||||
@@ -408,23 +408,23 @@ public static class TapadnSmartLoadOrchestrator
|
||||
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 currentSceneRecord = GetOrCreateState(adType, consumedScene);
|
||||
currentSceneRecord.ImmediateHitCount = Math.Max(0, currentSceneRecord.ImmediateHitCount) + 1;
|
||||
currentSceneRecord.LastUpdatedUnix = GetNowUnixSeconds();
|
||||
|
||||
var cacheState = GetOrCreateCacheState(adType);
|
||||
var cacheState = GetOrCreateCacheState(adType, slotId);
|
||||
if (!cacheState.HasReadyCache)
|
||||
{
|
||||
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)
|
||||
{
|
||||
return;
|
||||
@@ -458,9 +458,9 @@ public static class TapadnSmartLoadOrchestrator
|
||||
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)
|
||||
{
|
||||
return;
|
||||
@@ -479,9 +479,9 @@ public static class TapadnSmartLoadOrchestrator
|
||||
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)
|
||||
{
|
||||
return;
|
||||
@@ -502,9 +502,9 @@ public static class TapadnSmartLoadOrchestrator
|
||||
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)
|
||||
{
|
||||
return state;
|
||||
@@ -879,6 +879,16 @@ public static class TapadnSmartLoadOrchestrator
|
||||
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)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(scenario))
|
||||
|
||||
@@ -56,12 +56,12 @@ public sealed class TapadnSplashPlayer : ADPlayer, IDirichletSplashAutoAdListene
|
||||
}
|
||||
|
||||
curState = 1;
|
||||
TapadnSmartLoadOrchestrator.OnLoadStarted(AD_Type.Splash, AdScene);
|
||||
TapadnSmartLoadOrchestrator.OnLoadStarted(AD_Type.Splash, AdScene, Key);
|
||||
_adNative.LoadSplashAd(
|
||||
TapadnAdRequestFactory.BuildSplash(Key, TapadnAdController.CurrentOptions),
|
||||
ad =>
|
||||
{
|
||||
TapadnSmartLoadOrchestrator.OnLoadResult(AD_Type.Splash, AdScene, true);
|
||||
TapadnSmartLoadOrchestrator.OnLoadResult(AD_Type.Splash, AdScene, true, Key);
|
||||
_loadedAd?.Destroy();
|
||||
_loadedAd = ad;
|
||||
_loadedAd.Shown += OnManualShown;
|
||||
@@ -73,7 +73,7 @@ public sealed class TapadnSplashPlayer : ADPlayer, IDirichletSplashAutoAdListene
|
||||
},
|
||||
error =>
|
||||
{
|
||||
TapadnSmartLoadOrchestrator.OnLoadResult(AD_Type.Splash, AdScene, false);
|
||||
TapadnSmartLoadOrchestrator.OnLoadResult(AD_Type.Splash, AdScene, false, Key);
|
||||
curState = 0;
|
||||
Debug.LogError($"[TapADN] Splash load failed. code={error.Code}, message={error.Message}");
|
||||
});
|
||||
@@ -106,7 +106,7 @@ public sealed class TapadnSplashPlayer : ADPlayer, IDirichletSplashAutoAdListene
|
||||
}
|
||||
|
||||
_showSettled = true;
|
||||
TapadnSmartLoadOrchestrator.OnShowError(AD_Type.Splash, AdScene);
|
||||
TapadnSmartLoadOrchestrator.OnShowError(AD_Type.Splash, AdScene, Key);
|
||||
curState = 0;
|
||||
Debug.LogError($"[TapADN] Splash show failed. code={error?.Code}, message={error?.Message}");
|
||||
adListener.OnShowError();
|
||||
@@ -114,7 +114,7 @@ public sealed class TapadnSplashPlayer : ADPlayer, IDirichletSplashAutoAdListene
|
||||
|
||||
public void OnAdShow()
|
||||
{
|
||||
TapadnSmartLoadOrchestrator.OnShowStart(AD_Type.Splash, AdScene);
|
||||
TapadnSmartLoadOrchestrator.OnShowStart(AD_Type.Splash, AdScene, Key);
|
||||
NotifyShowStarted();
|
||||
}
|
||||
|
||||
@@ -136,12 +136,13 @@ public sealed class TapadnSplashPlayer : ADPlayer, IDirichletSplashAutoAdListene
|
||||
|
||||
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)
|
||||
{
|
||||
TapadnSmartLoadOrchestrator.OnEnterAdScenario(AD_Type.Splash, scenario, Key);
|
||||
AdScene = string.IsNullOrWhiteSpace(scenario) ? "__default__" : scenario.Trim();
|
||||
TapadnSmartLoadOrchestrator.OnEnterAdScenario(AD_Type.Splash, AdScene, Key);
|
||||
}
|
||||
|
||||
private bool UseAutoLoad()
|
||||
@@ -151,7 +152,7 @@ public sealed class TapadnSplashPlayer : ADPlayer, IDirichletSplashAutoAdListene
|
||||
|
||||
private void OnManualShown()
|
||||
{
|
||||
TapadnSmartLoadOrchestrator.OnShowStart(AD_Type.Splash, AdScene);
|
||||
TapadnSmartLoadOrchestrator.OnShowStart(AD_Type.Splash, AdScene, Key);
|
||||
NotifyShowStarted();
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "com.commercialization.tapadn",
|
||||
"displayName": "Commercialization.tapadn",
|
||||
"description": "TapADN / Dirichlet mediation implementation for CC-Framework.Commercialization.",
|
||||
"version": "1.0.3",
|
||||
"version": "1.0.8",
|
||||
"unity": "2022.3",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
@@ -15,7 +15,7 @@
|
||||
"url": "https://gitee.com/foldcc"
|
||||
},
|
||||
"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": [
|
||||
{
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
## 目录
|
||||
|
||||
* `Assets/DirichletMediation`: 官方聚合 Unity SDK `4.2.5.0`,已删除官方 Sample。
|
||||
* `Assets/DirichletMediation`: 官方聚合 Unity SDK 基础层 `4.2.5.0`,已删除官方 Sample;Android native AAR 单独升级到 `4.2.7.3`。
|
||||
* `Assets/Plugins/Android`: 官方 Android AAR、Manifest、ProGuard、本地微信 OpenSDK AAR。
|
||||
* `Assets/Plugins/iOS`: iOS Objective-C++ bridge。
|
||||
* `Assets/Tapadn_Adapter/Runtime/Scripts`: 商业化抽象层适配。
|
||||
|
||||
66
README.md
66
README.md
@@ -9,7 +9,7 @@
|
||||
```json
|
||||
{
|
||||
"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_load_retry_delay_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_prewarm_on_init`
|
||||
* `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`。
|
||||
|
||||
### 激励视频场景广告位
|
||||
|
||||
激励视频支持按游戏场景路由不同 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 依赖声明和构建后处理。
|
||||
|
||||
维护和升级说明见 `SDK_MAINTENANCE.md`,其中记录了官方源码改动清单、Android/iOS/Unity SDK 升级步骤和发布流程。
|
||||
|
||||
构建后处理会自动补齐:
|
||||
|
||||
* TapADN 所需权限。
|
||||
* TapADN `TapADFileProvider` 与 `tapad_ad_file_path.xml`。
|
||||
* 微信 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`。
|
||||
|
||||
包内不默认暴露可视化编辑面板;调试样例通过 `Samples~` 作为可选导入内容。
|
||||
|
||||
Reference in New Issue
Block a user