DI: Build root scope async while still registering events/commands during plugin init
This commit is contained in:
parent
adddbc452c
commit
f63e70b0c4
@ -1,141 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
using Dalamud.Game.ClientState;
|
|
||||||
using Dalamud.Game.Command;
|
|
||||||
using Dalamud.Game.Gui;
|
|
||||||
using ECommons.Schedulers;
|
|
||||||
using Pal.Client.Configuration;
|
|
||||||
using Pal.Client.DependencyInjection;
|
|
||||||
using Pal.Client.Extensions;
|
|
||||||
using Pal.Client.Properties;
|
|
||||||
using Pal.Client.Rendering;
|
|
||||||
using Pal.Client.Windows;
|
|
||||||
|
|
||||||
namespace Pal.Client.Commands
|
|
||||||
{
|
|
||||||
// should restructure this when more commands exist, if that ever happens
|
|
||||||
// this command is more-or-less a debug/troubleshooting command, if anything
|
|
||||||
internal sealed class PalCommand : IDisposable
|
|
||||||
{
|
|
||||||
private readonly IPalacePalConfiguration _configuration;
|
|
||||||
private readonly CommandManager _commandManager;
|
|
||||||
private readonly Chat _chat;
|
|
||||||
private readonly StatisticsService _statisticsService;
|
|
||||||
private readonly ConfigWindow _configWindow;
|
|
||||||
private readonly TerritoryState _territoryState;
|
|
||||||
private readonly FloorService _floorService;
|
|
||||||
private readonly ClientState _clientState;
|
|
||||||
|
|
||||||
public PalCommand(
|
|
||||||
IPalacePalConfiguration configuration,
|
|
||||||
CommandManager commandManager,
|
|
||||||
Chat chat,
|
|
||||||
StatisticsService statisticsService,
|
|
||||||
ConfigWindow configWindow,
|
|
||||||
TerritoryState territoryState,
|
|
||||||
FloorService floorService,
|
|
||||||
ClientState clientState)
|
|
||||||
{
|
|
||||||
_configuration = configuration;
|
|
||||||
_commandManager = commandManager;
|
|
||||||
_chat = chat;
|
|
||||||
_statisticsService = statisticsService;
|
|
||||||
_configWindow = configWindow;
|
|
||||||
_territoryState = territoryState;
|
|
||||||
_floorService = floorService;
|
|
||||||
_clientState = clientState;
|
|
||||||
|
|
||||||
_commandManager.AddHandler("/pal", new CommandInfo(OnCommand)
|
|
||||||
{
|
|
||||||
HelpMessage = Localization.Command_pal_HelpText
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_commandManager.RemoveHandler("/pal");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnCommand(string command, string arguments)
|
|
||||||
{
|
|
||||||
if (_configuration.FirstUse)
|
|
||||||
{
|
|
||||||
_chat.Error(Localization.Error_FirstTimeSetupRequired);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
arguments = arguments.Trim();
|
|
||||||
switch (arguments)
|
|
||||||
{
|
|
||||||
case "stats":
|
|
||||||
_statisticsService.ShowGlobalStatistics();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "test-connection":
|
|
||||||
case "tc":
|
|
||||||
_configWindow.IsOpen = true;
|
|
||||||
var _ = new TickScheduler(() => _configWindow.TestConnection());
|
|
||||||
break;
|
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
case "update-saves":
|
|
||||||
LocalState.UpdateAll();
|
|
||||||
_chat.Message(Localization.Command_pal_updatesaves);
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
case "":
|
|
||||||
case "config":
|
|
||||||
_configWindow.Toggle();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "near":
|
|
||||||
DebugNearest(_ => true);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "tnear":
|
|
||||||
DebugNearest(m => m.Type == Marker.EType.Trap);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "hnear":
|
|
||||||
DebugNearest(m => m.Type == Marker.EType.Hoard);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
_chat.Error(string.Format(Localization.Command_pal_UnknownSubcommand, arguments,
|
|
||||||
command));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
_chat.Error(e.ToString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DebugNearest(Predicate<Marker> predicate)
|
|
||||||
{
|
|
||||||
if (!_territoryState.IsInDeepDungeon())
|
|
||||||
return;
|
|
||||||
|
|
||||||
var state = _floorService.GetFloorMarkers(_clientState.TerritoryType);
|
|
||||||
var playerPosition = _clientState.LocalPlayer?.Position;
|
|
||||||
if (playerPosition == null)
|
|
||||||
return;
|
|
||||||
_chat.Message($"{playerPosition}");
|
|
||||||
|
|
||||||
var nearbyMarkers = state.Markers
|
|
||||||
.Where(m => predicate(m))
|
|
||||||
.Where(m => m.RenderElement != null && m.RenderElement.Color != RenderData.ColorInvisible)
|
|
||||||
.Select(m => new { m, distance = (playerPosition - m.Position)?.Length() ?? float.MaxValue })
|
|
||||||
.OrderBy(m => m.distance)
|
|
||||||
.Take(5)
|
|
||||||
.ToList();
|
|
||||||
foreach (var nearbyMarker in nearbyMarkers)
|
|
||||||
_chat.UnformattedMessage(
|
|
||||||
$"{nearbyMarker.distance:F2} - {nearbyMarker.m.Type} {nearbyMarker.m.NetworkId?.ToPartialId(length: 8)} - {nearbyMarker.m.Position}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
31
Pal.Client/Commands/PalConfigCommand.cs
Normal file
31
Pal.Client/Commands/PalConfigCommand.cs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
using Dalamud.Interface.Windowing;
|
||||||
|
using Pal.Client.Configuration;
|
||||||
|
using Pal.Client.Windows;
|
||||||
|
|
||||||
|
namespace Pal.Client.Commands
|
||||||
|
{
|
||||||
|
internal class PalConfigCommand
|
||||||
|
{
|
||||||
|
private readonly IPalacePalConfiguration _configuration;
|
||||||
|
private readonly AgreementWindow _agreementWindow;
|
||||||
|
private readonly ConfigWindow _configWindow;
|
||||||
|
|
||||||
|
public PalConfigCommand(
|
||||||
|
IPalacePalConfiguration configuration,
|
||||||
|
AgreementWindow agreementWindow,
|
||||||
|
ConfigWindow configWindow)
|
||||||
|
{
|
||||||
|
_configuration = configuration;
|
||||||
|
_agreementWindow = agreementWindow;
|
||||||
|
_configWindow = configWindow;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Execute()
|
||||||
|
{
|
||||||
|
if (_configuration.FirstUse)
|
||||||
|
_agreementWindow.IsOpen = true;
|
||||||
|
else
|
||||||
|
_configWindow.Toggle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
67
Pal.Client/Commands/PalNearCommand.cs
Normal file
67
Pal.Client/Commands/PalNearCommand.cs
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using Dalamud.Game.ClientState;
|
||||||
|
using Pal.Client.DependencyInjection;
|
||||||
|
using Pal.Client.Extensions;
|
||||||
|
using Pal.Client.Rendering;
|
||||||
|
|
||||||
|
namespace Pal.Client.Commands
|
||||||
|
{
|
||||||
|
internal sealed class PalNearCommand
|
||||||
|
{
|
||||||
|
private readonly Chat _chat;
|
||||||
|
private readonly ClientState _clientState;
|
||||||
|
private readonly TerritoryState _territoryState;
|
||||||
|
private readonly FloorService _floorService;
|
||||||
|
|
||||||
|
public PalNearCommand(Chat chat, ClientState clientState, TerritoryState territoryState,
|
||||||
|
FloorService floorService)
|
||||||
|
{
|
||||||
|
_chat = chat;
|
||||||
|
_clientState = clientState;
|
||||||
|
_territoryState = territoryState;
|
||||||
|
_floorService = floorService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Execute(string arguments)
|
||||||
|
{
|
||||||
|
switch (arguments)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
DebugNearest(_ => true);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "tnear":
|
||||||
|
DebugNearest(m => m.Type == Marker.EType.Trap);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "hnear":
|
||||||
|
DebugNearest(m => m.Type == Marker.EType.Hoard);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DebugNearest(Predicate<Marker> predicate)
|
||||||
|
{
|
||||||
|
if (!_territoryState.IsInDeepDungeon())
|
||||||
|
return;
|
||||||
|
|
||||||
|
var state = _floorService.GetFloorMarkers(_clientState.TerritoryType);
|
||||||
|
var playerPosition = _clientState.LocalPlayer?.Position;
|
||||||
|
if (playerPosition == null)
|
||||||
|
return;
|
||||||
|
_chat.Message($"{playerPosition}");
|
||||||
|
|
||||||
|
var nearbyMarkers = state.Markers
|
||||||
|
.Where(m => predicate(m))
|
||||||
|
.Where(m => m.RenderElement != null && m.RenderElement.Color != RenderData.ColorInvisible)
|
||||||
|
.Select(m => new { m, distance = (playerPosition - m.Position)?.Length() ?? float.MaxValue })
|
||||||
|
.OrderBy(m => m.distance)
|
||||||
|
.Take(5)
|
||||||
|
.ToList();
|
||||||
|
foreach (var nearbyMarker in nearbyMarkers)
|
||||||
|
_chat.UnformattedMessage(
|
||||||
|
$"{nearbyMarker.distance:F2} - {nearbyMarker.m.Type} {nearbyMarker.m.NetworkId?.ToPartialId(length: 8)} - {nearbyMarker.m.Position}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
18
Pal.Client/Commands/PalStatsCommand.cs
Normal file
18
Pal.Client/Commands/PalStatsCommand.cs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
using System;
|
||||||
|
using Pal.Client.DependencyInjection;
|
||||||
|
|
||||||
|
namespace Pal.Client.Commands
|
||||||
|
{
|
||||||
|
internal sealed class PalStatsCommand
|
||||||
|
{
|
||||||
|
private readonly StatisticsService _statisticsService;
|
||||||
|
|
||||||
|
public PalStatsCommand(StatisticsService statisticsService)
|
||||||
|
{
|
||||||
|
_statisticsService = statisticsService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Execute()
|
||||||
|
=> _statisticsService.ShowGlobalStatistics();
|
||||||
|
}
|
||||||
|
}
|
20
Pal.Client/Commands/PalTestConnectionCommand.cs
Normal file
20
Pal.Client/Commands/PalTestConnectionCommand.cs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
using ECommons.Schedulers;
|
||||||
|
using Pal.Client.Windows;
|
||||||
|
|
||||||
|
namespace Pal.Client.Commands
|
||||||
|
{
|
||||||
|
internal sealed class PalTestConnectionCommand
|
||||||
|
{
|
||||||
|
private readonly ConfigWindow _configWindow;
|
||||||
|
|
||||||
|
public PalTestConnectionCommand(ConfigWindow configWindow)
|
||||||
|
{
|
||||||
|
_configWindow = configWindow;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Execute()
|
||||||
|
{
|
||||||
|
var _ = new TickScheduler(() => _configWindow.TestConnection());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -52,7 +52,8 @@ namespace Pal.Client
|
|||||||
|
|
||||||
public string Name => Localization.Palace_Pal;
|
public string Name => Localization.Palace_Pal;
|
||||||
|
|
||||||
public DependencyInjectionContext(DalamudPluginInterface pluginInterface,
|
public DependencyInjectionContext(
|
||||||
|
DalamudPluginInterface pluginInterface,
|
||||||
ClientState clientState,
|
ClientState clientState,
|
||||||
GameGui gameGui,
|
GameGui gameGui,
|
||||||
ChatGui chatGui,
|
ChatGui chatGui,
|
||||||
@ -70,7 +71,6 @@ namespace Pal.Client
|
|||||||
#pragma warning restore CS0612
|
#pragma warning restore CS0612
|
||||||
|
|
||||||
// set up logging
|
// set up logging
|
||||||
CancellationToken token = _initCts.Token;
|
|
||||||
IServiceCollection services = new ServiceCollection();
|
IServiceCollection services = new ServiceCollection();
|
||||||
services.AddLogging(builder =>
|
services.AddLogging(builder =>
|
||||||
builder.AddFilter("Pal", LogLevel.Trace)
|
builder.AddFilter("Pal", LogLevel.Trace)
|
||||||
@ -100,32 +100,37 @@ namespace Pal.Client
|
|||||||
services.AddTransient<JsonMigration>();
|
services.AddTransient<JsonMigration>();
|
||||||
|
|
||||||
// plugin-specific
|
// plugin-specific
|
||||||
services.AddSingleton<Plugin>();
|
services.AddScoped<DependencyInjectionLoader>();
|
||||||
services.AddSingleton<DebugState>();
|
services.AddScoped<DebugState>();
|
||||||
services.AddSingleton<Hooks>();
|
services.AddScoped<Hooks>();
|
||||||
services.AddSingleton<RemoteApi>();
|
services.AddScoped<RemoteApi>();
|
||||||
services.AddSingleton<ConfigurationManager>();
|
services.AddScoped<ConfigurationManager>();
|
||||||
services.AddSingleton<IPalacePalConfiguration>(sp => sp.GetRequiredService<ConfigurationManager>().Load());
|
services.AddScoped<IPalacePalConfiguration>(sp => sp.GetRequiredService<ConfigurationManager>().Load());
|
||||||
services.AddTransient<RepoVerification>();
|
services.AddTransient<RepoVerification>();
|
||||||
services.AddSingleton<PalCommand>();
|
|
||||||
|
// commands
|
||||||
|
services.AddScoped<PalConfigCommand>();
|
||||||
|
services.AddScoped<PalNearCommand>();
|
||||||
|
services.AddScoped<PalStatsCommand>();
|
||||||
|
services.AddScoped<PalTestConnectionCommand>();
|
||||||
|
|
||||||
// territory & marker related services
|
// territory & marker related services
|
||||||
services.AddSingleton<TerritoryState>();
|
services.AddScoped<TerritoryState>();
|
||||||
services.AddSingleton<FrameworkService>();
|
services.AddScoped<FrameworkService>();
|
||||||
services.AddSingleton<ChatService>();
|
services.AddScoped<ChatService>();
|
||||||
services.AddSingleton<FloorService>();
|
services.AddScoped<FloorService>();
|
||||||
services.AddSingleton<ImportService>();
|
services.AddScoped<ImportService>();
|
||||||
|
|
||||||
// windows & related services
|
// windows & related services
|
||||||
services.AddSingleton<AgreementWindow>();
|
services.AddScoped<AgreementWindow>();
|
||||||
services.AddSingleton<ConfigWindow>();
|
services.AddScoped<ConfigWindow>();
|
||||||
services.AddTransient<StatisticsService>();
|
services.AddScoped<StatisticsService>();
|
||||||
services.AddSingleton<StatisticsWindow>();
|
services.AddScoped<StatisticsWindow>();
|
||||||
|
|
||||||
// these should maybe be scoped
|
// these should maybe be scoped
|
||||||
services.AddScoped<SimpleRenderer>();
|
services.AddScoped<SimpleRenderer>();
|
||||||
services.AddScoped<SplatoonRenderer>();
|
services.AddScoped<SplatoonRenderer>();
|
||||||
services.AddSingleton<RenderAdapter>();
|
services.AddScoped<RenderAdapter>();
|
||||||
|
|
||||||
// queue handling
|
// queue handling
|
||||||
services.AddTransient<IQueueOnFrameworkThread.Handler<QueuedImport>, QueuedImport.Handler>();
|
services.AddTransient<IQueueOnFrameworkThread.Handler<QueuedImport>, QueuedImport.Handler>();
|
||||||
@ -161,63 +166,8 @@ namespace Pal.Client
|
|||||||
// There's 2-3 seconds of slowdown primarily caused by the sqlite init, but that needs to happen for
|
// There's 2-3 seconds of slowdown primarily caused by the sqlite init, but that needs to happen for
|
||||||
// config stuff.
|
// config stuff.
|
||||||
_logger = _serviceProvider.GetRequiredService<ILogger<DependencyInjectionContext>>();
|
_logger = _serviceProvider.GetRequiredService<ILogger<DependencyInjectionContext>>();
|
||||||
_logger.LogInformation("Service container built, triggering async init");
|
_logger.LogInformation("Service container built, creating plugin");
|
||||||
Task.Run(async () =>
|
_plugin = new Plugin(pluginInterface, _serviceProvider, _initCts.Token);
|
||||||
{
|
|
||||||
using IDisposable? logScope = _logger.BeginScope("AsyncInit");
|
|
||||||
|
|
||||||
Chat? chat = null;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_logger.LogInformation("Starting async init");
|
|
||||||
chat = _serviceProvider.GetService<Chat>();
|
|
||||||
|
|
||||||
// initialize database
|
|
||||||
await using (var scope = _serviceProvider.CreateAsyncScope())
|
|
||||||
{
|
|
||||||
_logger.LogInformation("Loading database & running migrations");
|
|
||||||
await using var dbContext = scope.ServiceProvider.GetRequiredService<PalClientContext>();
|
|
||||||
await dbContext.Database.MigrateAsync(token);
|
|
||||||
|
|
||||||
_logger.LogInformation("Completed database migrations");
|
|
||||||
}
|
|
||||||
|
|
||||||
token.ThrowIfCancellationRequested();
|
|
||||||
|
|
||||||
// v1 migration: config migration for import history, json migration for markers
|
|
||||||
_serviceProvider.GetRequiredService<ConfigurationManager>().Migrate();
|
|
||||||
await _serviceProvider.GetRequiredService<JsonMigration>().MigrateAsync(token);
|
|
||||||
|
|
||||||
token.ThrowIfCancellationRequested();
|
|
||||||
|
|
||||||
// windows that have logic to open on startup
|
|
||||||
_serviceProvider.GetRequiredService<AgreementWindow>();
|
|
||||||
|
|
||||||
// initialize components that are mostly self-contained/self-registered
|
|
||||||
_serviceProvider.GetRequiredService<Hooks>();
|
|
||||||
_serviceProvider.GetRequiredService<PalCommand>();
|
|
||||||
_serviceProvider.GetRequiredService<FrameworkService>();
|
|
||||||
_serviceProvider.GetRequiredService<ChatService>();
|
|
||||||
|
|
||||||
token.ThrowIfCancellationRequested();
|
|
||||||
_plugin = new Plugin(pluginInterface, _serviceProvider);
|
|
||||||
|
|
||||||
_logger.LogInformation("Async init complete");
|
|
||||||
}
|
|
||||||
catch (ObjectDisposedException)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
catch (TaskCanceledException e)
|
|
||||||
{
|
|
||||||
_logger.LogError(e, "Task cancelled");
|
|
||||||
chat?.Error("Plugin was unloaded before it finished loading.");
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
_logger.LogError(e, "Async load failed");
|
|
||||||
chat?.Error($"Async loading failed: {e.GetType()}: {e.Message}");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
107
Pal.Client/DependencyInjectionLoader.cs
Normal file
107
Pal.Client/DependencyInjectionLoader.cs
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Pal.Client.Commands;
|
||||||
|
using Pal.Client.Configuration;
|
||||||
|
using Pal.Client.Configuration.Legacy;
|
||||||
|
using Pal.Client.Database;
|
||||||
|
using Pal.Client.DependencyInjection;
|
||||||
|
using Pal.Client.Properties;
|
||||||
|
using Pal.Client.Windows;
|
||||||
|
|
||||||
|
namespace Pal.Client
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Takes care of async plugin init - this is mostly everything that requires either the config or the database to
|
||||||
|
/// be available.
|
||||||
|
/// </summary>
|
||||||
|
internal sealed class DependencyInjectionLoader
|
||||||
|
{
|
||||||
|
private readonly ILogger<DependencyInjectionLoader> _logger;
|
||||||
|
private readonly IServiceProvider _serviceProvider;
|
||||||
|
|
||||||
|
public DependencyInjectionLoader(ILogger<DependencyInjectionLoader> logger, IServiceProvider serviceProvider)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_serviceProvider = serviceProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ELoadState LoadState { get; private set; } = ELoadState.Initializing;
|
||||||
|
|
||||||
|
public event Action<Action?>? InitCompleted;
|
||||||
|
|
||||||
|
public async Task InitializeAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
using IDisposable? logScope = _logger.BeginScope("AsyncInit");
|
||||||
|
|
||||||
|
Chat? chat = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Starting async init");
|
||||||
|
chat = _serviceProvider.GetService<Chat>();
|
||||||
|
|
||||||
|
// initialize database
|
||||||
|
await using (var scope = _serviceProvider.CreateAsyncScope())
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Loading database & running migrations");
|
||||||
|
await using var dbContext = scope.ServiceProvider.GetRequiredService<PalClientContext>();
|
||||||
|
|
||||||
|
// takes 2-3 seconds with initializing connections, loading driver etc.
|
||||||
|
await dbContext.Database.MigrateAsync(cancellationToken);
|
||||||
|
_logger.LogInformation("Completed database migrations");
|
||||||
|
}
|
||||||
|
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
|
// v1 migration: config migration for import history, json migration for markers
|
||||||
|
_serviceProvider.GetRequiredService<ConfigurationManager>().Migrate();
|
||||||
|
await _serviceProvider.GetRequiredService<JsonMigration>().MigrateAsync(cancellationToken);
|
||||||
|
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
|
// windows that have logic to open on startup
|
||||||
|
_serviceProvider.GetRequiredService<AgreementWindow>();
|
||||||
|
|
||||||
|
// initialize components that are mostly self-contained/self-registered
|
||||||
|
_serviceProvider.GetRequiredService<Hooks>();
|
||||||
|
_serviceProvider.GetRequiredService<FrameworkService>();
|
||||||
|
_serviceProvider.GetRequiredService<ChatService>();
|
||||||
|
|
||||||
|
// eager load any commands to find errors now, not when running them
|
||||||
|
_serviceProvider.GetRequiredService<PalConfigCommand>();
|
||||||
|
_serviceProvider.GetRequiredService<PalNearCommand>();
|
||||||
|
_serviceProvider.GetRequiredService<PalStatsCommand>();
|
||||||
|
_serviceProvider.GetRequiredService<PalTestConnectionCommand>();
|
||||||
|
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
|
LoadState = ELoadState.Loaded;
|
||||||
|
InitCompleted?.Invoke(null);
|
||||||
|
_logger.LogInformation("Async init complete");
|
||||||
|
}
|
||||||
|
catch (ObjectDisposedException)
|
||||||
|
{
|
||||||
|
InitCompleted?.Invoke(null);
|
||||||
|
LoadState = ELoadState.Error;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
_logger.LogError(e, "Async load failed");
|
||||||
|
InitCompleted?.Invoke(() => chat?.Error(string.Format(Localization.Error_LoadFailed, $"{e.GetType()} - {e.Message}")));
|
||||||
|
|
||||||
|
LoadState = ELoadState.Error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ELoadState
|
||||||
|
{
|
||||||
|
Initializing,
|
||||||
|
Loaded,
|
||||||
|
Error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -5,11 +5,17 @@ using Pal.Client.Windows;
|
|||||||
using System;
|
using System;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Dalamud.Game.ClientState;
|
||||||
|
using Dalamud.Game.Command;
|
||||||
using Pal.Client.Properties;
|
using Pal.Client.Properties;
|
||||||
using ECommons;
|
using ECommons;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Pal.Client.Commands;
|
||||||
using Pal.Client.Configuration;
|
using Pal.Client.Configuration;
|
||||||
|
using Pal.Client.DependencyInjection;
|
||||||
|
|
||||||
namespace Pal.Client
|
namespace Pal.Client
|
||||||
{
|
{
|
||||||
@ -17,51 +23,120 @@ namespace Pal.Client
|
|||||||
/// With all DI logic elsewhere, this plugin shell really only takes care of a few things around events that
|
/// With all DI logic elsewhere, this plugin shell really only takes care of a few things around events that
|
||||||
/// need to be sent to different receivers depending on priority or configuration .
|
/// need to be sent to different receivers depending on priority or configuration .
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <see cref="DependencyInjection.DependencyInjectionContext"/>
|
/// <see cref="DependencyInjectionContext"/>
|
||||||
internal sealed class Plugin : IDisposable
|
internal sealed class Plugin : IDisposable
|
||||||
{
|
{
|
||||||
private readonly DalamudPluginInterface _pluginInterface;
|
private readonly DalamudPluginInterface _pluginInterface;
|
||||||
private readonly IServiceProvider _serviceProvider;
|
|
||||||
private readonly ILogger<Plugin> _logger;
|
private readonly ILogger<Plugin> _logger;
|
||||||
private readonly IPalacePalConfiguration _configuration;
|
private readonly CommandManager _commandManager;
|
||||||
private readonly RenderAdapter _renderAdapter;
|
private readonly Chat _chat;
|
||||||
private readonly WindowSystem _windowSystem;
|
private readonly WindowSystem _windowSystem;
|
||||||
|
private readonly ClientState _clientState;
|
||||||
|
|
||||||
|
private readonly IServiceScope _rootScope;
|
||||||
|
private readonly DependencyInjectionLoader _loader;
|
||||||
|
|
||||||
|
private Action? _loginAction = null;
|
||||||
|
|
||||||
public Plugin(
|
public Plugin(
|
||||||
DalamudPluginInterface pluginInterface,
|
DalamudPluginInterface pluginInterface,
|
||||||
IServiceProvider serviceProvider)
|
IServiceProvider serviceProvider,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
_pluginInterface = pluginInterface;
|
_pluginInterface = pluginInterface;
|
||||||
_serviceProvider = serviceProvider;
|
_logger = serviceProvider.GetRequiredService<ILogger<Plugin>>();
|
||||||
_logger = _serviceProvider.GetRequiredService<ILogger<Plugin>>();
|
_commandManager = serviceProvider.GetRequiredService<CommandManager>();
|
||||||
_configuration = serviceProvider.GetRequiredService<IPalacePalConfiguration>();
|
_chat = serviceProvider.GetRequiredService<Chat>();
|
||||||
_renderAdapter = serviceProvider.GetRequiredService<RenderAdapter>();
|
|
||||||
_windowSystem = serviceProvider.GetRequiredService<WindowSystem>();
|
_windowSystem = serviceProvider.GetRequiredService<WindowSystem>();
|
||||||
|
_clientState = serviceProvider.GetRequiredService<ClientState>();
|
||||||
|
|
||||||
LanguageChanged(pluginInterface.UiLanguage);
|
_rootScope = serviceProvider.CreateScope();
|
||||||
|
_loader = _rootScope.ServiceProvider.GetRequiredService<DependencyInjectionLoader>();
|
||||||
|
_loader.InitCompleted += InitCompleted;
|
||||||
|
var _ = Task.Run(async () => await _loader.InitializeAsync(cancellationToken));
|
||||||
|
|
||||||
pluginInterface.UiBuilder.Draw += Draw;
|
pluginInterface.UiBuilder.Draw += Draw;
|
||||||
pluginInterface.UiBuilder.OpenConfigUi += OpenConfigUi;
|
pluginInterface.UiBuilder.OpenConfigUi += OpenConfigUi;
|
||||||
pluginInterface.LanguageChanged += LanguageChanged;
|
pluginInterface.LanguageChanged += LanguageChanged;
|
||||||
|
_clientState.Login += Login;
|
||||||
|
|
||||||
|
_commandManager.AddHandler("/pal", new CommandInfo(OnCommand)
|
||||||
|
{
|
||||||
|
HelpMessage = Localization.Command_pal_HelpText
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitCompleted(Action? loginAction)
|
||||||
|
{
|
||||||
|
LanguageChanged(_pluginInterface.UiLanguage);
|
||||||
|
|
||||||
|
if (_clientState.IsLoggedIn)
|
||||||
|
{
|
||||||
|
loginAction?.Invoke();
|
||||||
|
_loginAction = null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
_loginAction = loginAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Login(object? sender, EventArgs eventArgs)
|
||||||
|
{
|
||||||
|
_loginAction?.Invoke();
|
||||||
|
_loginAction = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnCommand(string command, string arguments)
|
||||||
|
{
|
||||||
|
arguments = arguments.Trim();
|
||||||
|
|
||||||
|
IPalacePalConfiguration configuration =
|
||||||
|
_rootScope.ServiceProvider.GetRequiredService<IPalacePalConfiguration>();
|
||||||
|
if (configuration.FirstUse && arguments != "" && arguments != "config")
|
||||||
|
{
|
||||||
|
_chat.Error(Localization.Error_FirstTimeSetupRequired);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var sp = _rootScope.ServiceProvider;
|
||||||
|
|
||||||
|
switch (arguments)
|
||||||
|
{
|
||||||
|
case "":
|
||||||
|
case "config":
|
||||||
|
sp.GetRequiredService<PalConfigCommand>().Execute();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "stats":
|
||||||
|
sp.GetRequiredService<PalStatsCommand>().Execute();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "tc":
|
||||||
|
case "test-connection":
|
||||||
|
sp.GetRequiredService<PalTestConnectionCommand>().Execute();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "near":
|
||||||
|
case "tnear":
|
||||||
|
case "hnear":
|
||||||
|
sp.GetRequiredService<PalNearCommand>().Execute(arguments);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
_chat.Error(string.Format(Localization.Command_pal_UnknownSubcommand, arguments,
|
||||||
|
command));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
_chat.Error(e.ToString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OpenConfigUi()
|
private void OpenConfigUi()
|
||||||
{
|
=> _rootScope.ServiceProvider.GetRequiredService<PalConfigCommand>().Execute();
|
||||||
Window configWindow;
|
|
||||||
if (_configuration.FirstUse)
|
|
||||||
configWindow = _serviceProvider.GetRequiredService<AgreementWindow>();
|
|
||||||
else
|
|
||||||
configWindow = _serviceProvider.GetRequiredService<ConfigWindow>();
|
|
||||||
|
|
||||||
configWindow.IsOpen = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_pluginInterface.UiBuilder.Draw -= Draw;
|
|
||||||
_pluginInterface.UiBuilder.OpenConfigUi -= OpenConfigUi;
|
|
||||||
_pluginInterface.LanguageChanged -= LanguageChanged;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void LanguageChanged(string languageCode)
|
private void LanguageChanged(string languageCode)
|
||||||
{
|
{
|
||||||
@ -74,8 +149,24 @@ namespace Pal.Client
|
|||||||
|
|
||||||
private void Draw()
|
private void Draw()
|
||||||
{
|
{
|
||||||
_renderAdapter.DrawLayers();
|
if (_loader.LoadState == DependencyInjectionLoader.ELoadState.Loaded)
|
||||||
|
{
|
||||||
|
_rootScope.ServiceProvider.GetRequiredService<RenderAdapter>().DrawLayers();
|
||||||
_windowSystem.Draw();
|
_windowSystem.Draw();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_commandManager.RemoveHandler("/pal");
|
||||||
|
|
||||||
|
_pluginInterface.UiBuilder.Draw -= Draw;
|
||||||
|
_pluginInterface.UiBuilder.OpenConfigUi -= OpenConfigUi;
|
||||||
|
_pluginInterface.LanguageChanged -= LanguageChanged;
|
||||||
|
_clientState.Login -= Login;
|
||||||
|
|
||||||
|
_loader.InitCompleted -= InitCompleted;
|
||||||
|
_rootScope.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
18
Pal.Client/Properties/Localization.Designer.cs
generated
18
Pal.Client/Properties/Localization.Designer.cs
generated
@ -149,15 +149,6 @@ namespace Pal.Client.Properties {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Looks up a localized string similar to Updated all locally cached marker files to latest version..
|
|
||||||
/// </summary>
|
|
||||||
internal static string Command_pal_updatesaves {
|
|
||||||
get {
|
|
||||||
return ResourceManager.GetString("Command_pal_updatesaves", resourceCulture);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to You are NOT in a deep dungeon..
|
/// Looks up a localized string similar to You are NOT in a deep dungeon..
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -664,6 +655,15 @@ namespace Pal.Client.Properties {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Plugin could not be loaded: {0}.
|
||||||
|
/// </summary>
|
||||||
|
internal static string Error_LoadFailed {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Error_LoadFailed", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Please install this plugin from the official repository at {0} to continue using it..
|
/// Looks up a localized string similar to Please install this plugin from the official repository at {0} to continue using it..
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -61,10 +61,6 @@
|
|||||||
<value>Impossible de récupérer les statistiques.</value>
|
<value>Impossible de récupérer les statistiques.</value>
|
||||||
<comment>Shown when /pal stats produces a server-side error, and the statistics window can't be loaded.</comment>
|
<comment>Shown when /pal stats produces a server-side error, and the statistics window can't be loaded.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="Command_pal_updatesaves" xml:space="preserve">
|
|
||||||
<value>Mise à jour de tous les marqueurs du cache local vers la dernière version.</value>
|
|
||||||
<comment>Shown after /pal update-saves was successful.</comment>
|
|
||||||
</data>
|
|
||||||
<!-- Messages while connecting to the server. These are typically only visible when clicking 'Test connection'. -->
|
<!-- Messages while connecting to the server. These are typically only visible when clicking 'Test connection'. -->
|
||||||
<data name="ConnectionSuccessful" xml:space="preserve">
|
<data name="ConnectionSuccessful" xml:space="preserve">
|
||||||
<value>Connexion réussie.</value>
|
<value>Connexion réussie.</value>
|
||||||
|
@ -61,10 +61,6 @@
|
|||||||
<value>統計情報を取得できません。</value>
|
<value>統計情報を取得できません。</value>
|
||||||
<comment>Shown when /pal stats produces a server-side error, and the statistics window can't be loaded.</comment>
|
<comment>Shown when /pal stats produces a server-side error, and the statistics window can't be loaded.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="Command_pal_updatesaves" xml:space="preserve">
|
|
||||||
<value>保存されたマーカーファイルを更新しました。</value>
|
|
||||||
<comment>Shown after /pal update-saves was successful.</comment>
|
|
||||||
</data>
|
|
||||||
<!-- Messages while connecting to the server. These are typically only visible when clicking 'Test connection'. -->
|
<!-- Messages while connecting to the server. These are typically only visible when clicking 'Test connection'. -->
|
||||||
<data name="ConnectionSuccessful" xml:space="preserve">
|
<data name="ConnectionSuccessful" xml:space="preserve">
|
||||||
<value>接続に成功しました。</value>
|
<value>接続に成功しました。</value>
|
||||||
|
@ -46,6 +46,9 @@
|
|||||||
<value>Please finish the initial setup first.</value>
|
<value>Please finish the initial setup first.</value>
|
||||||
<comment>Before using any /pal command, the initial setup/agreeement needs to be completed.</comment>
|
<comment>Before using any /pal command, the initial setup/agreeement needs to be completed.</comment>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Error_LoadFailed" xml:space="preserve">
|
||||||
|
<value>Plugin could not be loaded: {0}</value>
|
||||||
|
</data>
|
||||||
<data name="Error_WrongRepository" xml:space="preserve">
|
<data name="Error_WrongRepository" xml:space="preserve">
|
||||||
<value>Please install this plugin from the official repository at {0} to continue using it.</value>
|
<value>Please install this plugin from the official repository at {0} to continue using it.</value>
|
||||||
</data>
|
</data>
|
||||||
@ -66,10 +69,6 @@
|
|||||||
<value>Unable to fetch statistics.</value>
|
<value>Unable to fetch statistics.</value>
|
||||||
<comment>Shown when /pal stats produces a server-side error, and the statistics window can't be loaded.</comment>
|
<comment>Shown when /pal stats produces a server-side error, and the statistics window can't be loaded.</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="Command_pal_updatesaves" xml:space="preserve">
|
|
||||||
<value>Updated all locally cached marker files to latest version.</value>
|
|
||||||
<comment>Shown after /pal update-saves was successful.</comment>
|
|
||||||
</data>
|
|
||||||
|
|
||||||
<!-- Messages while connecting to the server. These are typically only visible when clicking 'Test connection'. -->
|
<!-- Messages while connecting to the server. These are typically only visible when clicking 'Test connection'. -->
|
||||||
<data name="ConnectionSuccessful" xml:space="preserve">
|
<data name="ConnectionSuccessful" xml:space="preserve">
|
||||||
@ -322,6 +321,5 @@ This is not synchronized with other players and not saved between floors/runs.</
|
|||||||
<data name="Error_ImportFailed_InvalidFile" xml:space="preserve">
|
<data name="Error_ImportFailed_InvalidFile" xml:space="preserve">
|
||||||
<value>Import failed: Invalid file.</value>
|
<value>Import failed: Invalid file.</value>
|
||||||
</data>
|
</data>
|
||||||
|
|
||||||
<!-- Other -->
|
<!-- Other -->
|
||||||
</root>
|
</root>
|
||||||
|
Loading…
Reference in New Issue
Block a user