Files

166 lines
4.8 KiB
C#
Raw Permalink Normal View History

2026-08-12 14:03:44 +03:00
using System;
using System.Threading.Tasks;
using UnityEngine;
using CoreClient = RudderSdk.Core.RudderClient;
namespace RudderSdk.Unity
{
public enum RudderState
{
NotInitialized,
Initializing,
Ready,
Failed
}
/// <summary>
/// Scene bootstrap MonoBehaviour and public entrypoint of the Rudder Unity SDK.
/// Put this on a startup scene object, assign a RudderConfiguration, then call
/// <see cref="Initialize"/>. Feature calls live on the returned Core client.
2026-08-12 14:03:44 +03:00
/// </summary>
[DefaultExecutionOrder(-1500), DisallowMultipleComponent]
public class Rudder : MonoBehaviour
{
private static Rudder _instance;
private static CoreClient _client;
private static TaskCompletionSource<CoreClient> _readyCompletion = NewCompletion();
[SerializeField] private RudderConfiguration _configuration;
/// Attach point for optional add-on packages so this assembly never has to
/// reference them. Sugar over Ready: subscribers that load late must check
/// State themselves.
public static event Action<CoreClient> Initialized;
public static RudderState State { get; private set; } = RudderState.NotInitialized;
public static Exception LastError { get; private set; }
public static CoreClient Client
{
get
{
if (State != RudderState.Ready || _client == null)
{
throw new InvalidOperationException(
"Rudder is not initialized. Add the Rudder component to a scene with a " +
"RudderConfiguration assigned, then call Rudder.Initialize().");
2026-08-12 14:03:44 +03:00
}
return _client;
}
}
/// Completes with the client after <see cref="Initialize"/>, or faults on
/// the initialization error. Prefer <see cref="Initialize"/> in game code.
2026-08-12 14:03:44 +03:00
public static Task<CoreClient> Ready => _readyCompletion.Task;
public static CoreClient Initialize()
2026-08-12 14:03:44 +03:00
{
if (State == RudderState.Ready && _client != null)
return _client;
2026-08-12 14:03:44 +03:00
if (State == RudderState.Failed)
{
_readyCompletion = NewCompletion();
LastError = null;
State = RudderState.NotInitialized;
}
2026-08-12 14:03:44 +03:00
if (_instance == null)
{
throw new InvalidOperationException(
"Rudder is not in the scene. Add the Rudder component to a startup scene " +
"and assign a RudderConfiguration.");
2026-08-12 14:03:44 +03:00
}
if (_instance._configuration == null)
2026-08-12 14:03:44 +03:00
{
throw new InvalidOperationException(
"Rudder has no configuration. Assign a RudderConfiguration on the Rudder " +
"component (Assets > Create > Rudder > Configuration).");
2026-08-12 14:03:44 +03:00
}
try
{
State = RudderState.Initializing;
LastError = null;
2026-08-12 14:03:44 +03:00
var options = new RudderUnityClientOptions
{
BaseUrl = _instance._configuration.BaseUrl,
ProjectKey = _instance._configuration.ProjectKey,
TimeoutSeconds = _instance._configuration.TimeoutSeconds,
2026-08-12 14:03:44 +03:00
KeyValueStore = new PlayerPrefsUnityKeyValueStore(),
LoggerSink = new UnityDebugLoggerSink()
};
_client = RudderUnityClientFactory.Create(options);
}
catch (Exception exception)
{
_client = null;
State = RudderState.Failed;
LastError = exception;
_readyCompletion.TrySetException(exception);
Debug.LogError(
"[Rudder] Failed to initialize the Rudder SDK. Check the RudderConfiguration asset " +
$"(ProjectKey and BaseUrl must be set). {exception.Message}");
throw;
2026-08-12 14:03:44 +03:00
}
State = RudderState.Ready;
Debug.Log("[Rudder] Rudder SDK initialized.");
_readyCompletion.TrySetResult(_client);
Initialized?.Invoke(_client);
return _client;
2026-08-12 14:03:44 +03:00
}
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
private static void ResetStatics()
2026-08-12 14:03:44 +03:00
{
_instance = null;
_client = null;
State = RudderState.NotInitialized;
LastError = null;
Initialized = null;
_readyCompletion = NewCompletion();
2026-08-12 14:03:44 +03:00
}
private void Awake()
2026-08-12 14:03:44 +03:00
{
if (_instance != null && _instance != this)
{
Destroy(gameObject);
return;
}
_instance = this;
DontDestroyOnLoad(gameObject);
}
private void Update()
{
_client?.Update(Time.deltaTime);
2026-08-12 14:03:44 +03:00
}
private void OnDestroy()
{
if (_instance != this)
return;
_instance = null;
_client = null;
State = RudderState.NotInitialized;
LastError = null;
_readyCompletion.TrySetException(new InvalidOperationException("Rudder was destroyed before initialization completed."));
_readyCompletion = NewCompletion();
}
private static TaskCompletionSource<CoreClient> NewCompletion()
{
return new TaskCompletionSource<CoreClient>(TaskCreationOptions.RunContinuationsAsynchronously);
}
}
}