Skip to content

Commit

Permalink
Object references in custom plugin configurations
Browse files Browse the repository at this point in the history
  • Loading branch information
sven-n committed Mar 20, 2024
1 parent 29229f6 commit a0f52ab
Show file tree
Hide file tree
Showing 22 changed files with 230 additions and 87 deletions.
2 changes: 1 addition & 1 deletion src/ChatServer/ExDbConnector/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ internal static async Task Main(string[] args)
// To make the chat server use our configured encryption key, we need to trick a bit. We add an endpoint with a special client version which is defined in the plugin.
var configuration = new ChatServerSettings();
configuration.Endpoints.Add(new ChatServerEndpoint { ClientVersion = ConfigurableNetworkEncryptionPlugIn.Version, NetworkPort = chatServerListenerPort });
var pluginManager = new PlugInManager(null, loggerFactory, serviceContainer);
var pluginManager = new PlugInManager(null, loggerFactory, serviceContainer, null);
pluginManager.DiscoverAndRegisterPlugInsOf<INetworkEncryptionFactoryPlugIn>();
var chatServer = new ChatServer(addressResolver, loggerFactory, pluginManager);
chatServer.Initialize(configuration);
Expand Down
2 changes: 1 addition & 1 deletion src/Network/Analyzer/MainForm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public MainForm()
this.InitializeComponent();
var serviceContainer = new ServiceContainer();
serviceContainer.AddService(typeof(ILoggerFactory), new NullLoggerFactory());
this._plugInManager = new PlugInManager(null, new NullLoggerFactory(), serviceContainer);
this._plugInManager = new PlugInManager(null, new NullLoggerFactory(), serviceContainer, null);
this._plugInManager.DiscoverAndRegisterPlugIns();
this.clientBindingSource.DataSource = this._proxiedConnections;
this.connectedClientsListBox.DisplayMember = nameof(ICapturedConnection.Name);
Expand Down
34 changes: 34 additions & 0 deletions src/Persistence/ByDataSourceReferenceHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// <copyright file="ByDataSourceReferenceHandler.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>

namespace MUnique.OpenMU.Persistence;

using System.Text.Json.Serialization;
using MUnique.OpenMU.DataModel.Configuration;

/// <summary>
/// A reference handler which uses a <see cref="IDataSource{T}"/> to resolve references.
/// </summary>
public class ByDataSourceReferenceHandler : ReferenceHandler
{
/// <summary>
/// The data source.
/// </summary>
private IDataSource<GameConfiguration> _dataSource;

/// <summary>
/// Initializes a new instance of the <see cref="ByDataSourceReferenceHandler"/> class.
/// </summary>
/// <param name="dataSource">The data source.</param>
public ByDataSourceReferenceHandler(IDataSource<GameConfiguration> dataSource)
{
this._dataSource = dataSource;
}

/// <inheritdoc />
public override ReferenceResolver CreateResolver()
{
return new ByDataSourceReferenceResolver(this._dataSource);
}
}
54 changes: 54 additions & 0 deletions src/Persistence/ByDataSourceReferenceResolver.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// <copyright file="ByDataSourceReferenceResolver.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>

namespace MUnique.OpenMU.Persistence;

using System.Text.Json.Serialization;
using MUnique.OpenMU.DataModel.Configuration;

/// <summary>
/// A reference resolver which uses a <see cref="IDataSource{T}"/> to resolve references.
/// </summary>
public class ByDataSourceReferenceResolver : ReferenceResolver
{
/// <summary>
/// The data source.
/// </summary>
private readonly IDataSource<GameConfiguration> _dataSource;

/// <summary>
/// Initializes a new instance of the <see cref="ByDataSourceReferenceResolver"/> class.
/// </summary>
/// <param name="dataSource">The data source.</param>
public ByDataSourceReferenceResolver(IDataSource<GameConfiguration> dataSource)
{
this._dataSource = dataSource;
}

/// <inheritdoc />
public override void AddReference(string referenceId, object value)
{
//throw new NotImplementedException();
}

/// <inheritdoc />
public override string GetReference(object value, out bool alreadyExists)
{
if (value is IIdentifiable identifiable)
{
alreadyExists = this._dataSource.Get(identifiable.Id) is { };
return identifiable.Id.ToString();
}

alreadyExists = false;
return string.Empty;
}

/// <inheritdoc />
public override object ResolveReference(string referenceId)
{
var id = Guid.Parse(referenceId);
return this._dataSource.Get(id) ?? throw new KeyNotFoundException($"Reference with id '{referenceId}' not found.");
}
}
15 changes: 8 additions & 7 deletions src/Persistence/Initialization/DataInitializationBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,10 @@ public async Task CreateInitialDataAsync(byte numberOfGameServers, bool createTe
// should never happen, but the access to the GameServer type is a trick to load the assembly into the current domain.
}

