> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cloudx.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Trusted Arbiter

> 在 Unity 游戏中比较 CloudX 出价与受支持的第三方出价

Trusted Arbiter 会比较已加载的 CloudX 出价与受支持的第三方出价，并返回选中的平台。从 Unity SDK 4.1.0 起可用（底层依赖 Android SDK 4.1.1 和 iOS SDK 3.4.1），支持 CloudX、Unity LevelPlay 和 PubMatic 出价输入。

<Info>
  请在 `CloudXSdk.Initialize()` 完成后（即收到 `CloudXInitializationCallbacks.OnSdkInitializedEvent` 事件之后）再调用 `CloudXSdk.Arbiter()`。在仲裁服务可用之前，SDK 会在传入的出价中回退选择可比较美元出价最高的平台。
</Info>

## 基础 API

从已加载的广告创建出价候选项，然后传给 `CloudXSdk.Arbiter()`。

```csharp theme={null}
using System.Collections.Generic;
using CloudX;
using UnityEngine;

// cloudXAd 是 CloudX OnAdLoadSuccess 回调中的 CloudXAd 对象。
// 它的 AdValues 映射携带了服务器用于校验出价的受信任载荷键。
// levelPlayNetwork / levelPlayRevenue / levelPlayPrecision 来自 Unity LevelPlay 广告信息。
// pubMaticPrice 和 pubMaticPartner 来自 PubMatic/OpenWrap 出价对象。
var bids = new List<CloudXArbiterBid>
{
    new CloudXArbiterBid.CloudX(cloudXAd),
    new CloudXArbiterBid.LevelPlay(
        NetworkName: levelPlayNetwork,
        Revenue: levelPlayRevenue,
        Precision: levelPlayPrecision),
    new CloudXArbiterBid.PubMatic(
        Price: pubMaticPrice,
        PartnerName: pubMaticPartner),
};

CloudXSdk.Arbiter(bids, result =>
{
    Debug.Log($"选中的平台: {result.Platform}");
});
```

`CloudXArbiterBid.CloudX` 接收 CloudX 加载回调中的 `CloudXAd` 对象。`CloudXArbiterBid.LevelPlay` 接收 Unity LevelPlay 广告信息值。`CloudXArbiterBid.PubMatic` 接收 PubMatic OpenWrap 出价价格和可选的合作伙伴名称。`Extras` 映射在 `LevelPlay` 和 `PubMatic` 出价中均为可选参数，`PartnerName` 在 `PubMatic` 出价中为可选参数。完成回调在主线程上执行，因此你可以直接在其中展示广告或更新 UI。

`result.Platform` 在选中平台时为 `CloudXArbiterPlatform.CloudX`、`LevelPlay` 或 `PubMatic`；当无法选出获胜平台时（例如未传入任何出价），则为 `CloudXArbiterPlatform.None`。结果还提供 `result.Id`（拍卖标识符）、`result.BidId`（获胜出价标识符，当平台为 `None` 时为 null）以及 `result.Extras`（获胜广告网络返回的附加元数据）。

## 分步示例：在 CloudX 与 LevelPlay 之间仲裁

本演练展示了应读取哪个 Unity LevelPlay 回调，以及应将哪些值传入 `CloudXSdk.Arbiter()`。示例使用插屏广告，但相同的字段映射适用于任何广告格式。

