Updated performance in plugin loading

This commit is contained in:
2024-08-18 14:32:13 +03:00
parent 95e8d95c92
commit c080074292
16 changed files with 463 additions and 244 deletions

View File

@@ -1,10 +1,9 @@
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using DiscordBotCore.Interfaces;
using DiscordBotCore.Others;
using DiscordBotCore.Others.Exceptions;
namespace DiscordBotCore.Loaders;
@@ -13,7 +12,7 @@ internal class Loader
internal delegate void FileLoadedHandler(FileLoaderResult result);
internal delegate void PluginLoadedHandler(PluginLoadResultData result);
internal delegate void PluginLoadedHandler(PluginLoaderResult result);
internal event FileLoadedHandler? OnFileLoadedException;
internal event PluginLoadedHandler? OnPluginLoaded;
@@ -21,8 +20,8 @@ internal class Loader
internal async Task Load()
{
var installedPlugins = await Application.CurrentApplication.PluginManager.GetInstalledPlugins();
var files = installedPlugins.Where(plugin=>plugin.IsEnabled).Select(plugin => plugin.FilePath).ToArray();
var files = installedPlugins.Where(plugin => plugin.IsEnabled).Select(plugin => plugin.FilePath).ToArray();
foreach (var file in files)
{
try
@@ -41,7 +40,7 @@ internal class Loader
await LoadEverythingOfType<ICommandAction>();
}
private async Task LoadEverythingOfType<T>()
private Task LoadEverythingOfType<T>()
{
var types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
@@ -58,25 +57,24 @@ internal class Loader
throw new Exception($"Failed to create instance of plugin with type {type.FullName} [{type.Assembly}]");
}
var pluginType = plugin switch
PluginLoaderResult result = plugin switch
{
IDbEvent => PluginType.EVENT,
IDbCommand => PluginType.COMMAND,
IDbSlashCommand => PluginType.SLASH_COMMAND,
ICommandAction => PluginType.ACTION,
_ => PluginType.UNKNOWN
IDbEvent @event => PluginLoaderResult.FromIDbEvent(@event),
IDbCommand command => PluginLoaderResult.FromIDbCommand(command),
IDbSlashCommand command => PluginLoaderResult.FromIDbSlashCommand(command),
ICommandAction action => PluginLoaderResult.FromICommandAction(action),
_ => PluginLoaderResult.FromException(new PluginNotFoundException($"Unknown plugin type {plugin.GetType().FullName}"))
};
if (pluginType == PluginType.UNKNOWN)
throw new Exception($"Unknown plugin type for plugin with type {type.FullName} [{type.Assembly}]");
OnPluginLoaded?.Invoke(new PluginLoadResultData(type.FullName, pluginType, true, plugin: plugin));
OnPluginLoaded?.Invoke(result);
}
catch (Exception ex)
{
OnPluginLoaded?.Invoke(new PluginLoadResultData(type.FullName, PluginType.UNKNOWN, false, ex.Message));
OnPluginLoaded?.Invoke(PluginLoaderResult.FromException(ex));
}
}
return Task.CompletedTask;
}
}

View File

@@ -1,23 +0,0 @@
using DiscordBotCore.Others;
namespace DiscordBotCore.Loaders;
public class PluginLoadResultData
{
public string PluginName { get; init; }
public PluginType PluginType { get; init; }
public string? ErrorMessage { get; init; }
public bool IsSuccess { get; init; }
public object Plugin { get; init; }
public PluginLoadResultData(string pluginName, PluginType pluginType, bool isSuccess, string? errorMessage = null,
object? plugin = null)
{
PluginName = pluginName;
PluginType = pluginType;
IsSuccess = isSuccess;
ErrorMessage = errorMessage;
Plugin = plugin is null ? new() : plugin;
}
}

View File

