274 lines
No EOL
9.3 KiB
C#
274 lines
No EOL
9.3 KiB
C#
using System;
|
||
using System.Collections;
|
||
using ShiroginSDK.Runtime.Core.SDK;
|
||
using ShiroginSDK.Runtime.Core.Utils;
|
||
using ShiroginSDK.Runtime.Modules.Data.Scripts;
|
||
using ShiroginSDK.Runtime.Modules.Data.Scripts.Enums;
|
||
using ShiroginSDK.Runtime.Modules.Events;
|
||
using ShiroginSDK.Runtime.Services.Base;
|
||
using ShiroginSDK.Runtime.Services.Interfaces;
|
||
using UnityEngine;
|
||
|
||
namespace ShiroginSDK.Runtime.Services.Implementations.Ads
|
||
{
|
||
/// <summary>
|
||
/// Handles Rewarded, Interstitial and Banner ads using AppLovin MAX SDK.
|
||
/// </summary>
|
||
public class AdsService : ServiceBase, IAdsService
|
||
{
|
||
private ShiroginConfig _config;
|
||
private int _retryAttempt;
|
||
private bool _canShowInterstitial;
|
||
private int _addShowInterstitialTime;
|
||
private Action<bool> _shouldReward;
|
||
private Coroutine _interstitialCycle;
|
||
|
||
private IDataService _dataService;
|
||
private IEventService _eventService;
|
||
|
||
// ----------------------------------------------------
|
||
// Initialization
|
||
// ----------------------------------------------------
|
||
protected override void OnInitialize()
|
||
{
|
||
// Resolve dependencies
|
||
_dataService = ServiceLocator.Get<IDataService>();
|
||
_eventService = ServiceLocator.Get<IEventService>();
|
||
|
||
_config = ShiroginConfig.Load();
|
||
|
||
if (string.IsNullOrEmpty(_config.applovinSdkKey))
|
||
{
|
||
Debug.LogError("[ShiroginSDK] Missing AppLovin SDK key in SDKConfig.");
|
||
return;
|
||
}
|
||
|
||
MaxSdk.SetSdkKey(_config.applovinSdkKey);
|
||
MaxSdk.InitializeSdk();
|
||
|
||
InitializeRewardedAds();
|
||
|
||
var storeData = _dataService.Get<StoreData>();
|
||
if (!storeData.HasRemovedAds)
|
||
{
|
||
InitializeInterstitialAds();
|
||
InitializeBannerAds();
|
||
}
|
||
|
||
if (Application.internetReachability != NetworkReachability.NotReachable)
|
||
_interstitialCycle = CoroutineRunner.Start(InterstitialCycle());
|
||
}
|
||
|
||
protected override void OnDispose()
|
||
{
|
||
if (_interstitialCycle != null)
|
||
CoroutineRunner.Stop(_interstitialCycle);
|
||
}
|
||
|
||
// ----------------------------------------------------
|
||
// Rewarded Ads
|
||
// ----------------------------------------------------
|
||
public bool IsRewardedReady => MaxSdk.IsRewardedAdReady(GetRewardedId());
|
||
|
||
public void ShowRewarded(Action<bool> onComplete)
|
||
{
|
||
_shouldReward = onComplete;
|
||
|
||
var economy = _dataService.Get<EconomyData>();
|
||
if (economy.RewardedTicket > 0)
|
||
{
|
||
economy.SpendCurrency(CurrencyType.RewardedTicket, 1);
|
||
_shouldReward?.Invoke(true);
|
||
return;
|
||
}
|
||
|
||
if (Application.internetReachability == NetworkReachability.NotReachable)
|
||
{
|
||
_eventService.Invoke(new ShiroginEvents.Connection.NoConnectionEvent());
|
||
_shouldReward?.Invoke(false);
|
||
return;
|
||
}
|
||
|
||
// 🔹 Tam buraya ekle (önce reklam hazır mı kontrol et)
|
||
if (!MaxSdk.IsRewardedAdReady(GetRewardedId()))
|
||
{
|
||
Debug.LogWarning("[AdsService] ⚠️ Rewarded ad not ready — NoAdsAvailable event triggered.");
|
||
_eventService.Invoke(new ShiroginEvents.Ads.RewardedAdUnavailableEvent()); // Popup
|
||
_shouldReward?.Invoke(false);
|
||
return;
|
||
}
|
||
|
||
// 🔹 Eğer buraya geldiyse reklam hazır, güvenle göster
|
||
MaxSdk.ShowRewardedAd(GetRewardedId());
|
||
}
|
||
|
||
|
||
private void InitializeRewardedAds()
|
||
{
|
||
// Reset retry counter
|
||
_retryAttempt = 0;
|
||
|
||
// --- When ad is successfully loaded
|
||
MaxSdkCallbacks.Rewarded.OnAdLoadedEvent += (adUnitId, info) =>
|
||
{
|
||
_retryAttempt = 0;
|
||
Debug.Log($"[AdsService] ✅ Rewarded loaded: {adUnitId}");
|
||
};
|
||
|
||
// --- When ad fails to load
|
||
MaxSdkCallbacks.Rewarded.OnAdLoadFailedEvent += (adUnitId, error) =>
|
||
{
|
||
_retryAttempt++;
|
||
var retryDelay = Math.Pow(2, Math.Min(6, _retryAttempt));
|
||
Debug.LogWarning($"[AdsService] ⚠️ Rewarded load failed. Retrying in {retryDelay:0.0}s...");
|
||
CoroutineRunner.InvokeDelayed(LoadRewardedAd, (float)retryDelay);
|
||
};
|
||
|
||
// --- When ad is displayed
|
||
MaxSdkCallbacks.Rewarded.OnAdDisplayedEvent += (adUnitId, info) =>
|
||
{
|
||
Debug.Log($"[AdsService] ▶️ Rewarded ad displayed: {adUnitId}");
|
||
};
|
||
|
||
// --- When ad fails to display
|
||
MaxSdkCallbacks.Rewarded.OnAdDisplayFailedEvent += (adUnitId, error, info) =>
|
||
{
|
||
Debug.LogError($"[AdsService] ❌ Rewarded display failed: {error.Message}");
|
||
LoadRewardedAd(); // Retry load immediately
|
||
};
|
||
|
||
// --- When ad is hidden (after being watched or skipped)
|
||
MaxSdkCallbacks.Rewarded.OnAdHiddenEvent += (adUnitId, info) =>
|
||
{
|
||
Debug.Log("[AdsService] 🧹 Rewarded closed. Reloading...");
|
||
LoadRewardedAd(); // Always reload when ad is closed
|
||
};
|
||
|
||
// --- When user earns reward
|
||
MaxSdkCallbacks.Rewarded.OnAdReceivedRewardEvent += OnRewardedCompleted;
|
||
|
||
// Initial load
|
||
LoadRewardedAd();
|
||
}
|
||
|
||
|
||
private void OnRewardedCompleted(string id, MaxSdkBase.Reward reward, MaxSdkBase.AdInfo info)
|
||
{
|
||
_shouldReward?.Invoke(true);
|
||
_eventService.Invoke(new ShiroginEvents.Ads.AdAnalyticsEvent(info.NetworkName, id, info.AdFormat + "_Reward",
|
||
true, info.Revenue));
|
||
|
||
_addShowInterstitialTime += 50;
|
||
|
||
// ✅ Yeni reklam yükle (önemli!)
|
||
LoadRewardedAd();
|
||
}
|
||
|
||
|
||
private void LoadRewardedAd()
|
||
{
|
||
MaxSdk.LoadRewardedAd(GetRewardedId());
|
||
}
|
||
|
||
// ----------------------------------------------------
|
||
// Interstitial Ads
|
||
// ----------------------------------------------------
|
||
private IEnumerator InterstitialCycle()
|
||
{
|
||
while (true)
|
||
{
|
||
yield return new WaitForSeconds(180f + _addShowInterstitialTime);
|
||
_canShowInterstitial = true;
|
||
_addShowInterstitialTime = 0;
|
||
yield return new WaitUntil(() => !_canShowInterstitial);
|
||
}
|
||
}
|
||
|
||
private void InitializeInterstitialAds()
|
||
{
|
||
MaxSdkCallbacks.Interstitial.OnAdLoadedEvent += (_, _) => _retryAttempt = 0;
|
||
MaxSdkCallbacks.Interstitial.OnAdLoadFailedEvent += (_, _) =>
|
||
{
|
||
_retryAttempt++;
|
||
var delay = Math.Pow(2, Math.Min(6, _retryAttempt));
|
||
CoroutineRunner.InvokeDelayed(LoadInterstitial, (float)delay);
|
||
};
|
||
LoadInterstitial();
|
||
}
|
||
|
||
private void LoadInterstitial()
|
||
{
|
||
MaxSdk.LoadInterstitial(GetInterstitialId());
|
||
}
|
||
|
||
public void ShowInterstitial(bool forceShow = false)
|
||
{
|
||
var storeData = _dataService.Get<StoreData>();
|
||
if (storeData.HasRemovedAds)
|
||
return;
|
||
|
||
if ((forceShow || _canShowInterstitial) && MaxSdk.IsInterstitialReady(GetInterstitialId()))
|
||
{
|
||
MaxSdk.ShowInterstitial(GetInterstitialId());
|
||
LoadInterstitial();
|
||
_canShowInterstitial = false;
|
||
}
|
||
}
|
||
|
||
// ----------------------------------------------------
|
||
// Banner Ads
|
||
// ----------------------------------------------------
|
||
private void InitializeBannerAds()
|
||
{
|
||
MaxSdk.CreateBanner(GetBannerId(), MaxSdkBase.BannerPosition.BottomCenter);
|
||
MaxSdkCallbacks.Banner.OnAdLoadedEvent += (_, _) => { };
|
||
MaxSdkCallbacks.Banner.OnAdLoadFailedEvent += (_, _) => { };
|
||
}
|
||
|
||
public void CreateBanner(bool show)
|
||
{
|
||
DestroyBanner();
|
||
MaxSdk.CreateBanner(GetBannerId(), MaxSdkBase.BannerPosition.BottomCenter);
|
||
if (show) MaxSdk.ShowBanner(GetBannerId());
|
||
}
|
||
|
||
public void ShowBanner()
|
||
{
|
||
MaxSdk.ShowBanner(GetBannerId());
|
||
}
|
||
|
||
public void HideBanner()
|
||
{
|
||
MaxSdk.HideBanner(GetBannerId());
|
||
}
|
||
|
||
public void DestroyBanner()
|
||
{
|
||
MaxSdk.DestroyBanner(GetBannerId());
|
||
}
|
||
|
||
// ----------------------------------------------------
|
||
// Helpers
|
||
// ----------------------------------------------------
|
||
#if UNITY_IOS
|
||
private string GetRewardedId() => _config.rewardedAdUnitIdIos;
|
||
private string GetInterstitialId() => _config.interstitialAdUnitIdIos;
|
||
private string GetBannerId() => _config.bannerAdUnitIdIos;
|
||
#else
|
||
private string GetRewardedId()
|
||
{
|
||
return _config.rewardedAdUnitIdDrd;
|
||
}
|
||
|
||
private string GetInterstitialId()
|
||
{
|
||
return _config.interstitialAdUnitIdDrd;
|
||
}
|
||
|
||
private string GetBannerId()
|
||
{
|
||
return _config.bannerAdUnitIdDrd;
|
||
}
|
||
#endif
|
||
}
|
||
} |