DI: Split QueueHandler into multiple classes

This commit is contained in:
Liza 2023-02-16 10:25:33 +01:00
parent 29aefee135
commit 7d04cd7575
8 changed files with 352 additions and 269 deletions

View File

@ -69,7 +69,6 @@ namespace Pal.Client.DependencyInjection
services.AddSingleton<FrameworkService>();
services.AddSingleton<ChatService>();
services.AddSingleton<FloorService>();
services.AddSingleton<QueueHandler>();
// windows & related services
services.AddSingleton<AgreementWindow>();
@ -82,6 +81,12 @@ namespace Pal.Client.DependencyInjection
services.AddSingleton<SplatoonRenderer>();
services.AddSingleton<RenderAdapter>();
// queue handling
services.AddTransient<IQueueOnFrameworkThread.Handler<QueuedImport>, QueuedImport.Handler>();
services.AddTransient<IQueueOnFrameworkThread.Handler<QueuedUndoImport>, QueuedUndoImport.Handler>();
services.AddTransient<IQueueOnFrameworkThread.Handler<QueuedConfigUpdate>, QueuedConfigUpdate.Handler>();
services.AddTransient<IQueueOnFrameworkThread.Handler<QueuedSyncResponse>, QueuedSyncResponse.Handler>();
// set up the current UI language before creating anything
Localization.Culture = new CultureInfo(pluginInterface.UiLanguage);

View File

