Fixed plugin reloading

This commit is contained in:
2025-06-17 17:20:03 +03:00
parent e9b61cc4be
commit f68b6cb877
6 changed files with 68 additions and 56 deletions

View File

@@ -23,5 +23,5 @@ public interface IPluginLoader
/// <summary> /// <summary>
/// Unload all plugins from the plugin manager. /// Unload all plugins from the plugin manager.
/// </summary> /// </summary>
public void UnloadAllPlugins(); public Task UnloadAllPlugins();
} }

View File

@@ -7,6 +7,7 @@ using DiscordBotCore.Logging;
using DiscordBotCore.PluginCore.Helpers.Execution.DbEvent; using DiscordBotCore.PluginCore.Helpers.Execution.DbEvent;
using DiscordBotCore.PluginCore.Interfaces; using DiscordBotCore.PluginCore.Interfaces;
using DiscordBotCore.PluginManagement.Loading.Exceptions; using DiscordBotCore.PluginManagement.Loading.Exceptions;
using DiscordBotCore.Utilities.Responses;
namespace DiscordBotCore.PluginManagement.Loading; namespace DiscordBotCore.PluginManagement.Loading;
@@ -24,8 +25,7 @@ public class PluginLoader : IPluginLoader
private readonly List<IDbCommand> _Commands = new List<IDbCommand>(); private readonly List<IDbCommand> _Commands = new List<IDbCommand>();
private readonly List<IDbEvent> _Events = new List<IDbEvent>(); private readonly List<IDbEvent> _Events = new List<IDbEvent>();
private readonly List<IDbSlashCommand> _SlashCommands = new List<IDbSlashCommand>(); private readonly List<IDbSlashCommand> _SlashCommands = new List<IDbSlashCommand>();
private readonly List<SocketApplicationCommand> _ApplicationCommands = new List<SocketApplicationCommand>();
private bool _IsFirstLoad = true;
public PluginLoader(IPluginManager pluginManager, ILogger logger, IConfiguration configuration) public PluginLoader(IPluginManager pluginManager, ILogger logger, IConfiguration configuration)
{ {
@@ -57,11 +57,16 @@ public class PluginLoader : IPluginLoader
public async Task LoadPlugins() public async Task LoadPlugins()
{ {
UnloadAllPlugins(); if (PluginLoaderContext is not null)
{
_Logger.Log("The plugins are already loaded", this, LogType.Error);
return;
}
_Events.Clear(); _Events.Clear();
_Commands.Clear(); _Commands.Clear();
_SlashCommands.Clear(); _SlashCommands.Clear();
_ApplicationCommands.Clear();
await LoadPluginFiles(); await LoadPluginFiles();
@@ -88,37 +93,45 @@ public class PluginLoader : IPluginLoader
_Logger.Log("Loaded plugins", this); _Logger.Log("Loaded plugins", this);
} }
public void UnloadAllPlugins() public async Task UnloadAllPlugins()
{ {
if (_IsFirstLoad)
{
// Allow unloading only after the first load
_IsFirstLoad = false;
return;
}
if (PluginLoaderContext is null) if (PluginLoaderContext is null)
{ {
_Logger.Log("The plugins are not loaded. Please load the plugins before unloading them.", this, LogType.Error); _Logger.Log("The plugins are not loaded. Please load the plugins before unloading them.", this, LogType.Error);
return; return;
} }
await UnloadSlashCommands();
PluginLoaderContext.Unload(); PluginLoaderContext.Unload();
PluginLoaderContext = null;
GC.Collect(); GC.Collect();
GC.WaitForPendingFinalizers(); GC.WaitForPendingFinalizers();
GC.Collect(); GC.Collect();
PluginLoaderContext = null;
}
private async Task UnloadSlashCommands()
{
if (_DiscordClient is null)
{
_Logger.Log("The client is not set. Please set the client before unloading slash commands.", this, LogType.Error);
return;
}
foreach (SocketApplicationCommand command in _ApplicationCommands)
{
await command.DeleteAsync();
}
_ApplicationCommands.Clear();
_Logger.Log("Unloaded all slash commands", this);
} }
private async Task LoadPluginFiles() private async Task LoadPluginFiles()
{ {
if (PluginLoaderContext is not null)
{
_Logger.Log("The plugins are already loaded", this, LogType.Error);
return;
}
var installedPlugins = await _PluginManager.GetInstalledPlugins(); var installedPlugins = await _PluginManager.GetInstalledPlugins();
if (installedPlugins.Count == 0) if (installedPlugins.Count == 0)
@@ -314,34 +327,45 @@ public class PluginLoader : IPluginLoader
builder.WithContextTypes(InteractionContextType.Guild); builder.WithContextTypes(InteractionContextType.Guild);
List<ulong> serverIds = _Configuration.GetList("ServerIds", new List<ulong>()); List<ulong> serverIds = _Configuration.GetList("ServerIds", new List<ulong>());
foreach(ulong guildId in serverIds)
{
bool result = await EnableSlashCommandPerGuild(guildId, builder);
if (!result)
{
_Logger.Log($"Failed to enable slash command {dbSlashCommand.Name} for guild {guildId}", this, LogType.Error);
return false;
}
}
await _DiscordClient.CreateGlobalApplicationCommandAsync(builder.Build());
if (serverIds.Any())
{
foreach(ulong guildId in serverIds)
{
IResponse<SocketApplicationCommand> result = await EnableSlashCommandPerGuild(guildId, builder);
if (!result.IsSuccess)
{
_Logger.Log($"Failed to enable slash command {dbSlashCommand.Name} for guild {guildId}", this, LogType.Error);
continue;
}
if (result.Data is null)
{
continue;
}
_ApplicationCommands.Add(result.Data);
}
return true;
}
var command = await _DiscordClient.CreateGlobalApplicationCommandAsync(builder.Build());
_ApplicationCommands.Add(command);
return true; return true;
} }
private async Task<bool> EnableSlashCommandPerGuild(ulong guildId, SlashCommandBuilder builder) private async Task<IResponse<SocketApplicationCommand>> EnableSlashCommandPerGuild(ulong guildId, SlashCommandBuilder builder)
{ {
SocketGuild? guild = _DiscordClient?.GetGuild(guildId); SocketGuild? guild = _DiscordClient?.GetGuild(guildId);
if (guild is null) if (guild is null)
{ {
_Logger.Log("Failed to get guild with ID " + guildId, this, LogType.Error); _Logger.Log("Failed to get guild with ID " + guildId, this, LogType.Error);
return false; return Response<SocketApplicationCommand>.Failure("Failed to get guild with ID " + guildId);
} }
await guild.CreateApplicationCommandAsync(builder.Build()); var command = await guild.CreateApplicationCommandAsync(builder.Build());
return Response<SocketApplicationCommand>.Success(command);
return true;
} }
} }