<Steps>
  <Step title="加载两个候选项">
    创建 LevelPlay 插屏广告，并为每个平台启动加载。请在加载之前订阅加载回调。

    ```csharp theme={null}
    var levelPlayAd = new LevelPlayInterstitialAd("YOUR_LEVELPLAY_AD_UNIT_ID");
    levelPlayAd.OnAdLoaded += OnLevelPlayLoaded;
    levelPlayAd.OnAdLoadFailed += OnLevelPlayLoadFailed;
    levelPlayAd.LoadAd();

    CloudXAdsCallbacks.Interstitial.OnAdLoadSuccess += OnCloudXLoaded;
    CloudXSdk.LoadInterstitial("YOUR_CLOUDX_AD_UNIT_ID");
    ```
  </Step>

  <Step title="保存每个平台已加载的广告">
    LevelPlay 在其 `OnAdLoaded` 回调中提供 `LevelPlayAdInfo`；CloudX 在 `OnAdLoadSuccess` 中提供 `CloudXAd`。请保存两者——下一步会从中读取仲裁输入。

    ```csharp theme={null}
    private CloudXAd _cloudXAd;
    private LevelPlayAdInfo _levelPlayInfo;

    private void OnCloudXLoaded(CloudXAd ad) => _cloudXAd = ad;
    private void OnLevelPlayLoaded(LevelPlayAdInfo info) => _levelPlayInfo = info;
    ```
  </Step>

  <Step title="将值映射为出价">
    从 `LevelPlayAdInfo` 读取 LevelPlay 字段，并传给 `CloudXArbiterBid.LevelPlay`。CloudX 出价直接接收 `CloudXAd`。

    | `LevelPlayAdInfo` 字段 | 类型        | `CloudXArbiterBid.LevelPlay` 参数 |
    | -------------------- | --------- | ------------------------------- |
    | `adNetwork`          | `string`  | `NetworkName`                   |
    | `revenue`            | `double?` | `Revenue`（用 `?? 0` 合并空值）        |
    | `precision`          | `string`  | `Precision`                     |

    ```csharp theme={null}
    var bids = new List<CloudXArbiterBid>
    {
        new CloudXArbiterBid.CloudX(_cloudXAd),
        new CloudXArbiterBid.LevelPlay(
            NetworkName: _levelPlayInfo.adNetwork,   // LevelPlayAdInfo.adNetwork
            Revenue: _levelPlayInfo.revenue ?? 0,    // LevelPlayAdInfo.revenue 为 double?
            Precision: _levelPlayInfo.precision),    // LevelPlayAdInfo.precision
    };
    ```
  </Step>

  <Step title="运行仲裁器">
    将出价连同完成回调传给 `CloudXSdk.Arbiter()`。回调在 Unity 主线程上执行。

    ```csharp theme={null}
    CloudXSdk.Arbiter(bids, OnArbiterCompleted);
    ```
  </Step>

  <Step title="展示获胜平台">
    根据 `result.Platform` 进行分支，展示获胜平台的广告。`CloudXArbiterPlatform.None` 表示未选出获胜平台——此时不展示广告，继续应用流程。

    ```csharp theme={null}
    private void OnArbiterCompleted(CloudXArbiterResult result)
    {
        switch (result.Platform)
        {
            case CloudXArbiterPlatform.CloudX:
                CloudXSdk.ShowInterstitial("YOUR_CLOUDX_AD_UNIT_ID", "level_complete");
                break;
            case CloudXArbiterPlatform.LevelPlay:
                levelPlayAd.ShowAd("level_complete");
                break;
            default:
                break; // CloudXArbiterPlatform.None——无获胜平台；不展示广告
        }
    }
    ```
  </Step>
</Steps>

<Note>
  在 LevelPlay Unity SDK 8.x 中，`LevelPlayAdInfo` 使用 camelCase 字段（`adNetwork`、`revenue`、`precision`）。`revenue` 为 `double?`，因此在传给非空的 `Revenue` 参数时请使用 `?? 0` 合并空值。
</Note>

下方的 `ArbiterInterstitialController` 将上述步骤封装为一个可复用的组件，会在到达广告位之前提前准备好获胜平台。

## 插屏广告示例

这个插屏广告示例会在两个平台之间仲裁：CloudX 和 Unity LevelPlay。在到达广告位之前先准备好获胜平台：