@ -9,7 +9,9 @@ using Dalamud.Game;
using Dalamud.Game.ClientState;
using Dalamud.Game.ClientState.Objects;
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Logging;
using ImGuiNET;
using Microsoft.Extensions.DependencyInjection;
using Pal.Client.Configuration;
using Pal.Client.Extensions;
using Pal.Client.Net;
@ -20,6 +22,7 @@ namespace Pal.Client.DependencyInjection
{
internal sealed class FrameworkService : IDisposable
{
private readonly IServiceProvider _serviceProvider;
private readonly Framework _framework;
private readonly ConfigurationManager _configurationManager;
private readonly IPalacePalConfiguration _configuration;
@ -28,7 +31,6 @@ namespace Pal.Client.DependencyInjection
private readonly FloorService _floorService;
private readonly DebugState _debugState;
private readonly RenderAdapter _renderAdapter;
private readonly QueueHandler _queueHandler;
private readonly ObjectTable _objectTable;
private readonly RemoteApi _remoteApi;
@ -36,7 +38,9 @@ namespace Pal.Client.DependencyInjection
internal Queue<IQueueOnFrameworkThread> LateEventQueue { get; } = new();
internal ConcurrentQueue<nint> NextUpdateObjects { get; } = new();
public FrameworkService(Framework framework,
public FrameworkService(
IServiceProvider serviceProvider,
Framework framework,
ConfigurationManager configurationManager,
IPalacePalConfiguration configuration,
ClientState clientState,
@ -44,10 +48,10 @@ namespace Pal.Client.DependencyInjection
FloorService floorService,
DebugState debugState,
RenderAdapter renderAdapter,
QueueHandler queueHandler,
ObjectTable objectTable,
RemoteApi remoteApi)
{
_serviceProvider = serviceProvider;
_framework = framework;
_configurationManager = configurationManager;
_configuration = configuration;
@ -56,7 +60,6 @@ namespace Pal.Client.DependencyInjection
_floorService = floorService;
_debugState = debugState;
_renderAdapter = renderAdapter;
_queueHandler = queueHandler;
_objectTable = objectTable;
_remoteApi = remoteApi;
@ -84,7 +87,7 @@ namespace Pal.Client.DependencyInjection
bool saveMarkers = false;
while (EarlyEventQueue.TryDequeue(out IQueueOnFrameworkThread? queued))
_queueHandler.Handle(queued, ref recreateLayout, ref saveMarkers);
HandleQueued(queued, ref recreateLayout, ref saveMarkers);
if (_territoryState.LastTerritory != _clientState.TerritoryType)
{
@ -111,12 +114,13 @@ namespace Pal.Client.DependencyInjection
}
while (LateEventQueue.TryDequeue(out IQueueOnFrameworkThread? queued))
_queueHandler.Handle(queued, ref recreateLayout, ref saveMarkers);
HandleQueued(queued, ref recreateLayout, ref saveMarkers);
var currentFloor = _floorService.GetFloorMarkers(_territoryState.LastTerritory);
IList<Marker> visibleMarkers = GetRelevantGameObjects();
HandlePersistentMarkers(currentFloor, visibleMarkers.Where(x => x.IsPermanent()).ToList(), saveMarkers, recreateLayout);
HandlePersistentMarkers(currentFloor, visibleMarkers.Where(x => x.IsPermanent()).ToList(), saveMarkers,
recreateLayout);
HandleEphemeralMarkers(visibleMarkers.Where(x => !x.IsPermanent()).ToList(), recreateLayout);
}
catch (Exception e)
@ -126,7 +130,9 @@ namespace Pal.Client.DependencyInjection
}
#region Render Markers
private void HandlePersistentMarkers(LocalState currentFloor, IList<Marker> visibleMarkers, bool saveMarkers, bool recreateLayout)
private void HandlePersistentMarkers(LocalState currentFloor, IList<Marker> visibleMarkers, bool saveMarkers,
bool recreateLayout)
{
var currentFloorMarkers = currentFloor.Markers;
@ -145,7 +151,8 @@ namespace Pal.Client.DependencyInjection
// This requires you to have seen a trap/hoard marker once per floor to synchronize this for older local states,
// markers discovered afterwards are automatically marked seen.
if (partialAccountId != null && knownMarker is { NetworkId: { }, RemoteSeenRequested: false } && !knownMarker.RemoteSeenOn.Contains(partialAccountId))
if (partialAccountId != null && knownMarker is { NetworkId: { }, RemoteSeenRequested: false } &&
!knownMarker.RemoteSeenOn.Contains(partialAccountId))
updateSeenMarkers = true;
continue;
@ -156,9 +163,10 @@ namespace Pal.Client.DependencyInjection
saveMarkers = true;
}
if (!recreateLayout && currentFloorMarkers.Count > 0 && (_configuration.DeepDungeons.Traps.OnlyVisibleAfterPomander || _configuration.DeepDungeons.HoardCoffers.OnlyVisibleAfterPomander))
if (!recreateLayout && currentFloorMarkers.Count > 0 &&
(_configuration.DeepDungeons.Traps.OnlyVisibleAfterPomander ||
_configuration.DeepDungeons.HoardCoffers.OnlyVisibleAfterPomander))
{
try
{
foreach (var marker in currentFloorMarkers)
@ -183,7 +191,9 @@ namespace Pal.Client.DependencyInjection
if (updateSeenMarkers && partialAccountId != null)
{
var markersToUpdate = currentFloorMarkers.Where(x => x is { Seen: true, NetworkId: { }, RemoteSeenRequested: false } && !x.RemoteSeenOn.Contains(partialAccountId)).ToList();
var markersToUpdate = currentFloorMarkers.Where(x =>
x is { Seen: true, NetworkId: { }, RemoteSeenRequested: false } &&
!x.RemoteSeenOn.Contains(partialAccountId)).ToList();
foreach (var marker in markersToUpdate)
marker.RemoteSeenRequested = true;
Task.Run(async () => await SyncSeenMarkersForTerritory(_territoryState.LastTerritory, markersToUpdate));
@ -195,12 +205,14 @@ namespace Pal.Client.DependencyInjection
if (_territoryState.TerritorySyncState == SyncState.Complete)
{
var markersToUpload = currentFloorMarkers.Where(x => x.IsPermanent() && x.NetworkId == null && !x.UploadRequested).ToList();
var markersToUpload = currentFloorMarkers
.Where(x => x.IsPermanent() && x.NetworkId == null && !x.UploadRequested).ToList();
if (markersToUpload.Count > 0)
{
foreach (var marker in markersToUpload)
marker.UploadRequested = true;
Task.Run(async () => await UploadMarkersForTerritory(_territoryState.LastTerritory, markersToUpload));
Task.Run(async () =>
await UploadMarkersForTerritory(_territoryState.LastTerritory, markersToUpload));
}
}
}
@ -212,15 +224,18 @@ namespace Pal.Client.DependencyInjection
List<IRenderElement> elements = new();
foreach (var marker in currentFloorMarkers)
{
if (marker.Seen || _configuration.Mode == EMode.Online || marker is { WasImported: true, Imports.Count: > 0 })
if (marker.Seen || _configuration.Mode == EMode.Online ||
marker is { WasImported: true, Imports.Count: > 0 })
{
if (marker.Type == Marker.EType.Trap)
{
CreateRenderElement(marker, elements, DetermineColor(marker, visibleMarkers), _configuration.DeepDungeons.Traps);
CreateRenderElement(marker, elements, DetermineColor(marker, visibleMarkers),
_configuration.DeepDungeons.Traps);
}
else if (marker.Type == Marker.EType.Hoard)
{
CreateRenderElement(marker, elements, DetermineColor(marker, visibleMarkers), _configuration.DeepDungeons.HoardCoffers);
CreateRenderElement(marker, elements, DetermineColor(marker, visibleMarkers),
_configuration.DeepDungeons.HoardCoffers);
}
}
}
@ -234,8 +249,10 @@ namespace Pal.Client.DependencyInjection
private void HandleEphemeralMarkers(IList<Marker> visibleMarkers, bool recreateLayout)
{
recreateLayout |= _floorService.EphemeralMarkers.Any(existingMarker => visibleMarkers.All(x => x != existingMarker));
recreateLayout |= visibleMarkers.Any(visibleMarker => _floorService.EphemeralMarkers.All(x => x != visibleMarker));
recreateLayout |=
_floorService.EphemeralMarkers.Any(existingMarker => visibleMarkers.All(x => x != existingMarker));
recreateLayout |=
visibleMarkers.Any(visibleMarker => _floorService.EphemeralMarkers.All(x => x != visibleMarker));
if (recreateLayout)
{
@ -249,7 +266,8 @@ namespace Pal.Client.DependencyInjection
if (marker.Type == Marker.EType.SilverCoffer && _configuration.DeepDungeons.SilverCoffers.Show)
{
CreateRenderElement(marker, elements, DetermineColor(marker, visibleMarkers), _configuration.DeepDungeons.SilverCoffers);
CreateRenderElement(marker, elements, DetermineColor(marker, visibleMarkers),
_configuration.DeepDungeons.SilverCoffers);
}
}
@ -264,9 +282,13 @@ namespace Pal.Client.DependencyInjection
{
switch (marker.Type)
{
case Marker.EType.Trap when _territoryState.PomanderOfSight == PomanderState.Inactive || !_configuration.DeepDungeons.Traps.OnlyVisibleAfterPomander || visibleMarkers.Any(x => x == marker):
case Marker.EType.Trap when _territoryState.PomanderOfSight == PomanderState.Inactive ||
!_configuration.DeepDungeons.Traps.OnlyVisibleAfterPomander ||
visibleMarkers.Any(x => x == marker):
return _configuration.DeepDungeons.Traps.Color;
case Marker.EType.Hoard when _territoryState.PomanderOfIntuition == PomanderState.Inactive || !_configuration.DeepDungeons.HoardCoffers.OnlyVisibleAfterPomander || visibleMarkers.Any(x => x == marker):
case Marker.EType.Hoard when _territoryState.PomanderOfIntuition == PomanderState.Inactive ||
!_configuration.DeepDungeons.HoardCoffers.OnlyVisibleAfterPomander ||
visibleMarkers.Any(x => x == marker):
return _configuration.DeepDungeons.HoardCoffers.Color;
case Marker.EType.SilverCoffer:
return _configuration.DeepDungeons.SilverCoffers.Color;
@ -278,7 +300,8 @@ namespace Pal.Client.DependencyInjection
}
}
private void CreateRenderElement(Marker marker, List<IRenderElement> elements, uint color, MarkerConfiguration config)
private void CreateRenderElement(Marker marker, List<IRenderElement> elements, uint color,
MarkerConfiguration config)
{
if (!config.Show)
return;
@ -287,9 +310,11 @@ namespace Pal.Client.DependencyInjection
marker.RenderElement = element;
elements.Add(element);
}
#endregion
#region Up-/Download
private async Task DownloadMarkersForTerritory(ushort territoryId)
{
try
@ -346,6 +371,7 @@ namespace Pal.Client.DependencyInjection
_debugState.SetFromException(e);
}
}
#endregion
private IList<Marker> GetRelevantGameObjects()
@ -388,5 +414,13 @@ namespace Pal.Client.DependencyInjection
return result;
}
private void HandleQueued(IQueueOnFrameworkThread queued, ref bool recreateLayout, ref bool saveMarkers)
{
Type handlerType = typeof(IQueueOnFrameworkThread.Handler<>).MakeGenericType(queued.GetType());
var handler = (IQueueOnFrameworkThread.IHandler)_serviceProvider.GetRequiredService(handlerType);
handler.RunIfCompatible(queued, ref recreateLayout, ref saveMarkers);
}
}
}

View File

@ -1,6 +1,31 @@
namespace Pal.Client.Scheduled
using Dalamud.Logging;
namespace Pal.Client.Scheduled
{
internal interface IQueueOnFrameworkThread
{
internal interface IHandler
{
void RunIfCompatible(IQueueOnFrameworkThread queued, ref bool recreateLayout, ref bool saveMarkers);
}
internal abstract class Handler<T> : IHandler
where T : IQueueOnFrameworkThread
{
protected abstract void Run(T queued, ref bool recreateLayout, ref bool saveMarkers);
public void RunIfCompatible(IQueueOnFrameworkThread queued, ref bool recreateLayout, ref bool saveMarkers)
{
if (queued is T t)
{
PluginLog.Information($"Handling {queued.GetType()} with handler {GetType()}");
Run(t, ref recreateLayout, ref saveMarkers);
}
else
{
PluginLog.Error($"Could not use queue handler {GetType()} with type {queued.GetType()}");
}
}
}
}
}

View File

@ -1,203 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Dalamud.Game.Gui;
using Dalamud.Logging;
using Pal.Client.Configuration;
using Pal.Client.DependencyInjection;
using Pal.Client.Extensions;
using Pal.Client.Net;
using Pal.Client.Properties;
using Pal.Common;
namespace Pal.Client.Scheduled
{
// TODO The idea was to split this from the queue objects, should be in individual classes tho
internal sealed class QueueHandler
{
private readonly ConfigurationManager _configurationManager;
private readonly IPalacePalConfiguration _configuration;
private readonly FloorService _floorService;
private readonly TerritoryState _territoryState;
private readonly DebugState _debugState;
private readonly ChatGui _chatGui;
public QueueHandler(
ConfigurationManager configurationManager,
IPalacePalConfiguration configuration,
FloorService floorService,
TerritoryState territoryState,
DebugState debugState,
ChatGui chatGui)
{
_configurationManager = configurationManager;
_configuration = configuration;
_floorService = floorService;
_territoryState = territoryState;
_debugState = debugState;
_chatGui = chatGui;
}
public void Handle(IQueueOnFrameworkThread queued, ref bool recreateLayout, ref bool saveMarkers)
{
if (queued is QueuedConfigUpdate)
{
ConfigUpdate(ref recreateLayout, ref saveMarkers);
}
else if (queued is QueuedSyncResponse queuedSyncResponse)
{
SyncResponse(queuedSyncResponse);
recreateLayout = true;
saveMarkers = true;
}
else if (queued is QueuedImport queuedImport)
{
Import(queuedImport);
recreateLayout = true;
saveMarkers = true;
}
else if (queued is QueuedUndoImport queuedUndoImport)
{
UndoImport(queuedUndoImport);
recreateLayout = true;
saveMarkers = true;
}
else
throw new InvalidOperationException();
}
private void ConfigUpdate(ref bool recreateLayout, ref bool saveMarkers)
{
if (_configuration.Mode == EMode.Offline)
{
LocalState.UpdateAll();
_floorService.FloorMarkers.Clear();
_floorService.EphemeralMarkers.Clear();
_territoryState.LastTerritory = 0;
recreateLayout = true;
saveMarkers = true;
}
}
private void SyncResponse(QueuedSyncResponse queued)
{
try
{
var remoteMarkers = queued.Markers;
var currentFloor = _floorService.GetFloorMarkers(queued.TerritoryType);
if (_configuration.Mode == EMode.Online && queued.Success && remoteMarkers.Count > 0)
{
switch (queued.Type)
{
case SyncType.Download:
case SyncType.Upload:
foreach (var remoteMarker in remoteMarkers)
{
// Both uploads and downloads return the network id to be set, but only the downloaded marker is new as in to-be-saved.
Marker? localMarker = currentFloor.Markers.SingleOrDefault(x => x == remoteMarker);
if (localMarker != null)
{
localMarker.NetworkId = remoteMarker.NetworkId;
continue;
}
if (queued.Type == SyncType.Download)
currentFloor.Markers.Add(remoteMarker);
}
break;
case SyncType.MarkSeen:
var partialAccountId =
_configuration.FindAccount(RemoteApi.RemoteUrl)?.AccountId.ToPartialId();
if (partialAccountId == null)
break;
foreach (var remoteMarker in remoteMarkers)
{
Marker? localMarker = currentFloor.Markers.SingleOrDefault(x => x == remoteMarker);
if (localMarker != null)
localMarker.RemoteSeenOn.Add(partialAccountId);
}
break;
}
}
// don't modify state for outdated floors
if (_territoryState.LastTerritory != queued.TerritoryType)
return;
if (queued.Type == SyncType.Download)
{
if (queued.Success)
_territoryState.TerritorySyncState = SyncState.Complete;
else
_territoryState.TerritorySyncState = SyncState.Failed;
}
}
catch (Exception e)
{
_debugState.SetFromException(e);
if (queued.Type == SyncType.Download)
_territoryState.TerritorySyncState = SyncState.Failed;
}
}
private void Import(QueuedImport queued)
{
try
{
if (!queued.Validate(_chatGui))
return;
var oldExportIds = string.IsNullOrEmpty(queued.Export.ServerUrl)
? _configuration.ImportHistory.Where(x => x.RemoteUrl == queued.Export.ServerUrl).Select(x => x.Id)
.Where(x => x != Guid.Empty).ToList()
: new List<Guid>();
foreach (var remoteFloor in queued.Export.Floors)
{
ushort territoryType = (ushort)remoteFloor.TerritoryType;
var localState = _floorService.GetFloorMarkers(territoryType);
localState.UndoImport(oldExportIds);
queued.ImportFloor(remoteFloor, localState);
localState.Save();
}
_configuration.ImportHistory.RemoveAll(hist =>
oldExportIds.Contains(hist.Id) || hist.Id == queued.ExportId);
_configuration.ImportHistory.Add(new ConfigurationV1.ImportHistoryEntry
{
Id = queued.ExportId,
RemoteUrl = queued.Export.ServerUrl,
ExportedAt = queued.Export.CreatedAt.ToDateTime(),
ImportedAt = DateTime.UtcNow,
});
_configurationManager.Save(_configuration);
_chatGui.Print(string.Format(Localization.ImportCompleteStatistics, queued.ImportedTraps,
queued.ImportedHoardCoffers));
}
catch (Exception e)
{
PluginLog.Error(e, "Import failed");
_chatGui.PalError(string.Format(Localization.Error_ImportFailed, e));
}
}
private void UndoImport(QueuedUndoImport queued)
{
foreach (ETerritoryType territoryType in typeof(ETerritoryType).GetEnumValues())
{
var localState = _floorService.GetFloorMarkers((ushort)territoryType);
localState.UndoImport(new List<Guid> { queued.ExportId });
localState.Save();
}
_configuration.ImportHistory.RemoveAll(hist => hist.Id == queued.ExportId);
}
}
}

View File

@ -1,6 +1,37 @@
namespace Pal.Client.Scheduled
using Pal.Client.Configuration;
using Pal.Client.DependencyInjection;
namespace Pal.Client.Scheduled
{
internal sealed class QueuedConfigUpdate : IQueueOnFrameworkThread
{
internal sealed class Handler : IQueueOnFrameworkThread.Handler<QueuedConfigUpdate>
{
private readonly IPalacePalConfiguration _configuration;
private readonly FloorService _floorService;
private readonly TerritoryState _territoryState;
public Handler(IPalacePalConfiguration configuration, FloorService floorService,
TerritoryState territoryState)
{
_configuration = configuration;
_floorService = floorService;
_territoryState = territoryState;
}
protected override void Run(QueuedConfigUpdate queued, ref bool recreateLayout, ref bool saveMarkers)
{
if (_configuration.Mode == EMode.Offline)
{
LocalState.UpdateAll();
_floorService.FloorMarkers.Clear();
_floorService.EphemeralMarkers.Clear();
_territoryState.LastTerritory = 0;
recreateLayout = true;
saveMarkers = true;
}
}
}
}
}

View File

@ -1,20 +1,25 @@
using Account;
using Pal.Common;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Numerics;
using Dalamud.Game.Gui;
using Dalamud.Logging;
using Pal.Client.Configuration;
using Pal.Client.DependencyInjection;
using Pal.Client.Extensions;
using Pal.Client.Properties;
namespace Pal.Client.Scheduled
{
internal sealed class QueuedImport : IQueueOnFrameworkThread
{
public ExportRoot Export { get; }
public Guid ExportId { get; private set; }
public int ImportedTraps { get; private set; }
public int ImportedHoardCoffers { get; private set; }
private ExportRoot Export { get; }
private Guid ExportId { get; set; }
private int ImportedTraps { get; set; }
private int ImportedHoardCoffers { get; set; }
public QueuedImport(string sourcePath)
{
@ -22,50 +27,116 @@ namespace Pal.Client.Scheduled
Export = ExportRoot.Parser.ParseFrom(input);
}
public bool Validate(ChatGui chatGui)
internal sealed class Handler : IQueueOnFrameworkThread.Handler<QueuedImport>
{
if (Export.ExportVersion != ExportConfig.ExportVersion)
private readonly ChatGui _chatGui;
private readonly IPalacePalConfiguration _configuration;
private readonly ConfigurationManager _configurationManager;
private readonly FloorService _floorService;
public Handler(ChatGui chatGui, IPalacePalConfiguration configuration,
ConfigurationManager configurationManager, FloorService floorService)
{
chatGui.PrintError(Localization.Error_ImportFailed_IncompatibleVersion);
return false;
_chatGui = chatGui;
_configuration = configuration;
_configurationManager = configurationManager;
_floorService = floorService;
}
if (!Guid.TryParse(Export.ExportId, out Guid exportId) || ExportId == Guid.Empty)
protected override void Run(QueuedImport import, ref bool recreateLayout, ref bool saveMarkers)
{
chatGui.PrintError(Localization.Error_ImportFailed_InvalidFile);
return false;
}
recreateLayout = true;
saveMarkers = true;
ExportId = exportId;
if (string.IsNullOrEmpty(Export.ServerUrl))
{
// If we allow for backups as import/export, this should be removed
chatGui.PrintError(Localization.Error_ImportFailed_InvalidFile);
return false;
}
return true;
}
public void ImportFloor(ExportFloor remoteFloor, LocalState localState)
{
var remoteMarkers = remoteFloor.Objects.Select(m => new Marker((Marker.EType)m.Type, new Vector3(m.X, m.Y, m.Z)) { WasImported = true });
foreach (var remoteMarker in remoteMarkers)
{
Marker? localMarker = localState.Markers.SingleOrDefault(x => x == remoteMarker);
if (localMarker == null)
try
{
localState.Markers.Add(remoteMarker);
localMarker = remoteMarker;
if (!Validate(import))
return;
if (localMarker.Type == Marker.EType.Trap)
ImportedTraps++;
else if (localMarker.Type == Marker.EType.Hoard)
ImportedHoardCoffers++;
var oldExportIds = string.IsNullOrEmpty(import.Export.ServerUrl)
? _configuration.ImportHistory.Where(x => x.RemoteUrl == import.Export.ServerUrl)
.Select(x => x.Id)
.Where(x => x != Guid.Empty).ToList()
: new List<Guid>();
foreach (var remoteFloor in import.Export.Floors)
{
ushort territoryType = (ushort)remoteFloor.TerritoryType;
var localState = _floorService.GetFloorMarkers(territoryType);
localState.UndoImport(oldExportIds);
ImportFloor(import, remoteFloor, localState);
localState.Save();
}
_configuration.ImportHistory.RemoveAll(hist =>
oldExportIds.Contains(hist.Id) || hist.Id == import.ExportId);
_configuration.ImportHistory.Add(new ConfigurationV1.ImportHistoryEntry
{
Id = import.ExportId,
RemoteUrl = import.Export.ServerUrl,
ExportedAt = import.Export.CreatedAt.ToDateTime(),
ImportedAt = DateTime.UtcNow,
});
_configurationManager.Save(_configuration);
_chatGui.Print(string.Format(Localization.ImportCompleteStatistics, import.ImportedTraps,
import.ImportedHoardCoffers));
}
catch (Exception e)
{
PluginLog.Error(e, "Import failed");
_chatGui.PalError(string.Format(Localization.Error_ImportFailed, e));
}
}
private bool Validate(QueuedImport import)
{
if (import.Export.ExportVersion != ExportConfig.ExportVersion)
{
_chatGui.PrintError(Localization.Error_ImportFailed_IncompatibleVersion);
return false;
}
remoteMarker.Imports.Add(ExportId);
if (!Guid.TryParse(import.Export.ExportId, out Guid exportId) || import.ExportId == Guid.Empty)
{
_chatGui.PrintError(Localization.Error_ImportFailed_InvalidFile);
return false;
}
import.ExportId = exportId;
if (string.IsNullOrEmpty(import.Export.ServerUrl))
{
// If we allow for backups as import/export, this should be removed
_chatGui.PrintError(Localization.Error_ImportFailed_InvalidFile);
return false;
}
return true;
}
private void ImportFloor(QueuedImport import, ExportFloor remoteFloor, LocalState localState)
{
var remoteMarkers = remoteFloor.Objects.Select(m =>
new Marker((Marker.EType)m.Type, new Vector3(m.X, m.Y, m.Z)) { WasImported = true });
foreach (var remoteMarker in remoteMarkers)
{
Marker? localMarker = localState.Markers.SingleOrDefault(x => x == remoteMarker);
if (localMarker == null)
{
localState.Markers.Add(remoteMarker);
localMarker = remoteMarker;
if (localMarker.Type == Marker.EType.Trap)
import.ImportedTraps++;
else if (localMarker.Type == Marker.EType.Hoard)
import.ImportedHoardCoffers++;
}
remoteMarker.Imports.Add(import.ExportId);
}
}
}
}

View File

@ -1,4 +1,10 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using Pal.Client.Configuration;
using Pal.Client.DependencyInjection;
using Pal.Client.Extensions;
using Pal.Client.Net;
namespace Pal.Client.Scheduled
{
@ -8,6 +14,89 @@ namespace Pal.Client.Scheduled
public required ushort TerritoryType { get; init; }
public required bool Success { get; init; }
public required List<Marker> Markers { get; init; }
internal sealed class Handler : IQueueOnFrameworkThread.Handler<QueuedSyncResponse>
{
private readonly IPalacePalConfiguration _configuration;
private readonly FloorService _floorService;
private readonly TerritoryState _territoryState;
private readonly DebugState _debugState;
public Handler(IPalacePalConfiguration configuration, FloorService floorService, TerritoryState territoryState, DebugState debugState)
{
_configuration = configuration;
_floorService = floorService;
_territoryState = territoryState;
_debugState = debugState;
}
protected override void Run(QueuedSyncResponse queued, ref bool recreateLayout, ref bool saveMarkers)
{
recreateLayout = true;
saveMarkers = true;
try
{
var remoteMarkers = queued.Markers;
var currentFloor = _floorService.GetFloorMarkers(queued.TerritoryType);
if (_configuration.Mode == EMode.Online && queued.Success && remoteMarkers.Count > 0)
{
switch (queued.Type)
{
case SyncType.Download:
case SyncType.Upload:
foreach (var remoteMarker in remoteMarkers)
{
// Both uploads and downloads return the network id to be set, but only the downloaded marker is new as in to-be-saved.
Marker? localMarker = currentFloor.Markers.SingleOrDefault(x => x == remoteMarker);
if (localMarker != null)
{
localMarker.NetworkId = remoteMarker.NetworkId;
continue;
}
if (queued.Type == SyncType.Download)
currentFloor.Markers.Add(remoteMarker);
}
break;
case SyncType.MarkSeen:
var partialAccountId =
_configuration.FindAccount(RemoteApi.RemoteUrl)?.AccountId.ToPartialId();
if (partialAccountId == null)
break;
foreach (var remoteMarker in remoteMarkers)
{
Marker? localMarker = currentFloor.Markers.SingleOrDefault(x => x == remoteMarker);
if (localMarker != null)
localMarker.RemoteSeenOn.Add(partialAccountId);
}
break;
}
}
// don't modify state for outdated floors
if (_territoryState.LastTerritory != queued.TerritoryType)
return;
if (queued.Type == SyncType.Download)
{
if (queued.Success)
_territoryState.TerritorySyncState = SyncState.Complete;
else
_territoryState.TerritorySyncState = SyncState.Failed;
}
}
catch (Exception e)
{
_debugState.SetFromException(e);
if (queued.Type == SyncType.Download)
_territoryState.TerritorySyncState = SyncState.Failed;
}
}
}
}
public enum SyncState

View File

@ -1,4 +1,8 @@
using System;
using System.Collections.Generic;
using Pal.Client.Configuration;
using Pal.Client.DependencyInjection;
using Pal.Common;
namespace Pal.Client.Scheduled
{
@ -9,6 +13,33 @@ namespace Pal.Client.Scheduled
ExportId = exportId;
}
public Guid ExportId { get; }
private Guid ExportId { get; }
internal sealed class Handler : IQueueOnFrameworkThread.Handler<QueuedUndoImport>
{
private readonly IPalacePalConfiguration _configuration;
private readonly FloorService _floorService;
public Handler(IPalacePalConfiguration configuration, FloorService floorService)
{
_configuration = configuration;
_floorService = floorService;
}
protected override void Run(QueuedUndoImport queued, ref bool recreateLayout, ref bool saveMarkers)
{
recreateLayout = true;
saveMarkers = true;
foreach (ETerritoryType territoryType in typeof(ETerritoryType).GetEnumValues())
{
var localState = _floorService.GetFloorMarkers((ushort)territoryType);
localState.UndoImport(new List<Guid> { queued.ExportId });
localState.Save();
}
_configuration.ImportHistory.RemoveAll(hist => hist.Id == queued.ExportId);
}
}
}
}