View File

@@ -44,6 +44,8 @@ public class DiscordBotApplication : IDiscordBotApplication
return; return;
} }
await _PluginLoader.UnloadAllPlugins();
await Client.LogoutAsync(); await Client.LogoutAsync();
await Client.StopAsync(); await Client.StopAsync();
@@ -54,6 +56,7 @@ public class DiscordBotApplication : IDiscordBotApplication
await Client.DisposeAsync(); await Client.DisposeAsync();
IsReady = false; IsReady = false;
} }
@@ -73,7 +76,7 @@ public class DiscordBotApplication : IDiscordBotApplication
}; };
DiscordSocketClient client = new DiscordSocketClient(config); DiscordSocketClient client = new DiscordSocketClient(config);
Client = client;
_Service = new CommandService(); _Service = new CommandService();
@@ -82,8 +85,8 @@ public class DiscordBotApplication : IDiscordBotApplication
client.Ready += Ready; client.Ready += Ready;
client.Disconnected += Client_Disconnected; client.Disconnected += Client_Disconnected;
Client = client;
await client.LoginAsync(TokenType.Bot, _Configuration.Get<string>("token")); await client.LoginAsync(TokenType.Bot, _Configuration.Get<string>("token"));
await client.StartAsync(); await client.StartAsync();
_CommandServiceHandler = new CommandHandler(_Logger, _PluginLoader, _Configuration, _Service); _CommandServiceHandler = new CommandHandler(_Logger, _PluginLoader, _Configuration, _Service);

View File