@@ -1,25 +1,21 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;
using Discord.WebSocket;
using DiscordBotCore.Interfaces;
using DiscordBotCore.Others;
using DiscordBotCore.Others.Exceptions;
namespace DiscordBotCore.Loaders;
public class PluginLoader
public sealed class PluginLoader
{
internal readonly DiscordSocketClient _Client;
public delegate void CommandLoaded(PluginLoadResultData resultData);
public delegate void EventLoaded(PluginLoadResultData resultData);
public delegate void SlashCommandLoaded(PluginLoadResultData resultData);
public delegate void ActionLoaded(PluginLoadResultData resultData);
private readonly DiscordSocketClient _Client;
public delegate void CommandLoaded(IDbCommand eCommand);
public delegate void EventLoaded(IDbEvent eEvent);
public delegate void SlashCommandLoaded(IDbSlashCommand eSlashCommand);
public delegate void ActionLoaded(ICommandAction eAction);
public CommandLoaded? OnCommandLoaded;
public EventLoaded? OnEventLoaded;
@@ -38,13 +34,6 @@ public class PluginLoader
public async Task LoadPlugins()
{
if (_Client == null)
{
Application.Logger.Log("Discord client is null", this, LogType.Error);
return;
}
Commands.Clear();
Events.Clear();
SlashCommands.Clear();
@@ -65,48 +54,62 @@ public class PluginLoader
Application.Logger.Log(result.ErrorMessage, this, LogType.Error);
}
private async void OnPluginLoaded(PluginLoadResultData result)
private async void InitializeCommand(ICommandAction action)
{
switch (result.PluginType)
if (action.RunType == InternalActionRunType.OnStartup || action.RunType == InternalActionRunType.OnStartupAndCall)
await Application.CurrentApplication.InternalActionManager.StartAction(action, null);
if(action.RunType == InternalActionRunType.OnCall || action.RunType == InternalActionRunType.OnStartupAndCall)
Actions.Add(action);
OnActionLoaded?.Invoke(action);
}
private void InitializeDbCommand(IDbCommand command)
{
Commands.Add(command);
OnCommandLoaded?.Invoke(command);
}
private void InitializeEvent(IDbEvent eEvent)
{
if (!eEvent.TryStartEvent())
{
case PluginType.ACTION:
ICommandAction action = (ICommandAction)result.Plugin;
if (action.RunType == InternalActionRunType.OnStartup || action.RunType == InternalActionRunType.OnStartupAndCall)
await Application.CurrentApplication.InternalActionManager.StartAction(action, null);
if(action.RunType == InternalActionRunType.OnCall || action.RunType == InternalActionRunType.OnStartupAndCall)
Actions.Add(action);
OnActionLoaded?.Invoke(result);
break;
case PluginType.COMMAND:
Commands.Add((IDbCommand)result.Plugin);
OnCommandLoaded?.Invoke(result);
break;
case PluginType.EVENT:
if (this.TryStartEvent((IDbEvent)result.Plugin))
{
Events.Add((IDbEvent)result.Plugin);
OnEventLoaded?.Invoke(result);
}
break;
case PluginType.SLASH_COMMAND:
if (await this.TryStartSlashCommand((IDbSlashCommand)result.Plugin))
{
if(((IDbSlashCommand)result.Plugin).HasInteraction)
_Client.InteractionCreated += ((IDbSlashCommand)result.Plugin).ExecuteInteraction;
SlashCommands.Add((IDbSlashCommand)result.Plugin);
OnSlashCommandLoaded?.Invoke(result);
}
else
Application.Logger.Log($"Failed to start slash command {result.PluginName}", this, LogType.Error);
break;
case PluginType.UNKNOWN:
default:
Application.Logger.Log("Unknown plugin type", this, LogType.Error);
break;
return;
}
Events.Add(eEvent);
OnEventLoaded?.Invoke(eEvent);
}
private async void InitializeSlashCommand(IDbSlashCommand slashCommand)
{
Result result = await slashCommand.TryStartSlashCommand();
result.Match(
() =>
{
if (slashCommand.HasInteraction)
_Client.InteractionCreated += slashCommand.ExecuteInteraction;
SlashCommands.Add(slashCommand);
OnSlashCommandLoaded?.Invoke(slashCommand);
},
HandleError
);
}
private void HandleError(Exception exception)
{
Application.Logger.Log(exception.Message, this, LogType.Error);
}
private void OnPluginLoaded(PluginLoaderResult result)
{
result.Match(
InitializeDbCommand,
InitializeEvent,
InitializeSlashCommand,
InitializeCommand,
HandleError
);
}
}

View File