var referenceHandler = new ByDataSourceReferenceHandler(new GameConfigurationDataSource(this._loggerFactory.CreateLogger<GameConfigurationDataSource>(), this._persistenceContextProvider));
var serviceContainer = new ServiceContainer();
serviceContainer.AddService(typeof(IPersistenceContextProvider), this._persistenceContextProvider);
var plugInManager = new PlugInManager(null, this._loggerFactory, serviceContainer);
var plugInManager = new PlugInManager(null, this._loggerFactory, serviceContainer, referenceHandler);
plugInManager.DiscoverAndRegisterPlugIns();
plugInManager.KnownPlugInTypes.ForEach(plugInType =>
{
Expand All @@ -136,32 +137,32 @@ public async Task CreateInitialDataAsync(byte numberOfGameServers, bool createTe
if (plugInType == typeof(ResetFeaturePlugIn))
{
plugInConfiguration.IsActive = false;
plugInConfiguration.SetConfiguration(new ResetConfiguration());
plugInConfiguration.SetConfiguration(new ResetConfiguration(), referenceHandler);
}

if (plugInType == typeof(ChaosCastleStartPlugIn))
{
plugInConfiguration.SetConfiguration(ChaosCastleStartConfiguration.Default);
plugInConfiguration.SetConfiguration(ChaosCastleStartConfiguration.Default, referenceHandler);
}

if (plugInType == typeof(GoldenInvasionPlugIn))
{
plugInConfiguration.SetConfiguration(PeriodicInvasionConfiguration.DefaultGoldenInvasion);
plugInConfiguration.SetConfiguration(PeriodicInvasionConfiguration.DefaultGoldenInvasion, referenceHandler);
}

if (plugInType == typeof(RedDragonInvasionPlugIn))
{
plugInConfiguration.SetConfiguration(PeriodicInvasionConfiguration.DefaultRedDragonInvasion);
plugInConfiguration.SetConfiguration(PeriodicInvasionConfiguration.DefaultRedDragonInvasion, referenceHandler);
}

if (plugInType == typeof(HappyHourPlugIn))
{
plugInConfiguration.SetConfiguration(HappyHourConfiguration.Default);
plugInConfiguration.SetConfiguration(HappyHourConfiguration.Default, referenceHandler);
}

if (plugInType == typeof(MuHelperFeaturePlugIn))
{
plugInConfiguration.SetConfiguration(new MuHelperConfiguration());
plugInConfiguration.SetConfiguration(new MuHelperConfiguration(), referenceHandler);
}

// We don't move the player anymore by his request. This was usually requested after a player performed a skill.
Expand Down
34 changes: 26 additions & 8 deletions src/PlugIns/PlugInConfigurationExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace MUnique.OpenMU.PlugIns;

using System.Text.Json;
using System.Text.Json.Serialization;

/// <summary>
/// Extension methods for the plugin configuration.
Expand All @@ -16,36 +17,48 @@ public static class PlugInConfigurationExtensions
/// </summary>
/// <typeparam name="T">The custom configuration type.</typeparam>
/// <param name="configuration">The configuration.</param>
/// <param name="referenceHandler">The reference handler.</param>
/// <returns>
/// The custom configuration as <typeparamref name="T" />.
/// </returns>
public static T? GetConfiguration<T>(this PlugInConfiguration configuration)
public static T? GetConfiguration<T>(this PlugInConfiguration configuration, ReferenceHandler? referenceHandler)
where T : class
{
if (string.IsNullOrWhiteSpace(configuration.CustomConfiguration))
{
return default;
}

return JsonSerializer.Deserialize<T>(configuration.CustomConfiguration);
var options = new JsonSerializerOptions
{
ReferenceHandler = referenceHandler,
};

return JsonSerializer.Deserialize<T>(configuration.CustomConfiguration, options);
}

/// <summary>
/// Gets the configuration.
/// </summary>
/// <param name="configuration">The configuration.</param>
/// <param name="configurationType">Type of the configuration.</param>
/// <param name="referenceHandler">The reference handler.</param>
/// <returns>
/// The custom configuration as the given specified type.
/// </returns>
public static object? GetConfiguration(this PlugInConfiguration configuration, Type configurationType)
public static object? GetConfiguration(this PlugInConfiguration configuration, Type configurationType, ReferenceHandler? referenceHandler)
{
if (string.IsNullOrWhiteSpace(configuration.CustomConfiguration))
{
return default;
}

return JsonSerializer.Deserialize(configuration.CustomConfiguration, configurationType);
var options = new JsonSerializerOptions
{
ReferenceHandler = referenceHandler,
};

return JsonSerializer.Deserialize(configuration.CustomConfiguration, configurationType, options);
}

/// <summary>
Expand All @@ -54,18 +67,23 @@ public static class PlugInConfigurationExtensions
/// <typeparam name="T">The custom configuration type.</typeparam>
/// <param name="plugInConfiguration">The plug in configuration.</param>
/// <param name="configuration">The configuration.</param>
public static void SetConfiguration<T>(this PlugInConfiguration plugInConfiguration, T configuration)
/// <param name="referenceHandler">The reference handler.</param>
public static void SetConfiguration<T>(this PlugInConfiguration plugInConfiguration, T configuration, ReferenceHandler? referenceHandler)
{
plugInConfiguration.CustomConfiguration = JsonSerializer.Serialize(configuration, new JsonSerializerOptions { WriteIndented = true });
plugInConfiguration.CustomConfiguration = JsonSerializer.Serialize(configuration, new JsonSerializerOptions { WriteIndented = true, ReferenceHandler = referenceHandler });
}

/// <summary>
/// Sets the configuration.
/// </summary>
/// <param name="plugInConfiguration">The plug in configuration.</param>
/// <param name="configuration">The configuration.</param>
public static void SetConfiguration(this PlugInConfiguration plugInConfiguration, object configuration)
/// <param name="referenceHandler">The reference handler.</param>
public static void SetConfiguration(this PlugInConfiguration plugInConfiguration, object configuration, ReferenceHandler? referenceHandler)
{
plugInConfiguration.CustomConfiguration = JsonSerializer.Serialize(configuration, configuration.GetType(), new JsonSerializerOptions { WriteIndented = true });
plugInConfiguration.CustomConfiguration = JsonSerializer.Serialize(
configuration,
configuration.GetType(),
new JsonSerializerOptions { WriteIndented = true, ReferenceHandler = referenceHandler });
}
}
2 changes: 1 addition & 1 deletion src/PlugIns/PlugInContainerBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ private void OnPlugInConfigurationChanged(object? sender, PlugInConfigurationCha
is { } configSupportInterface)
{
var configType = configSupportInterface.GenericTypeArguments[0];
var typedCustomConfiguration = e.Configuration.GetConfiguration(configType);
var typedCustomConfiguration = e.Configuration.GetConfiguration(configType, this.Manager.CustomConfigReferenceHandler);
configSupportInterface
.GetProperty(nameof(ISupportCustomConfiguration<object>.Configuration))
?.SetMethod
Expand Down
10 changes: 9 additions & 1 deletion src/PlugIns/PlugInManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ namespace MUnique.OpenMU.PlugIns;
using System.ComponentModel.Design;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text.Json.Serialization;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
Expand All @@ -32,7 +33,7 @@ public class PlugInManager
/// <param name="configurations">The configurations.</param>
/// <param name="loggerFactory">The logger factory.</param>
/// <param name="serviceProvider">The service provider.</param>
public PlugInManager(ICollection<PlugInConfiguration>? configurations, ILoggerFactory loggerFactory, IServiceProvider? serviceProvider)
public PlugInManager(ICollection<PlugInConfiguration>? configurations, ILoggerFactory loggerFactory, IServiceProvider? serviceProvider, ReferenceHandler? customConfigReferenceHandler)
{
_ = typeof(Nito.AsyncEx.AsyncReaderWriterLock); // Ensure Nito.AsyncEx.Coordination is loaded so it will be available in proxy generation.

Expand All @@ -41,6 +42,8 @@ public PlugInManager(ICollection<PlugInConfiguration>? configurations, ILoggerFa
this._serviceContainer.AddService(typeof(PlugInManager), this);
this._serviceContainer.AddService(typeof(ILoggerFactory), loggerFactory);

this.CustomConfigReferenceHandler = customConfigReferenceHandler;

if (configurations is not null)
{
this.DiscoverAndRegisterPlugIns();
Expand Down Expand Up @@ -75,6 +78,11 @@ public PlugInManager(ICollection<PlugInConfiguration>? configurations, ILoggerFa
/// </value>
public IEnumerable<Type> KnownPlugInTypes => this._knownPlugIns.Values;

/// <summary>
/// Gets the reference handler for references in custom plugin configurations.
/// </summary>
public ReferenceHandler? CustomConfigReferenceHandler { get; }

/// <summary>
/// Discovers and registers all plug ins of all loaded assemblies.
/// </summary>
Expand Down
Loading

0 comments on commit a0f52ab

Please sign in to comment.