@@ -13,8 +13,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Modules", "Modules", "{EA4F
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LevelingSystem", "Plugins\LevelingSystem\LevelingSystem.csproj", "{FCE9743F-7EB4-4639-A080-FCDDFCC7D689}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LevelingSystem", "Plugins\LevelingSystem\LevelingSystem.csproj", "{FCE9743F-7EB4-4639-A080-FCDDFCC7D689}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MusicPlayer", "Plugins\MusicPlayer\MusicPlayer.csproj", "{F3C61A47-F758-4145-B496-E3ECCF979D89}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CppCompatibilityModule", "Modules\CppCompatibilityModule\CppCompatibilityModule.csproj", "{C67908F9-4A55-4DD8-B993-C26C648226F1}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CppCompatibilityModule", "Modules\CppCompatibilityModule\CppCompatibilityModule.csproj", "{C67908F9-4A55-4DD8-B993-C26C648226F1}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DiscordBotCore.PluginCore", "DiscordBotCore.PluginCore\DiscordBotCore.PluginCore.csproj", "{B5725C86-2633-4C7A-A6A4-49AAA2767E54}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DiscordBotCore.PluginCore", "DiscordBotCore.PluginCore\DiscordBotCore.PluginCore.csproj", "{B5725C86-2633-4C7A-A6A4-49AAA2767E54}"
@@ -77,18 +75,6 @@ Global
{FCE9743F-7EB4-4639-A080-FCDDFCC7D689}.Release|ARM64.Build.0 = Release|ARM64 {FCE9743F-7EB4-4639-A080-FCDDFCC7D689}.Release|ARM64.Build.0 = Release|ARM64
{FCE9743F-7EB4-4639-A080-FCDDFCC7D689}.Release|x64.ActiveCfg = Release|x64 {FCE9743F-7EB4-4639-A080-FCDDFCC7D689}.Release|x64.ActiveCfg = Release|x64
{FCE9743F-7EB4-4639-A080-FCDDFCC7D689}.Release|x64.Build.0 = Release|x64 {FCE9743F-7EB4-4639-A080-FCDDFCC7D689}.Release|x64.Build.0 = Release|x64
{F3C61A47-F758-4145-B496-E3ECCF979D89}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F3C61A47-F758-4145-B496-E3ECCF979D89}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F3C61A47-F758-4145-B496-E3ECCF979D89}.Debug|ARM64.ActiveCfg = Debug|ARM64
{F3C61A47-F758-4145-B496-E3ECCF979D89}.Debug|ARM64.Build.0 = Debug|ARM64
{F3C61A47-F758-4145-B496-E3ECCF979D89}.Debug|x64.ActiveCfg = Debug|x64
{F3C61A47-F758-4145-B496-E3ECCF979D89}.Debug|x64.Build.0 = Debug|x64
{F3C61A47-F758-4145-B496-E3ECCF979D89}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F3C61A47-F758-4145-B496-E3ECCF979D89}.Release|Any CPU.Build.0 = Release|Any CPU
{F3C61A47-F758-4145-B496-E3ECCF979D89}.Release|ARM64.ActiveCfg = Release|ARM64
{F3C61A47-F758-4145-B496-E3ECCF979D89}.Release|ARM64.Build.0 = Release|ARM64
{F3C61A47-F758-4145-B496-E3ECCF979D89}.Release|x64.ActiveCfg = Release|x64
{F3C61A47-F758-4145-B496-E3ECCF979D89}.Release|x64.Build.0 = Release|x64
{C67908F9-4A55-4DD8-B993-C26C648226F1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C67908F9-4A55-4DD8-B993-C26C648226F1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C67908F9-4A55-4DD8-B993-C26C648226F1}.Debug|Any CPU.Build.0 = Debug|Any CPU {C67908F9-4A55-4DD8-B993-C26C648226F1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C67908F9-4A55-4DD8-B993-C26C648226F1}.Debug|ARM64.ActiveCfg = Debug|ARM64 {C67908F9-4A55-4DD8-B993-C26C648226F1}.Debug|ARM64.ActiveCfg = Debug|ARM64
@@ -251,7 +237,6 @@ Global
EndGlobalSection EndGlobalSection
GlobalSection(NestedProjects) = preSolution GlobalSection(NestedProjects) = preSolution
{FCE9743F-7EB4-4639-A080-FCDDFCC7D689} = {5CF9AD7B-6BF0-4035-835F-722F989C01E1} {FCE9743F-7EB4-4639-A080-FCDDFCC7D689} = {5CF9AD7B-6BF0-4035-835F-722F989C01E1}
{F3C61A47-F758-4145-B496-E3ECCF979D89} = {5CF9AD7B-6BF0-4035-835F-722F989C01E1}
{C67908F9-4A55-4DD8-B993-C26C648226F1} = {EA4FA308-7B2C-458E-8485-8747D745DD59} {C67908F9-4A55-4DD8-B993-C26C648226F1} = {EA4FA308-7B2C-458E-8485-8747D745DD59}
{9A4B98C1-00AC-481C-BE55-A70C0B9D3BE7} = {5CF9AD7B-6BF0-4035-835F-722F989C01E1} {9A4B98C1-00AC-481C-BE55-A70C0B9D3BE7} = {5CF9AD7B-6BF0-4035-835F-722F989C01E1}
{94238D37-60C6-4E40-80EC-4B4D242F5914} = {8F27B3EA-F292-40DF-B9B3-4B0E6BEA4E70} {94238D37-60C6-4E40-80EC-4B4D242F5914} = {8F27B3EA-F292-40DF-B9B3-4B0E6BEA4E70}

View File

@@ -82,7 +82,7 @@
private async Task DeletePluginButtonClick(InstalledPlugin plugin) private async Task DeletePluginButtonClick(InstalledPlugin plugin)
{ {
PluginLoader.UnloadAllPlugins(); await PluginLoader.UnloadAllPlugins();
Logger.Log($"Deleting plugin {plugin.Name}", this); Logger.Log($"Deleting plugin {plugin.Name}", this);

View File

@@ -30,7 +30,7 @@
</div> </div>
<button type="submit" class="btn btn-primary w-100"> <button type="submit" class="btn btn-primary w-100">
Save&nbsp;<i class="bi bi-check-lg"></i> Save
</button> </button>
</EditForm> </EditForm>
</CenteredCard> </CenteredCard>