// Copyright (c) 2015 - 2023 Doozy Entertainment. All Rights Reserved. // This code can only be used under the standard Unity Asset Store End User License Agreement // A Copy of the EULA APPENDIX 1 is available at http://unity3d.com/company/legal/as_terms using System; using System.Collections.Generic; using System.Linq; using Doozy.Runtime.Common.Attributes; using Doozy.Runtime.Common.Extensions; using UnityEngine; using UnityEngine.Events; using Object = UnityEngine.Object; // ReSharper disable MemberCanBePrivate.Global // ReSharper disable UnassignedField.Global namespace Doozy.Runtime.Signals { public static partial class SignalsService { [ExecuteOnReload] private static void OnReload() { Providers.Clear(); foreach (SignalStream stream in Streams.Values) stream.Close(); Streams.Clear(); } #region Providers /// Providers Database public static readonly List Providers = new List(); /// Whenever a ISignalProvider is added, this action gets invoked public static UnityAction OnProviderAdded; /// Whenever a ISignalProvider is removed, this action gets invoked public static UnityAction OnProviderRemoved; /// Add a new SignalProvider to Providers internal static ISignalProvider AddProvider(ISignalProvider provider) { RemoveNullProviders(); if (provider == null) return null; if (Providers.Contains(provider)) return provider; Providers.Add(provider); OnProviderAdded?.Invoke(provider); return provider; } /// Remove the given provider from Providers internal static void RemoveProvider(ISignalProvider provider) { RemoveNullProviders(); if (provider == null) return; if (!Providers.Contains(provider)) return; Providers.Remove(provider); OnProviderRemoved?.Invoke(provider); } internal static void RemoveNullProviders() { for (int i = Providers.Count - 1; i >= 0; i--) if (Providers[i] == null) Providers.RemoveAt(i); } /// /// Get the provider with the given provider id /// If a provider is not found, one will get created /// /// Provider id /// If the provider type is Local, then it needs a signalSource to attach to public static ISignalProvider GetProvider(ProviderId providerId, GameObject signalSource) { Type providerType = SignalProvider.GetProviderType(providerId); signalSource = providerId.Type == ProviderType.Global ? Signals.instance.gameObject : signalSource; if (signalSource == null) throw new NullReferenceException($"{nameof(signalSource)} cannot be null when the {nameof(ProviderType)} is {providerId.Type}"); Component component = signalSource.GetComponent(providerType) ?? signalSource.AddComponent(providerType); // ReSharper disable once SuspiciousTypeConversion.Global return (ISignalProvider)component; } /// /// Get the provider with the given properties /// If a provider is not found, one will get created /// /// Type of provider (Local or Global) /// Provider category name /// Provider name (from the given category) /// If the provider type is Local, then it needs a signalSource to attach to public static ISignalProvider GetProvider(ProviderType providerType, string providerCategory, string providerName, GameObject signalSource) => GetProvider(new ProviderId(providerType, providerCategory, providerName), signalSource); /// /// Get the provider associated with the given stream /// If a provider is not found, this method returns null /// /// Signal stream public static ISignalProvider GetProvider(SignalStream stream) => Providers.FirstOrDefault(provider => provider.stream == stream); #endregion #region Streams public const string k_TypeCategory = "Type"; /// Streams Database public static readonly Dictionary Streams = new Dictionary(); /// Whenever a Stream is added, this action gets invoked public static UnityAction OnStreamAdded; /// Whenever a Stream is removed, this action gets invoked public static UnityAction OnStreamRemoved; /// Add a new SignalStream to Streams internal static SignalStream AddStream(SignalStream stream) { if (stream == null) return null; if (Streams.ContainsValue(stream)) return stream; Streams.Add(stream.key, stream); OnStreamAdded?.Invoke(stream); return stream; } /// Remove the given stream from Streams internal static void RemoveStream(SignalStream stream) { if (stream == null) return; if (!Streams.ContainsKey(stream.key)) return; Streams.Remove(stream.key); OnStreamRemoved?.Invoke(stream); } /// /// Get a new unique stream key (Guid) /// This method makes sure the newly generated Guid is not used by any other registered stream /// internal static Guid GetNewStreamKey() { var guid = Guid.NewGuid(); bool generateNewId = Streams.ContainsKey(guid); while (generateNewId) { guid = Guid.NewGuid(); generateNewId = Streams.ContainsKey(guid); } return guid; } #region GetStream /// Create a new stream and get a reference to it public static SignalStream GetStream() => AddStream(new SignalStream(GetNewStreamKey())); public static SignalStream GetTypeStream(string typeName) => GetStream(k_TypeCategory, typeName); /// /// Get the stream with the given stream category and name. /// If not found, this method creates a new stream with the given stream category and name, and returns a reference to it /// /// Stream category /// Stream name public static SignalStream GetStream(string streamCategory, string streamName) { streamCategory = streamCategory.Trim(); if (streamCategory.IsNullOrEmpty()) streamCategory = SignalStream.k_DefaultCategory; streamName = streamName.Trim(); if (streamName.IsNullOrEmpty()) return GetStream(); foreach (SignalStream s in Streams.Values .Where(s => s.category.Equals(streamCategory)) .Where(s => s.name.Equals(streamName))) return s; SignalStream stream = new SignalStream(GetNewStreamKey()).SetCategory(streamCategory).SetName(streamName); return AddStream(stream); } #endregion #region FindStream /// /// Find the stream with the given stream key. /// If not found, this method returns null /// /// Stream key to search for public static SignalStream FindStream(Guid streamKey) => Streams.ContainsKey(streamKey) ? Streams[streamKey] : null; /// /// Find the stream with the given stream category and name. /// If not found, this method returns null /// Stream category /// Stream name public static SignalStream FindStream(string streamCategory, string streamName) { streamCategory = streamCategory.Trim(); if (streamCategory.IsNullOrEmpty()) streamCategory = SignalStream.k_DefaultCategory; streamName = streamName.Trim(); if (streamName.IsNullOrEmpty()) return null; return Streams.Values .Where(s => s.category.Equals(streamCategory)) .FirstOrDefault(s => s.name.Equals(streamName)); } #endregion #region CloseStream /// Close the given stream /// Target signal stream public static void CloseStream(SignalStream stream) { stream?.Close(); RemoveStream(stream); RemoveProvider(GetProvider(stream)); } /// Close the stream associated to the given provider /// Target signal provider public static void CloseStream(ISignalProvider provider) { if (provider == null) return; if (provider.isConnected) { CloseStream(provider.stream); return; } RemoveProvider(provider); } #endregion #endregion #region SendSignal /// Whenever a Signal is sent, this action gets invoked public static UnityAction OnSignal; #region Send Signal - using a stream Category and Name /// Send a Signal on the stream with the given stream category and name /// Target stream category /// Target stream name /// Text message used to pass info about this Signal public static bool SendSignal(string streamCategory, string streamName, string message = "") => SendSignal(GetStream(streamCategory, streamName), message); /// Send a Signal on the stream with the given stream category and name, with a reference to the GameObject from where it is sent /// Target stream category /// Target stream name /// Reference to the GameObject from where this Signal is sent /// Text message used to pass info about this Signal public static bool SendSignal(string streamCategory, string streamName, GameObject signalSource, string message = "") => SendSignal(GetStream(streamCategory, streamName), signalSource, message); /// Send a Signal on the stream with the given stream category and name, with a reference to the SignalProvider that sent it /// Target stream category /// Target stream name /// Reference to the SignalProvider that sends this Signal /// Text message used to pass info about this Signal public static bool SendSignal(string streamCategory, string streamName, SignalProvider signalProvider, string message = "") => SendSignal(GetStream(streamCategory, streamName), signalProvider, message); /// Send a Signal on the stream with the given stream category and name, with a reference to the Object that sent it /// Target stream category /// Target stream name /// Reference to the Object that sends this Signal /// Text message used to pass info about this Signal public static bool SendSignal(string streamCategory, string streamName, Object signalSender, string message = "") => SendSignal(GetStream(streamCategory, streamName), signalSender, message); #endregion #region Send MetaSignal - using a stream Category and Name /// Send a MetaSignal on the stream with the given stream category and name /// Target stream category /// Target stream name /// Signal value /// Text message used to pass info about this Signal public static bool SendSignal(string streamCategory, string streamName, T signalValue, string message = "") => SendSignal(GetStream(streamCategory, streamName), signalValue, message); /// Send a MetaSignal on the stream with the given stream category and name, with a reference to the GameObject from where it is sent /// Target stream category /// Target stream name /// Signal value /// Reference to the GameObject from where this Signal is sent /// Text message used to pass info about this Signal /// Signal value type public static bool SendSignal(string streamCategory, string streamName, T signalValue, GameObject signalSource, string message = "") => SendSignal(GetStream(streamCategory, streamName), signalValue, signalSource, message); /// Send a MetaSignal on the stream with the given stream category and name, with a reference to the SignalProvider that sent it /// Target stream category /// Target stream name /// Signal value /// Reference to the SignalProvider that sends this Signal /// Text message used to pass info about this Signal /// Signal value type public static bool SendSignal(string streamCategory, string streamName, T signalValue, SignalProvider signalProvider, string message = "") => SendSignal(GetStream(streamCategory, streamName), signalValue, signalProvider, message); /// Send a MetaSignal on the stream with the given stream category and name, with a reference to the Object that sent it /// Target stream category /// Target stream name /// Signal value /// Reference to the Object that sends this Signal /// Text message used to pass info about this Signal /// Signal value type public static bool SendSignal(string streamCategory, string streamName, T signalValue, Object signalSender, string message = "") => SendSignal(GetStream(streamCategory, streamName), signalValue, signalSender, message); #endregion #region Send Signal - using a Guid streamKey /// Send a Signal on the stream with the given stream key (Guid) /// Target stream key (Guid) /// Text message used to pass info about this Signal public static bool SendSignal(Guid streamKey, string message = "") => SendSignal(FindStream(streamKey), message); /// Send a Signal on the stream with the given stream key (Guid), with a reference to the GameObject from where it was sent /// Target stream key (Guid) /// Reference to the GameObject from where this Signal is sent from /// Text message used to pass info about this Signal public static bool SendSignal(Guid streamKey, GameObject signalSource, string message = "") => SendSignal(FindStream(streamKey), signalSource, message); /// Send a Signal on the stream with the given stream key (Guid), with a reference to the SignalProvider that sent it /// Target stream key (Guid) /// Reference to the SignalProvider that sends this Signal /// Text message used to pass info about this Signal public static bool SendSignal(Guid streamKey, SignalProvider signalProvider, string message = "") => SendSignal(FindStream(streamKey), signalProvider, message); /// Send a Signal on the stream with the given stream key (Guid), with a reference to the Object that sent it /// Target stream key (Guid) /// Reference to the Object that sends this Signal /// Text message used to pass info about this Signal public static bool SendSignal(Guid streamKey, Object signalSender, string message = "") => SendSignal(FindStream(streamKey), signalSender, message); #endregion #region Send MetaSignal - using a Guid streamKey /// Send a MetaSignal on the stream with the given stream key (Guid) /// Target stream key (Guid) /// Signal value /// Text message used to pass info about this Signal /// Signal value type public static bool SendSignal(Guid streamKey, T signalValue, string message = "") => SendSignal(FindStream(streamKey), signalValue, message); /// Send a MetaSignal on the stream with the given stream key (Guid), with a reference to the GameObject from where it was sent /// Target stream key (Guid) /// Signal value /// Reference to the GameObject from where this Signal is sent from /// Text message used to pass info about this Signal /// Signal value type public static bool SendSignal(Guid streamKey, T signalValue, GameObject signalSource, string message = "") => SendSignal(FindStream(streamKey), signalValue, signalSource, message); /// Send a MetaSignal on the stream with the given stream key (Guid), with a reference to the SignalProvider that sent it /// Target stream key (Guid) /// Signal value /// Reference to the SignalProvider that sends this Signal /// Text message used to pass info about this Signal /// Signal value type public static bool SendSignal(Guid streamKey, T signalValue, SignalProvider signalProvider, string message = "") => SendSignal(FindStream(streamKey), signalValue, signalProvider, message); /// Send a MetaSignal on the stream with the given stream key (Guid), with a reference to the Object that sent it /// Target stream key (Guid) /// Signal value /// Reference to the Object that sends this Signal /// Text message used to pass info about this Signal /// Signal value type public static bool SendSignal(Guid streamKey, T signalValue, Object signalSender, string message = "") => SendSignal(FindStream(streamKey), signalValue, signalSender, message); #endregion #region Send Signal - using a SignalStream reference /// Send a Signal on the given stream /// Target signal stream /// Text message used to pass info about this Signal public static bool SendSignal(SignalStream stream, string message = "") => stream != null && stream.SendSignal(message); /// Send a Signal on the given stream, with a reference to the GameObject from where it was sent /// Target signal stream /// Reference to the GameObject from where this Signal is sent from /// Text message used to pass info about this Signal public static bool SendSignal(SignalStream stream, GameObject signalSource, string message = "") => stream != null && stream.SendSignal(signalSource, message); /// Send a Signal on the given stream, with a reference to the SignalProvider that sent it /// Target signal stream /// Reference to the SignalProvider that sends this Signal /// Text message used to pass info about this Signal public static bool SendSignal(SignalStream stream, SignalProvider signalProvider, string message = "") => stream != null && stream.SendSignal(signalProvider, message); /// Send a Signal on the given stream, with a reference to the Object that sent it /// Target signal stream /// Reference to the Object that sends this Signal /// Text message used to pass info about this Signal public static bool SendSignal(SignalStream stream, Object signalSender, string message = "") => stream != null && stream.SendSignal(signalSender, message); #endregion #region Send MetaSignal - using a SignalStream reference /// Send a MetaSignal on the given stream /// Target signal stream /// Signal value /// Text message used to pass info about this Signal /// Signal value type public static bool SendSignal(SignalStream stream, T signalValue, string message = "") => stream != null && stream.SendSignal(signalValue, message); /// Send a MetaSignal on the given stream, with a reference to the GameObject from where it was sent /// Target signal stream /// Signal value /// Reference to the GameObject from where this Signal is sent from /// Text message used to pass info about this Signal /// Signal value type public static bool SendSignal(SignalStream stream, T signalValue, GameObject signalSource, string message = "") => stream != null && stream.SendSignal(signalValue, signalSource, message); /// Send a MetaSignal on the given stream, with a reference to the SignalProvider that sent it /// Target signal stream /// Signal value /// Reference to the SignalProvider that sends this Signal /// Text message used to pass info about this Signal /// Signal value type public static bool SendSignal(SignalStream stream, T signalValue, SignalProvider signalProvider, string message = "") => stream != null && stream.SendSignal(signalValue, signalProvider, message); /// Send a MetaSignal on the given stream, with a reference to the Object that sent it /// Target signal stream /// Signal value /// Reference to the Object that sends this Signal /// Text message used to pass info about this Signal /// Signal value type public static bool SendSignal(SignalStream stream, T signalValue, Object signalSender, string message = "") => stream != null && stream.SendSignal(signalValue, signalSender, message); #endregion #endregion } }