| | 1 | | using System; |
| | 2 | | using System.Collections.Generic; |
| | 3 | | using UnityEngine; |
| | 4 | | using UnityEngine.Assertions; |
| | 5 | |
|
| | 6 | | namespace DCL |
| | 7 | | { |
| | 8 | | public delegate IPlugin PluginBuilder(); |
| | 9 | |
|
| | 10 | | /// <summary> |
| | 11 | | /// This class implements a plugin system pattern. |
| | 12 | | /// |
| | 13 | | /// - Many plugins can share the same feature flag |
| | 14 | | /// - Plugins are registered by using a PluginBuilder delegate that must create and return the plugin instance. |
| | 15 | | /// - Any active plugin is an instantiated plugin. A disabled plugin is a never created or disposed plugin. |
| | 16 | | /// - No Initialize() pattern. Plugins abide to RAII idiom. |
| | 17 | | /// - Feature flags can be set automatically by using SetFeatureFlagsData. |
| | 18 | | /// </summary> |
| | 19 | | public class PluginSystem : IDisposable |
| | 20 | | { |
| 1 | 21 | | private static ILogger logger = new Logger(Debug.unityLogger); |
| 7 | 22 | | private PluginGroup allPlugins = new PluginGroup(); |
| 7 | 23 | | private Dictionary<string, PluginGroup> pluginGroupByFlag = new Dictionary<string, PluginGroup>(); |
| | 24 | | private BaseVariable<FeatureFlag> featureFlagsDataSource; |
| | 25 | |
|
| | 26 | | /// <summary> |
| | 27 | | /// Returns true if the plugin defined by the PluginBuilder delegate is currently enabled. |
| | 28 | | /// |
| | 29 | | /// An enabled plugin is an instantiated and currently active plugin. |
| | 30 | | /// </summary> |
| | 31 | | /// <param name="pluginBuilder">The PluginBuilder delegate instance used to instance this plugin.</param> |
| | 32 | | /// <returns>True if the plugin defined by the PluginBuilder delegate is currently enabled</returns> |
| | 33 | | public bool IsEnabled(PluginBuilder pluginBuilder) |
| | 34 | | { |
| 14 | 35 | | if (!allPlugins.plugins.ContainsKey(pluginBuilder)) |
| 0 | 36 | | return false; |
| | 37 | |
|
| 14 | 38 | | return allPlugins.plugins[pluginBuilder].isEnabled; |
| | 39 | | } |
| | 40 | |
|
| | 41 | | /// <summary> |
| | 42 | | /// Registers a plugin builder and binds it to a feature flag. |
| | 43 | | /// </summary> |
| | 44 | | /// <param name="pluginBuilder">The builder used to construct the plugin.</param> |
| | 45 | | /// <param name="featureFlag">The flag to be bounded. When this flag is true, the plugin will be created.</param |
| | 46 | | public void RegisterWithFlag(PluginBuilder pluginBuilder, string featureFlag) |
| | 47 | | { |
| 8 | 48 | | Register(pluginBuilder, false); |
| 8 | 49 | | BindFlag(pluginBuilder, featureFlag); |
| 8 | 50 | | } |
| | 51 | |
|
| | 52 | | /// <summary> |
| | 53 | | /// Registers a new plugin to be built by the PluginBuilder delegate. |
| | 54 | | /// </summary> |
| | 55 | | /// <param name="pluginBuilder">The pluginBuilder instance. This instance will create the plugin when enabled.</ |
| | 56 | | /// <param name="enable">If true, the plugin will be constructed as soon as this method is called.</param> |
| | 57 | | public void Register(PluginBuilder pluginBuilder, bool enable = true) |
| | 58 | | { |
| 9 | 59 | | Assert.IsNotNull(pluginBuilder); |
| | 60 | |
|
| 9 | 61 | | PluginInfo pluginInfo = new PluginInfo() { builder = pluginBuilder }; |
| 9 | 62 | | allPlugins.Add(pluginBuilder, pluginInfo); |
| | 63 | |
|
| 9 | 64 | | if (enable) |
| 1 | 65 | | pluginInfo.Enable(); |
| 9 | 66 | | } |
| | 67 | |
|
| | 68 | | /// <summary> |
| | 69 | | /// Unregisters a registered plugin. If the plugin was active, it will be disposed. |
| | 70 | | /// </summary> |
| | 71 | | /// <param name="plugin">The plugin builder instance used to register the plugin.</param> |
| | 72 | | public void Unregister(PluginBuilder plugin) |
| | 73 | | { |
| 0 | 74 | | if ( !allPlugins.plugins.ContainsKey(plugin)) |
| 0 | 75 | | return; |
| | 76 | |
|
| 0 | 77 | | PluginInfo info = allPlugins.plugins[plugin]; |
| 0 | 78 | | info.Disable(); |
| | 79 | |
|
| 0 | 80 | | string flag = info.flag; |
| | 81 | |
|
| 0 | 82 | | allPlugins.Remove(plugin); |
| 0 | 83 | | pluginGroupByFlag[flag].Remove(plugin); |
| 0 | 84 | | } |
| | 85 | |
|
| | 86 | | /// <summary> |
| | 87 | | /// Bind a feature flag to the given plugin builder. |
| | 88 | | /// When the given feature flag is set to true, this class will construct the plugin, initializing it. |
| | 89 | | /// </summary> |
| | 90 | | /// <param name="plugin">The given plugin builder.</param> |
| | 91 | | /// <param name="featureFlag">The given feature flag. If this feature flag is set to true the plugin will be cre |
| | 92 | | public void BindFlag(PluginBuilder plugin, string featureFlag) |
| | 93 | | { |
| 8 | 94 | | Assert.IsNotNull(plugin); |
| | 95 | |
|
| 8 | 96 | | if ( !pluginGroupByFlag.ContainsKey(featureFlag) ) |
| 4 | 97 | | pluginGroupByFlag.Add(featureFlag, new PluginGroup()); |
| | 98 | |
|
| 8 | 99 | | pluginGroupByFlag[featureFlag].Add(plugin, allPlugins.plugins[plugin]); |
| 8 | 100 | | } |
| | 101 | |
|
| | 102 | | /// <summary> |
| | 103 | | /// Sets a feature flag. Disabling or enabling any plugin that was bounded to it. |
| | 104 | | /// </summary> |
| | 105 | | /// <param name="featureFlag">The given feature flag to set.</param> |
| | 106 | | /// <param name="enabled">The feature flag is enabled?</param> |
| | 107 | | public void SetFlag(string featureFlag, bool enabled) |
| | 108 | | { |
| 5 | 109 | | if (enabled) |
| 5 | 110 | | EnableFlag(featureFlag); |
| | 111 | | else |
| 0 | 112 | | DisableFlag(featureFlag); |
| 0 | 113 | | } |
| | 114 | |
|
| | 115 | | /// <summary> |
| | 116 | | /// Enables a given feature flag. This enables any plugin that was bounded to it. |
| | 117 | | /// Enabling a plugin means that the plugin instance will be created. |
| | 118 | | /// </summary> |
| | 119 | | /// <param name="featureFlag">The feature flag to enable</param> |
| | 120 | | public void EnableFlag(string featureFlag) |
| | 121 | | { |
| 5 | 122 | | if ( !pluginGroupByFlag.ContainsKey(featureFlag) ) |
| 1 | 123 | | return; |
| | 124 | |
|
| 4 | 125 | | PluginGroup pluginGroup = pluginGroupByFlag[featureFlag]; |
| | 126 | |
|
| 24 | 127 | | foreach ( var feature in pluginGroup.plugins ) |
| | 128 | | { |
| 8 | 129 | | PluginInfo info = feature.Value; |
| 8 | 130 | | info.Enable(); |
| | 131 | | } |
| 4 | 132 | | } |
| | 133 | |
|
| | 134 | | /// <summary> |
| | 135 | | /// Disables a given feature flag. This disables any plugin that was bounded to it. |
| | 136 | | /// Disabling a plugin means that the plugin instance will be disposed. |
| | 137 | | /// </summary> |
| | 138 | | /// <param name="featureFlag">The feature flag to enable</param> |
| | 139 | | public void DisableFlag(string featureFlag) |
| | 140 | | { |
| 0 | 141 | | if ( !pluginGroupByFlag.ContainsKey(featureFlag) ) |
| 0 | 142 | | return; |
| | 143 | |
|
| 0 | 144 | | PluginGroup pluginGroup = pluginGroupByFlag[featureFlag]; |
| | 145 | |
|
| 0 | 146 | | foreach ( var feature in pluginGroup.plugins ) |
| | 147 | | { |
| 0 | 148 | | PluginInfo info = feature.Value; |
| 0 | 149 | | info.Disable(); |
| | 150 | | } |
| 0 | 151 | | } |
| | 152 | |
|
| | 153 | | /// <summary> |
| | 154 | | /// Sets the FeatureFlag instance used to listen for feature flag changes. |
| | 155 | | /// |
| | 156 | | /// After setting the feature flags data of the given instance, the PluginSystem |
| | 157 | | /// will react to all the feature flag changes. |
| | 158 | | /// </summary> |
| | 159 | | /// <param name="featureFlagsBaseVariable">The data object to listen to.</param> |
| | 160 | | public void SetFeatureFlagsData(BaseVariable<FeatureFlag> featureFlagsBaseVariable) |
| | 161 | | { |
| 4 | 162 | | if ( featureFlagsDataSource != null ) |
| 0 | 163 | | featureFlagsDataSource.OnChange -= OnFeatureFlagsChange; |
| | 164 | |
|
| 4 | 165 | | featureFlagsDataSource = featureFlagsBaseVariable; |
| 4 | 166 | | featureFlagsDataSource.OnChange += OnFeatureFlagsChange; |
| 4 | 167 | | OnFeatureFlagsChange(featureFlagsBaseVariable.Get(), featureFlagsBaseVariable.Get()); |
| 4 | 168 | | } |
| | 169 | |
|
| | 170 | | private void OnFeatureFlagsChange(FeatureFlag current, FeatureFlag previous) |
| | 171 | | { |
| 6 | 172 | | Assert.IsNotNull(current, "Current feature flags object should never be null!"); |
| | 173 | |
|
| 22 | 174 | | foreach ( var flag in current.flags ) |
| | 175 | | { |
| 5 | 176 | | SetFlag(flag.Key, flag.Value); |
| | 177 | | } |
| 6 | 178 | | } |
| | 179 | |
|
| | 180 | | public void Dispose() |
| | 181 | | { |
| 32 | 182 | | foreach ( var kvp in allPlugins.plugins ) |
| | 183 | | { |
| 9 | 184 | | PluginInfo info = kvp.Value; |
| 9 | 185 | | info.Disable(); |
| | 186 | | } |
| | 187 | |
|
| 7 | 188 | | if ( featureFlagsDataSource != null ) |
| 4 | 189 | | featureFlagsDataSource.OnChange -= OnFeatureFlagsChange; |
| 7 | 190 | | } |
| | 191 | | } |
| | 192 | | } |