1. 并行加载 CloudX 和 LevelPlay。
2. 等待两个平台都加载完成或加载失败。
3. 只将已加载的候选项提交给 Trusted Arbiter。
4. 缓存选中的平台。
5. 到达广告位时，立即展示缓存的获胜广告。

如果两个平台都加载失败，开始新的加载周期。如果到达广告位时还没有准备好获胜平台，则继续应用流程，不展示广告。

```csharp ArbiterInterstitialController.cs theme={null}
using System.Collections.Generic;
using CloudX;
using UnityEngine;

/// <summary>
/// 提前准备好 Trusted Arbiter 的获胜平台，以便在到达广告位时立即展示插屏广告。
///
/// 并行加载 CloudX 和 LevelPlay 插屏广告，等待两者都加载完成或加载失败，
/// 将已加载的候选项提交给 CloudXSdk.Arbiter，并将选中的 CloudXArbiterPlatform
/// 缓存到 _nextWinner 中。
/// </summary>
public class ArbiterInterstitialController : MonoBehaviour
{
    private const string CloudXAdUnitId = "interstitial_main";

    // 当 LevelPlay Unity SDK 报告插屏广告已加载时，由宿主设置。
    public string LevelPlayNetwork;
    public double LevelPlayRevenue;
    public string LevelPlayPrecision;

    // 当仲裁器为下一次展示选出平台后调用。
    public System.Action<CloudXArbiterPlatform> OnWinnerPrepared;

    private CloudXAd _cloudXAd;
    private bool _cloudXLoadDone;
    private bool _levelPlayLoaded;
    private bool _levelPlayLoadDone;
    private CloudXArbiterPlatform? _nextWinner;

    private void OnEnable()
    {
        CloudXAdsCallbacks.Interstitial.OnAdLoadSuccess += OnCloudXLoaded;
        CloudXAdsCallbacks.Interstitial.OnAdLoadFailed += OnCloudXLoadFailed;
        CloudXAdsCallbacks.Interstitial.OnAdHidden += OnCloudXHidden;
        CloudXAdsCallbacks.Interstitial.OnAdShowFailed += OnCloudXShowFailed;
    }

    private void OnDisable()
    {
        CloudXAdsCallbacks.Interstitial.OnAdLoadSuccess -= OnCloudXLoaded;
        CloudXAdsCallbacks.Interstitial.OnAdLoadFailed -= OnCloudXLoadFailed;
        CloudXAdsCallbacks.Interstitial.OnAdHidden -= OnCloudXHidden;
        CloudXAdsCallbacks.Interstitial.OnAdShowFailed -= OnCloudXShowFailed;
    }

    /// <summary>为每个当前没有缓存广告的平台启动加载。</summary>
    public void LoadMissingAds()
    {
        if (_cloudXAd == null) CloudXSdk.LoadInterstitial(CloudXAdUnitId);
        if (!_levelPlayLoaded) LoadLevelPlayInterstitial();
    }

    /// <summary>
    /// 展示已准备好的获胜广告，仅当确实发起了展示调用时返回 true。
    ///
    /// 当没有准备好获胜平台或缓存的广告已不可用时返回 false，
    /// 此时会启动一次新的加载周期。
    /// </summary>
    public bool ShowAtPlacement(string placement)
    {
        switch (_nextWinner)
        {
            case CloudXArbiterPlatform.CloudX:
                return ShowCloudX(placement);
            case CloudXArbiterPlatform.LevelPlay:
                return ShowLevelPlay(placement);
            default:
                return false;
        }
    }

    /// <summary>
    /// 在两个平台都完成后运行仲裁器，然后缓存获胜平台。
    ///
    /// 在两个加载都完成之前提前返回。如果两个平台都没有加载成功，则重启加载周期；
    /// 否则将已加载的候选项提交给 CloudXSdk.Arbiter。
    /// </summary>
    private void MaybePrepareWinner()
    {
        if (!_cloudXLoadDone || !_levelPlayLoadDone) return;

        if (_cloudXAd == null && !_levelPlayLoaded)
        {
            _cloudXLoadDone = false;
            _levelPlayLoadDone = false;
            LoadMissingAds();
            return;
        }

        var bids = new List<CloudXArbiterBid>();
        if (_cloudXAd != null)
        {
            bids.Add(new CloudXArbiterBid.CloudX(_cloudXAd));
        }

        if (_levelPlayLoaded)
        {
            bids.Add(new CloudXArbiterBid.LevelPlay(
                NetworkName: LevelPlayNetwork,
                Revenue: LevelPlayRevenue,
                Precision: LevelPlayPrecision));
        }

        CloudXSdk.Arbiter(bids, result =>
        {
            _nextWinner = result.Platform;
            OnWinnerPrepared?.Invoke(result.Platform);
        });
    }

    private bool ShowCloudX(string placement)
    {
        if (CloudXSdk.IsInterstitialReady(CloudXAdUnitId))
        {
            CloudXSdk.ShowInterstitial(CloudXAdUnitId, placement);
            return true;
        }

        ClearCloudXAndLoadMissingAds();
        return false;
    }

    private bool ShowLevelPlay(string placement)
    {
        if (IsLevelPlayInterstitialReady())
        {
            ShowLevelPlayInterstitial(placement);
            return true;
        }

        ClearLevelPlayAndLoadMissingAds();
        return false;
    }

    private void OnCloudXLoaded(CloudXAd ad)
    {
        _cloudXAd = ad;
        _cloudXLoadDone = true;
        MaybePrepareWinner();
    }

    private void OnCloudXLoadFailed(string adUnitId, CloudXError error)
    {
        _cloudXAd = null;
        _cloudXLoadDone = true;
        MaybePrepareWinner();
    }

    private void OnCloudXHidden(CloudXAd ad) => ClearCloudXAndLoadMissingAds();

    private void OnCloudXShowFailed(CloudXAd ad, CloudXError error) => ClearCloudXAndLoadMissingAds();

    private void ClearCloudXAndLoadMissingAds()
    {
        _cloudXAd = null;
        _cloudXLoadDone = false;
        _nextWinner = null;
        LoadMissingAds();
    }

    private void ClearLevelPlayAndLoadMissingAds()
    {
        _levelPlayLoaded = false;
        _levelPlayLoadDone = false;
        _nextWinner = null;
        LoadMissingAds();
    }

    /*
     * 下面的成员封装了 LevelPlay Unity SDK。请用你集成的 LevelPlay SDK 调用替换其方法体，
     * 并将其加载回调接入上面的字段：加载成功时，设置 _levelPlayLoaded = true 及各个
     * LevelPlay* 值，然后设置 _levelPlayLoadDone = true 并调用 MaybePrepareWinner()；
     * 加载失败时，设置 _levelPlayLoaded = false、_levelPlayLoadDone = true，
     * 并调用 MaybePrepareWinner()。广告关闭或展示失败时，调用
     * ClearLevelPlayAndLoadMissingAds() 以丢弃已消耗的广告并开始新的加载周期。
     */
    private void LoadLevelPlayInterstitial() { /* LevelPlay.LoadInterstitial(...) */ }
    private bool IsLevelPlayInterstitialReady() => _levelPlayLoaded;
    private void ShowLevelPlayInterstitial(string placement) { /* LevelPlay.ShowInterstitial(...) */ }
}
```

`ShowAtPlacement()` 仅当确实发起了展示调用时返回 `true`。在 LevelPlay 广告保持加载状态期间，请持续更新缓存的 LevelPlay 候选项值。

对于 PubMatic OpenWrap，使用 `new CloudXArbiterBid.PubMatic(price, partnerName)` 创建第三方出价。如果仲裁服务不可用，SDK 会在传入的受支持出价输入中回退选择可比较美元出价最高的平台。