@@ -14,7 +14,7 @@ namespace DiscordBotCore.Loaders;
internal static class PluginLoaderExtensions
{
internal static bool TryStartEvent(this PluginLoader pluginLoader, IDbEvent? dbEvent)
internal static bool TryStartEvent(this IDbEvent? dbEvent)
{
try
{
@@ -23,7 +23,7 @@ internal static class PluginLoaderExtensions
throw new ArgumentNullException(nameof(dbEvent));
}
dbEvent.Start(pluginLoader._Client);
dbEvent.Start(Application.CurrentApplication.DiscordBotClient.Client);
return true;
}
catch (Exception e)
@@ -34,18 +34,18 @@ internal static class PluginLoaderExtensions
}
}
internal static async Task<bool> TryStartSlashCommand(this PluginLoader pluginLoader, IDbSlashCommand? dbSlashCommand)
internal static async Task<Result> TryStartSlashCommand(this IDbSlashCommand? dbSlashCommand)
{
try
{
if (dbSlashCommand is null)
{
return false;
return Result.Failure(new Exception("dbSlashCommand is null"));
}
if (pluginLoader._Client.Guilds.Count == 0)
if (Application.CurrentApplication.DiscordBotClient.Client.Guilds.Count == 0)
{
return false;
return Result.Failure(new Exception("No guilds found"));
}
var builder = new SlashCommandBuilder();
@@ -60,28 +60,27 @@ internal static class PluginLoaderExtensions
foreach(ulong guildId in Application.CurrentApplication.ServerIDs)
{
bool result = await pluginLoader.EnableSlashCommandPerGuild(guildId, builder);
bool result = await EnableSlashCommandPerGuild(guildId, builder);
if (!result)
{
Application.Logger.Log($"Failed to enable slash command {dbSlashCommand.Name} for guild {guildId}", typeof(PluginLoader), LogType.Error);
return Result.Failure($"Failed to enable slash command {dbSlashCommand.Name} for guild {guildId}");
}
}
await pluginLoader._Client.CreateGlobalApplicationCommandAsync(builder.Build());
await Application.CurrentApplication.DiscordBotClient.Client.CreateGlobalApplicationCommandAsync(builder.Build());
return true;
return Result.Success();
}
catch (Exception e)
{
Application.Logger.Log($"Error starting slash command {dbSlashCommand.Name}: {e.Message}", typeof(PluginLoader), LogType.Error);
return false;
return Result.Failure("Error starting slash command");
}
}
private static async Task<bool> EnableSlashCommandPerGuild(this PluginLoader pluginLoader, ulong guildId, SlashCommandBuilder builder)
private static async Task<bool> EnableSlashCommandPerGuild(ulong guildId, SlashCommandBuilder builder)
{
SocketGuild? guild = pluginLoader._Client.GetGuild(guildId);
SocketGuild? guild = Application.CurrentApplication.DiscordBotClient.Client.GetGuild(guildId);
if (guild is null)
{
Application.Logger.Log("Failed to get guild with ID " + guildId, typeof(PluginLoader), LogType.Error);

View File

@@ -0,0 +1,42 @@
using System;
using DiscordBotCore.Interfaces;
using DiscordBotCore.Others;
using DiscordBotCore.Others.Exceptions;
namespace DiscordBotCore.Loaders;
public class PluginLoaderResult
{
private Option4<IDbCommand, IDbEvent, IDbSlashCommand, ICommandAction, Exception> _Result;
public static PluginLoaderResult FromIDbCommand(IDbCommand command) => new PluginLoaderResult(new Option4<IDbCommand, IDbEvent, IDbSlashCommand, ICommandAction, Exception>(command));
public static PluginLoaderResult FromIDbEvent(IDbEvent dbEvent) => new PluginLoaderResult(new Option4<IDbCommand, IDbEvent, IDbSlashCommand, ICommandAction, Exception>(dbEvent));
public static PluginLoaderResult FromIDbSlashCommand(IDbSlashCommand slashCommand) => new PluginLoaderResult(new Option4<IDbCommand, IDbEvent, IDbSlashCommand, ICommandAction, Exception>(slashCommand));
public static PluginLoaderResult FromICommandAction(ICommandAction commandAction) => new PluginLoaderResult(new Option4<IDbCommand, IDbEvent, IDbSlashCommand, ICommandAction, Exception>(commandAction));
public static PluginLoaderResult FromException(Exception exception) => new PluginLoaderResult(new Option4<IDbCommand, IDbEvent, IDbSlashCommand, ICommandAction, Exception>(exception));
public static PluginLoaderResult FromException(string exception) => new PluginLoaderResult(new Option4<IDbCommand, IDbEvent, IDbSlashCommand, ICommandAction, Exception>(new Exception(message: exception)));
private PluginLoaderResult(Option4<IDbCommand, IDbEvent, IDbSlashCommand, ICommandAction, Exception> result)
{
_Result = result;
}
public void Match(Action<IDbCommand> commandAction, Action<IDbEvent> eventAction, Action<IDbSlashCommand> slashCommandAction,
Action<ICommandAction> commandActionAction, Action<Exception> exceptionAction)
{
_Result.Match(commandAction, eventAction, slashCommandAction, commandActionAction, exceptionAction);
}
public TResult Match<TResult>(Func<IDbCommand, TResult> commandFunc, Func<IDbEvent, TResult> eventFunc,
Func<IDbSlashCommand, TResult> slashCommandFunc, Func<ICommandAction, TResult> commandActionFunc,
Func<Exception, TResult> exceptionFunc)
{
return _Result.Match(commandFunc, eventFunc, slashCommandFunc, commandActionFunc, exceptionFunc);
}
}