// // Licensed under the MIT License. See LICENSE file in the project root for full license information. // namespace MUnique.OpenMU.GameServer.RemoteView; using System.ComponentModel.Design; using System.Reflection; using Microsoft.Extensions.DependencyInjection; using MUnique.OpenMU.GameLogic; using MUnique.OpenMU.GameLogic.Views; using MUnique.OpenMU.Network.PlugIns; using MUnique.OpenMU.PlugIns; /// /// A plugin container which selects plugin based on the provided version/season metadata. /// /// /// /// Simplified example: View plugin container is meant for season 6. /// There are some IChatMessageViewPlugIns available for the seasons 1, 2, 6 and 7. /// Which one to choose? /// In this case, it's easy, we just select the one which is equal. /// Now, if we remove the plugin for season 6, so that 1, 2 and 7 are available? /// In this case, we assume that the last available season before the target season is the correct one, season 2. /// internal sealed class ViewPlugInContainer : CustomPlugInContainerBase, IDisposable { private readonly RemotePlayer _player; private readonly ServiceContainer _serviceContainer; /// /// Initializes a new instance of the class. /// /// The player. /// The client information. /// The manager. public ViewPlugInContainer(RemotePlayer player, ClientVersion clientVersion, PlugInManager manager) : base(manager) { this._player = player; this.Client = clientVersion; this._player.ClientVersionChanged += this.OnClientVersionChanged; this._serviceContainer = new ServiceContainer(); this._serviceContainer.AddService(typeof(RemotePlayer), this._player); this.Initialize(); } /// /// Gets the client. /// /// /// The client. /// public ClientVersion Client { get; private set; } /// public void Dispose() { this._serviceContainer.Dispose(); this.KnownPlugIns.OfType().ForEach(d => d.Dispose()); } /// /// We look if the activated plugin is rated at a higher client version than the current one. protected override bool IsNewPlugInReplacingOld(IViewPlugIn currentEffectivePlugIn, IViewPlugIn activatedPlugIn) { var currentPlugInClient = currentEffectivePlugIn.GetType().GetCustomAttribute(inherit: false)?.Client ?? default; var activatedPluginClient = activatedPlugIn.GetType().GetCustomAttribute(inherit: false)?.Client ?? default; return currentPlugInClient.CompareTo(activatedPluginClient) < 0; } /// /// We sort by version and choose the highest one. protected override IViewPlugIn? DetermineEffectivePlugIn(Type interfaceType) { return this.ActivePlugIns.OrderByDescending(p => p.GetType().GetCustomAttribute(typeof(MinimumClientAttribute), inherit: false)).FirstOrDefault(interfaceType.IsInstanceOfType); } /// /// We just take the plugins which have a equal or lower version than our target . protected override void CreatePlugInIfSuitable(Type plugInType) { if (this.Client.IsPlugInSuitable(plugInType)) { var plugIn = (IViewPlugIn)ActivatorUtilities.CreateInstance(this._serviceContainer, plugInType)!; this.AddPlugIn(plugIn, true); } } private void OnClientVersionChanged(object? sender, EventArgs e) { this.Client = this._player.ClientVersion; foreach (var activePlugIn in this.ActivePlugIns) { if (!this.Client.IsPlugInSuitable(activePlugIn.GetType())) { this.DeactivatePlugIn(activePlugIn); } } this.Initialize(); } }