⚗️ Import/Export: Import, event rework
This commit is contained in:
parent
bb721bc37f
commit
4c22334761
@ -1,5 +1,6 @@
|
|||||||
using Dalamud.Configuration;
|
using Dalamud.Configuration;
|
||||||
using Dalamud.Logging;
|
using Dalamud.Logging;
|
||||||
|
using Pal.Client.Scheduled;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@ -25,6 +26,8 @@ namespace Pal.Client
|
|||||||
public Dictionary<string, Guid> AccountIds { private get; set; } = new();
|
public Dictionary<string, Guid> AccountIds { private get; set; } = new();
|
||||||
public Dictionary<string, AccountInfo> Accounts { get; set; } = new();
|
public Dictionary<string, AccountInfo> Accounts { get; set; } = new();
|
||||||
|
|
||||||
|
public List<ImportHistoryEntry> ImportHistory { get; set; } = new();
|
||||||
|
|
||||||
public bool ShowTraps { get; set; } = true;
|
public bool ShowTraps { get; set; } = true;
|
||||||
public Vector4 TrapColor { get; set; } = new Vector4(1, 0, 0, 0.4f);
|
public Vector4 TrapColor { get; set; } = new Vector4(1, 0, 0, 0.4f);
|
||||||
public bool OnlyVisibleTrapsAfterPomander { get; set; } = true;
|
public bool OnlyVisibleTrapsAfterPomander { get; set; } = true;
|
||||||
@ -36,10 +39,12 @@ namespace Pal.Client
|
|||||||
public bool ShowSilverCoffers { get; set; } = false;
|
public bool ShowSilverCoffers { get; set; } = false;
|
||||||
public Vector4 SilverCofferColor { get; set; } = new Vector4(1, 1, 1, 0.4f);
|
public Vector4 SilverCofferColor { get; set; } = new Vector4(1, 1, 1, 0.4f);
|
||||||
public bool FillSilverCoffers { get; set; } = true;
|
public bool FillSilverCoffers { get; set; } = true;
|
||||||
#endregion
|
|
||||||
|
|
||||||
public delegate void OnSaved();
|
/// <summary>
|
||||||
public event OnSaved? Saved;
|
/// Needs to be manually set.
|
||||||
|
/// </summary>
|
||||||
|
public string BetaKey { get; set; } = "";
|
||||||
|
#endregion
|
||||||
|
|
||||||
#pragma warning disable CS0612 // Type or member is obsolete
|
#pragma warning disable CS0612 // Type or member is obsolete
|
||||||
public void Migrate()
|
public void Migrate()
|
||||||
@ -75,7 +80,7 @@ namespace Pal.Client
|
|||||||
public void Save()
|
public void Save()
|
||||||
{
|
{
|
||||||
Service.PluginInterface.SavePluginConfig(this);
|
Service.PluginInterface.SavePluginConfig(this);
|
||||||
Saved?.Invoke();
|
Service.Plugin.EarlyEventQueue.Enqueue(new QueuedConfigUpdate());
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum EMode
|
public enum EMode
|
||||||
@ -105,5 +110,17 @@ namespace Pal.Client
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public List<string> CachedRoles { get; set; } = new List<string>();
|
public List<string> CachedRoles { get; set; } = new List<string>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class ImportHistoryEntry
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public string? RemoteUrl { get; set; }
|
||||||
|
public DateTime ExportedAt { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set when the file is imported locally.
|
||||||
|
/// </summary>
|
||||||
|
public DateTime ImportedAt { get; set; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,11 @@ namespace Pal.Client
|
|||||||
private void ApplyFilters()
|
private void ApplyFilters()
|
||||||
{
|
{
|
||||||
if (Service.Configuration.Mode == Configuration.EMode.Offline)
|
if (Service.Configuration.Mode == Configuration.EMode.Offline)
|
||||||
Markers = new ConcurrentBag<Marker>(Markers.Where(x => x.Seen));
|
Markers = new ConcurrentBag<Marker>(Markers.Where(x => x.Seen || (x.WasImported && x.Imports.Count > 0)));
|
||||||
|
else
|
||||||
|
// ensure old import markers are removed if they are no longer part of a "current" import
|
||||||
|
// this MAY remove markers the server sent you (and that you haven't seen), but this should be fixed the next time you enter the zone
|
||||||
|
Markers = new ConcurrentBag<Marker>(Markers.Where(x => x.Seen || !x.WasImported || x.Imports.Count > 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LocalState? Load(uint territoryType)
|
public static LocalState? Load(uint territoryType)
|
||||||
|
@ -41,6 +41,15 @@ namespace Pal.Client
|
|||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public bool RemoteSeenRequested { get; set; } = false;
|
public bool RemoteSeenRequested { get; set; } = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// To keep track of which markers were imported through a downloaded file, we save the associated import-id.
|
||||||
|
///
|
||||||
|
/// Importing another file for the same remote server will remove the old import-id, and add the new import-id here.
|
||||||
|
/// </summary>
|
||||||
|
public List<Guid> Imports { get; set; } = new List<Guid>();
|
||||||
|
|
||||||
|
public bool WasImported { get; set; }
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public Element? SplatoonElement { get; set; }
|
public Element? SplatoonElement { get; set; }
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ using Grpc.Core;
|
|||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
using Lumina.Excel.GeneratedSheets;
|
using Lumina.Excel.GeneratedSheets;
|
||||||
using Pal.Client.Net;
|
using Pal.Client.Net;
|
||||||
|
using Pal.Client.Scheduled;
|
||||||
using Pal.Client.Windows;
|
using Pal.Client.Windows;
|
||||||
using Pal.Common;
|
using Pal.Common;
|
||||||
using System;
|
using System;
|
||||||
@ -35,23 +36,23 @@ namespace Pal.Client
|
|||||||
private const string SPLATOON_TRAP_HOARD = "PalacePal.TrapHoard";
|
private const string SPLATOON_TRAP_HOARD = "PalacePal.TrapHoard";
|
||||||
private const string SPLATOON_REGULAR_COFFERS = "PalacePal.RegularCoffers";
|
private const string SPLATOON_REGULAR_COFFERS = "PalacePal.RegularCoffers";
|
||||||
|
|
||||||
private readonly ConcurrentQueue<Sync> _pendingSyncResponses = new();
|
|
||||||
private readonly static Dictionary<Marker.EType, MarkerConfig> _markerConfig = new Dictionary<Marker.EType, MarkerConfig>
|
private readonly static Dictionary<Marker.EType, MarkerConfig> _markerConfig = new Dictionary<Marker.EType, MarkerConfig>
|
||||||
{
|
{
|
||||||
{ Marker.EType.Trap, new MarkerConfig { Radius = 1.7f } },
|
{ Marker.EType.Trap, new MarkerConfig { Radius = 1.7f } },
|
||||||
{ Marker.EType.Hoard, new MarkerConfig { Radius = 1.7f, OffsetY = -0.03f } },
|
{ Marker.EType.Hoard, new MarkerConfig { Radius = 1.7f, OffsetY = -0.03f } },
|
||||||
{ Marker.EType.SilverCoffer, new MarkerConfig { Radius = 1f } },
|
{ Marker.EType.SilverCoffer, new MarkerConfig { Radius = 1f } },
|
||||||
};
|
};
|
||||||
private bool _configUpdated = false;
|
|
||||||
private LocalizedChatMessages _localizedChatMessages = new();
|
private LocalizedChatMessages _localizedChatMessages = new();
|
||||||
|
|
||||||
internal ConcurrentDictionary<ushort, LocalState> FloorMarkers { get; } = new();
|
internal ConcurrentDictionary<ushort, LocalState> FloorMarkers { get; } = new();
|
||||||
internal ConcurrentBag<Marker> EphemeralMarkers { get; set; } = new();
|
internal ConcurrentBag<Marker> EphemeralMarkers { get; set; } = new();
|
||||||
internal ushort LastTerritory { get; private set; }
|
internal ushort LastTerritory { get; set; }
|
||||||
public SyncState TerritorySyncState { get; set; }
|
public SyncState TerritorySyncState { get; set; }
|
||||||
public PomanderState PomanderOfSight { get; set; } = PomanderState.Inactive;
|
public PomanderState PomanderOfSight { get; set; } = PomanderState.Inactive;
|
||||||
public PomanderState PomanderOfIntuition { get; set; } = PomanderState.Inactive;
|
public PomanderState PomanderOfIntuition { get; set; } = PomanderState.Inactive;
|
||||||
public string? DebugMessage { get; set; }
|
public string? DebugMessage { get; set; }
|
||||||
|
internal Queue<IQueueOnFrameworkThread> EarlyEventQueue { get; } = new();
|
||||||
|
internal Queue<IQueueOnFrameworkThread> LateEventQueue { get; } = new();
|
||||||
|
|
||||||
public string Name => "Palace Pal";
|
public string Name => "Palace Pal";
|
||||||
|
|
||||||
@ -101,7 +102,6 @@ namespace Pal.Client
|
|||||||
pluginInterface.UiBuilder.Draw += Service.WindowSystem.Draw;
|
pluginInterface.UiBuilder.Draw += Service.WindowSystem.Draw;
|
||||||
pluginInterface.UiBuilder.OpenConfigUi += OnOpenConfigUi;
|
pluginInterface.UiBuilder.OpenConfigUi += OnOpenConfigUi;
|
||||||
Service.Framework.Update += OnFrameworkUpdate;
|
Service.Framework.Update += OnFrameworkUpdate;
|
||||||
Service.Configuration.Saved += OnConfigSaved;
|
|
||||||
Service.Chat.ChatMessage += OnChatMessage;
|
Service.Chat.ChatMessage += OnChatMessage;
|
||||||
Service.CommandManager.AddHandler("/pal", new CommandInfo(OnCommand)
|
Service.CommandManager.AddHandler("/pal", new CommandInfo(OnCommand)
|
||||||
{
|
{
|
||||||
@ -182,7 +182,6 @@ namespace Pal.Client
|
|||||||
Service.PluginInterface.UiBuilder.Draw -= Service.WindowSystem.Draw;
|
Service.PluginInterface.UiBuilder.Draw -= Service.WindowSystem.Draw;
|
||||||
Service.PluginInterface.UiBuilder.OpenConfigUi -= OnOpenConfigUi;
|
Service.PluginInterface.UiBuilder.OpenConfigUi -= OnOpenConfigUi;
|
||||||
Service.Framework.Update -= OnFrameworkUpdate;
|
Service.Framework.Update -= OnFrameworkUpdate;
|
||||||
Service.Configuration.Saved -= OnConfigSaved;
|
|
||||||
Service.Chat.ChatMessage -= OnChatMessage;
|
Service.Chat.ChatMessage -= OnChatMessage;
|
||||||
|
|
||||||
Service.WindowSystem.RemoveAllWindows();
|
Service.WindowSystem.RemoveAllWindows();
|
||||||
@ -208,11 +207,6 @@ namespace Pal.Client
|
|||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
private void OnConfigSaved()
|
|
||||||
{
|
|
||||||
_configUpdated = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnChatMessage(XivChatType type, uint senderId, ref SeString sender, ref SeString seMessage, ref bool isHandled)
|
private void OnChatMessage(XivChatType type, uint senderId, ref SeString sender, ref SeString seMessage, ref bool isHandled)
|
||||||
{
|
{
|
||||||
if (Service.Configuration.FirstUse)
|
if (Service.Configuration.FirstUse)
|
||||||
@ -259,27 +253,18 @@ namespace Pal.Client
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
bool recreateLayout = false;
|
bool recreateLayout = false;
|
||||||
if (_configUpdated)
|
|
||||||
{
|
|
||||||
if (Service.Configuration.Mode == Configuration.EMode.Offline)
|
|
||||||
{
|
|
||||||
LocalState.UpdateAll();
|
|
||||||
FloorMarkers.Clear();
|
|
||||||
EphemeralMarkers.Clear();
|
|
||||||
LastTerritory = 0;
|
|
||||||
}
|
|
||||||
_configUpdated = false;
|
|
||||||
recreateLayout = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool saveMarkers = false;
|
bool saveMarkers = false;
|
||||||
|
|
||||||
|
while (EarlyEventQueue.TryDequeue(out IQueueOnFrameworkThread? queued))
|
||||||
|
queued?.Run(this, ref recreateLayout, ref saveMarkers);
|
||||||
|
|
||||||
if (LastTerritory != Service.ClientState.TerritoryType)
|
if (LastTerritory != Service.ClientState.TerritoryType)
|
||||||
{
|
{
|
||||||
LastTerritory = Service.ClientState.TerritoryType;
|
LastTerritory = Service.ClientState.TerritoryType;
|
||||||
TerritorySyncState = SyncState.NotAttempted;
|
TerritorySyncState = SyncState.NotAttempted;
|
||||||
|
|
||||||
if (IsInDeepDungeon())
|
if (IsInDeepDungeon())
|
||||||
FloorMarkers[LastTerritory] = LocalState.Load(LastTerritory) ?? new LocalState(LastTerritory);
|
GetFloorMarkers(LastTerritory);
|
||||||
EphemeralMarkers.Clear();
|
EphemeralMarkers.Clear();
|
||||||
PomanderOfSight = PomanderState.Inactive;
|
PomanderOfSight = PomanderState.Inactive;
|
||||||
PomanderOfIntuition = PomanderState.Inactive;
|
PomanderOfIntuition = PomanderState.Inactive;
|
||||||
@ -296,15 +281,10 @@ namespace Pal.Client
|
|||||||
Task.Run(async () => await DownloadMarkersForTerritory(LastTerritory));
|
Task.Run(async () => await DownloadMarkersForTerritory(LastTerritory));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_pendingSyncResponses.Count > 0)
|
while (LateEventQueue.TryDequeue(out IQueueOnFrameworkThread? queued))
|
||||||
{
|
queued?.Run(this, ref recreateLayout, ref saveMarkers);
|
||||||
HandleSyncResponses();
|
|
||||||
recreateLayout = true;
|
|
||||||
saveMarkers = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!FloorMarkers.TryGetValue(LastTerritory, out var currentFloor))
|
var currentFloor = GetFloorMarkers(LastTerritory);
|
||||||
FloorMarkers[LastTerritory] = currentFloor = new LocalState(LastTerritory);
|
|
||||||
|
|
||||||
IList<Marker> visibleMarkers = GetRelevantGameObjects();
|
IList<Marker> visibleMarkers = GetRelevantGameObjects();
|
||||||
HandlePersistentMarkers(currentFloor, visibleMarkers.Where(x => x.IsPermanent()).ToList(), saveMarkers, recreateLayout);
|
HandlePersistentMarkers(currentFloor, visibleMarkers.Where(x => x.IsPermanent()).ToList(), saveMarkers, recreateLayout);
|
||||||
@ -316,6 +296,11 @@ namespace Pal.Client
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal LocalState GetFloorMarkers(ushort territoryType)
|
||||||
|
{
|
||||||
|
return FloorMarkers.GetOrAdd(territoryType, tt => LocalState.Load(tt) ?? new LocalState(tt));
|
||||||
|
}
|
||||||
|
|
||||||
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 config = Service.Configuration;
|
var config = Service.Configuration;
|
||||||
@ -403,7 +388,7 @@ namespace Pal.Client
|
|||||||
List<Element> elements = new List<Element>();
|
List<Element> elements = new List<Element>();
|
||||||
foreach (var marker in currentFloorMarkers)
|
foreach (var marker in currentFloorMarkers)
|
||||||
{
|
{
|
||||||
if (marker.Seen || config.Mode == Configuration.EMode.Online)
|
if (marker.Seen || config.Mode == Configuration.EMode.Online || (marker.WasImported && marker.Imports.Count > 0))
|
||||||
{
|
{
|
||||||
if (marker.Type == Marker.EType.Trap && config.ShowTraps)
|
if (marker.Type == Marker.EType.Trap && config.ShowTraps)
|
||||||
{
|
{
|
||||||
@ -503,7 +488,7 @@ namespace Pal.Client
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var (success, downloadedMarkers) = await Service.RemoteApi.DownloadRemoteMarkers(territoryId);
|
var (success, downloadedMarkers) = await Service.RemoteApi.DownloadRemoteMarkers(territoryId);
|
||||||
_pendingSyncResponses.Enqueue(new Sync
|
LateEventQueue.Enqueue(new QueuedSyncResponse
|
||||||
{
|
{
|
||||||
Type = SyncType.Download,
|
Type = SyncType.Download,
|
||||||
TerritoryType = territoryId,
|
TerritoryType = territoryId,
|
||||||
@ -522,7 +507,7 @@ namespace Pal.Client
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var (success, uploadedMarkers) = await Service.RemoteApi.UploadMarker(territoryId, markersToUpload);
|
var (success, uploadedMarkers) = await Service.RemoteApi.UploadMarker(territoryId, markersToUpload);
|
||||||
_pendingSyncResponses.Enqueue(new Sync
|
LateEventQueue.Enqueue(new QueuedSyncResponse
|
||||||
{
|
{
|
||||||
Type = SyncType.Upload,
|
Type = SyncType.Upload,
|
||||||
TerritoryType = territoryId,
|
TerritoryType = territoryId,
|
||||||
@ -541,7 +526,7 @@ namespace Pal.Client
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var success = await Service.RemoteApi.MarkAsSeen(territoryId, markersToUpdate);
|
var success = await Service.RemoteApi.MarkAsSeen(territoryId, markersToUpdate);
|
||||||
_pendingSyncResponses.Enqueue(new Sync
|
LateEventQueue.Enqueue(new QueuedSyncResponse
|
||||||
{
|
{
|
||||||
Type = SyncType.MarkSeen,
|
Type = SyncType.MarkSeen,
|
||||||
TerritoryType = territoryId,
|
TerritoryType = territoryId,
|
||||||
@ -587,70 +572,6 @@ namespace Pal.Client
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleSyncResponses()
|
|
||||||
{
|
|
||||||
while (_pendingSyncResponses.TryDequeue(out Sync? sync) && sync != null)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var territoryId = sync.TerritoryType;
|
|
||||||
var remoteMarkers = sync.Markers;
|
|
||||||
if (Service.Configuration.Mode == Configuration.EMode.Online && sync.Success && FloorMarkers.TryGetValue(territoryId, out var currentFloor) && remoteMarkers.Count > 0)
|
|
||||||
{
|
|
||||||
switch (sync.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 (sync.Type == SyncType.Download)
|
|
||||||
currentFloor.Markers.Add(remoteMarker);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SyncType.MarkSeen:
|
|
||||||
var accountId = Service.RemoteApi.AccountId;
|
|
||||||
if (accountId == null)
|
|
||||||
break;
|
|
||||||
foreach (var remoteMarker in remoteMarkers)
|
|
||||||
{
|
|
||||||
Marker? localMarker = currentFloor.Markers.SingleOrDefault(x => x == remoteMarker);
|
|
||||||
if (localMarker != null)
|
|
||||||
localMarker.RemoteSeenOn.Add(accountId.Value);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// don't modify state for outdated floors
|
|
||||||
if (LastTerritory != territoryId)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (sync.Type == SyncType.Download)
|
|
||||||
{
|
|
||||||
if (sync.Success)
|
|
||||||
TerritorySyncState = SyncState.Complete;
|
|
||||||
else
|
|
||||||
TerritorySyncState = SyncState.Failed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
DebugMessage = $"{DateTime.Now}\n{e}";
|
|
||||||
if (sync.Type == SyncType.Download)
|
|
||||||
TerritorySyncState = SyncState.Failed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private IList<Marker> GetRelevantGameObjects()
|
private IList<Marker> GetRelevantGameObjects()
|
||||||
{
|
{
|
||||||
List<Marker> result = new();
|
List<Marker> result = new();
|
||||||
@ -729,30 +650,6 @@ namespace Pal.Client
|
|||||||
return Service.DataManager.GetExcelSheet<LogMessage>()?.GetRow(id)?.Text?.ToString() ?? "Unknown";
|
return Service.DataManager.GetExcelSheet<LogMessage>()?.GetRow(id)?.Text?.ToString() ?? "Unknown";
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class Sync
|
|
||||||
{
|
|
||||||
public SyncType Type { get; set; }
|
|
||||||
public ushort TerritoryType { get; set; }
|
|
||||||
public bool Success { get; set; }
|
|
||||||
public List<Marker> Markers { get; set; } = new();
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum SyncState
|
|
||||||
{
|
|
||||||
NotAttempted,
|
|
||||||
NotNeeded,
|
|
||||||
Started,
|
|
||||||
Complete,
|
|
||||||
Failed,
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum SyncType
|
|
||||||
{
|
|
||||||
Upload,
|
|
||||||
Download,
|
|
||||||
MarkSeen,
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum PomanderState
|
public enum PomanderState
|
||||||
{
|
{
|
||||||
Inactive,
|
Inactive,
|
||||||
|
13
Pal.Client/Scheduled/IQueueOnFrameworkThread.cs
Normal file
13
Pal.Client/Scheduled/IQueueOnFrameworkThread.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Pal.Client.Scheduled
|
||||||
|
{
|
||||||
|
internal interface IQueueOnFrameworkThread
|
||||||
|
{
|
||||||
|
void Run(Plugin plugin, ref bool recreateLayout, ref bool saveMarkers);
|
||||||
|
}
|
||||||
|
}
|
19
Pal.Client/Scheduled/QueuedConfigUpdate.cs
Normal file
19
Pal.Client/Scheduled/QueuedConfigUpdate.cs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
namespace Pal.Client.Scheduled
|
||||||
|
{
|
||||||
|
internal class QueuedConfigUpdate : IQueueOnFrameworkThread
|
||||||
|
{
|
||||||
|
public void Run(Plugin plugin, ref bool recreateLayout, ref bool saveMarkers)
|
||||||
|
{
|
||||||
|
if (Service.Configuration.Mode == Configuration.EMode.Offline)
|
||||||
|
{
|
||||||
|
LocalState.UpdateAll();
|
||||||
|
plugin.FloorMarkers.Clear();
|
||||||
|
plugin.EphemeralMarkers.Clear();
|
||||||
|
plugin.LastTerritory = 0;
|
||||||
|
|
||||||
|
recreateLayout = true;
|
||||||
|
saveMarkers = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
121
Pal.Client/Scheduled/QueuedImport.cs
Normal file
121
Pal.Client/Scheduled/QueuedImport.cs
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
using Account;
|
||||||
|
using Dalamud.Logging;
|
||||||
|
using Pal.Common;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Numerics;
|
||||||
|
|
||||||
|
namespace Pal.Client.Scheduled
|
||||||
|
{
|
||||||
|
internal class QueuedImport : IQueueOnFrameworkThread
|
||||||
|
{
|
||||||
|
private readonly ExportRoot _export;
|
||||||
|
private Guid _exportId;
|
||||||
|
private int importedTraps;
|
||||||
|
private int importedHoardCoffers;
|
||||||
|
|
||||||
|
public QueuedImport(string sourcePath)
|
||||||
|
{
|
||||||
|
using (var input = File.OpenRead(sourcePath))
|
||||||
|
_export = ExportRoot.Parser.ParseFrom(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Run(Plugin plugin, ref bool recreateLayout, ref bool saveMarkers)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!Validate())
|
||||||
|
return;
|
||||||
|
|
||||||
|
var config = Service.Configuration;
|
||||||
|
var oldExportIds = string.IsNullOrEmpty(_export.ServerUrl) ? config.ImportHistory.Where(x => x.RemoteUrl == _export.ServerUrl).Select(x => x.Id).Where(x => x != Guid.Empty).ToList() : new List<Guid>();
|
||||||
|
|
||||||
|
foreach (var remoteFloor in _export.Floors)
|
||||||
|
{
|
||||||
|
ushort territoryType = (ushort)remoteFloor.TerritoryType;
|
||||||
|
var localState = plugin.GetFloorMarkers(territoryType);
|
||||||
|
|
||||||
|
CleanupFloor(localState, oldExportIds);
|
||||||
|
ImportFloor(remoteFloor, localState);
|
||||||
|
|
||||||
|
localState.Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
config.ImportHistory.RemoveAll(hist => oldExportIds.Contains(hist.Id) || hist.Id == _exportId);
|
||||||
|
config.ImportHistory.Add(new Configuration.ImportHistoryEntry
|
||||||
|
{
|
||||||
|
Id = _exportId,
|
||||||
|
RemoteUrl = _export.ServerUrl,
|
||||||
|
ExportedAt = _export.CreatedAt.ToDateTime(),
|
||||||
|
ImportedAt = DateTime.UtcNow,
|
||||||
|
});
|
||||||
|
config.Save();
|
||||||
|
|
||||||
|
recreateLayout = true;
|
||||||
|
saveMarkers = true;
|
||||||
|
|
||||||
|
Service.Chat.Print($"Imported {importedTraps} new trap locations and {importedHoardCoffers} new hoard coffer locations.");
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
PluginLog.Error(e, "Import failed");
|
||||||
|
Service.Chat.PrintError($"Import failed: {e}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool Validate()
|
||||||
|
{
|
||||||
|
if (_export.ExportVersion != ExportConfig.ExportVersion)
|
||||||
|
{
|
||||||
|
Service.Chat.PrintError("Import failed: Incompatible version.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Guid.TryParse(_export.ExportId, out _exportId) || _exportId == Guid.Empty)
|
||||||
|
{
|
||||||
|
Service.Chat.PrintError("Import failed: No id present.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(_export.ServerUrl))
|
||||||
|
{
|
||||||
|
// If we allow for backups as import/export, this should be removed
|
||||||
|
Service.Chat.PrintError("Import failed: Unknown server.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CleanupFloor(LocalState localState, List<Guid> oldExportIds)
|
||||||
|
{
|
||||||
|
// When saving a floor state, any markers not seen, not remote seen, and not having an import id are removed;
|
||||||
|
// so it is possible to remove "wrong" markers by not having them be in the current import.
|
||||||
|
foreach (var marker in localState.Markers)
|
||||||
|
marker.Imports.RemoveAll(id => oldExportIds.Contains(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
private 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)
|
||||||
|
{
|
||||||
|
localState.Markers.Add(remoteMarker);
|
||||||
|
localMarker = remoteMarker;
|
||||||
|
|
||||||
|
if (localMarker.Type == Marker.EType.Trap)
|
||||||
|
importedTraps++;
|
||||||
|
else if (localMarker.Type == Marker.EType.Hoard)
|
||||||
|
importedHoardCoffers++;
|
||||||
|
}
|
||||||
|
|
||||||
|
remoteMarker.Imports.Add(_exportId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
97
Pal.Client/Scheduled/QueuedSyncResponse.cs
Normal file
97
Pal.Client/Scheduled/QueuedSyncResponse.cs
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using static Pal.Client.Plugin;
|
||||||
|
|
||||||
|
namespace Pal.Client.Scheduled
|
||||||
|
{
|
||||||
|
internal class QueuedSyncResponse : IQueueOnFrameworkThread
|
||||||
|
{
|
||||||
|
public SyncType Type { get; set; }
|
||||||
|
public ushort TerritoryType { get; set; }
|
||||||
|
public bool Success { get; set; }
|
||||||
|
public List<Marker> Markers { get; set; } = new();
|
||||||
|
|
||||||
|
public void Run(Plugin plugin, ref bool recreateLayout, ref bool saveMarkers)
|
||||||
|
{
|
||||||
|
recreateLayout = true;
|
||||||
|
saveMarkers = true;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var remoteMarkers = Markers;
|
||||||
|
var currentFloor = plugin.GetFloorMarkers(TerritoryType);
|
||||||
|
if (Service.Configuration.Mode == Configuration.EMode.Online && Success && remoteMarkers.Count > 0)
|
||||||
|
{
|
||||||
|
switch (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 (Type == SyncType.Download)
|
||||||
|
currentFloor.Markers.Add(remoteMarker);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SyncType.MarkSeen:
|
||||||
|
var accountId = Service.RemoteApi.AccountId;
|
||||||
|
if (accountId == null)
|
||||||
|
break;
|
||||||
|
foreach (var remoteMarker in remoteMarkers)
|
||||||
|
{
|
||||||
|
Marker? localMarker = currentFloor.Markers.SingleOrDefault(x => x == remoteMarker);
|
||||||
|
if (localMarker != null)
|
||||||
|
localMarker.RemoteSeenOn.Add(accountId.Value);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// don't modify state for outdated floors
|
||||||
|
if (plugin.LastTerritory != TerritoryType)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (Type == SyncType.Download)
|
||||||
|
{
|
||||||
|
if (Success)
|
||||||
|
plugin.TerritorySyncState = SyncState.Complete;
|
||||||
|
else
|
||||||
|
plugin.TerritorySyncState = SyncState.Failed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
plugin.DebugMessage = $"{DateTime.Now}\n{e}";
|
||||||
|
if (Type == SyncType.Download)
|
||||||
|
plugin.TerritorySyncState = SyncState.Failed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum SyncState
|
||||||
|
{
|
||||||
|
NotAttempted,
|
||||||
|
NotNeeded,
|
||||||
|
Started,
|
||||||
|
Complete,
|
||||||
|
Failed,
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum SyncType
|
||||||
|
{
|
||||||
|
Upload,
|
||||||
|
Download,
|
||||||
|
MarkSeen,
|
||||||
|
}
|
||||||
|
}
|
@ -4,11 +4,14 @@ using Dalamud.Interface.Components;
|
|||||||
using Dalamud.Interface.ImGuiFileDialog;
|
using Dalamud.Interface.ImGuiFileDialog;
|
||||||
using Dalamud.Interface.Windowing;
|
using Dalamud.Interface.Windowing;
|
||||||
using Dalamud.Logging;
|
using Dalamud.Logging;
|
||||||
|
using ECommons;
|
||||||
using ECommons.Reflection;
|
using ECommons.Reflection;
|
||||||
using ECommons.SplatoonAPI;
|
using ECommons.SplatoonAPI;
|
||||||
using Google.Protobuf;
|
using Google.Protobuf;
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
using Pal.Client.Net;
|
using Pal.Client.Net;
|
||||||
|
using Pal.Client.Scheduled;
|
||||||
|
using Pal.Common;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@ -88,7 +91,7 @@ namespace Pal.Client.Windows
|
|||||||
{
|
{
|
||||||
DrawTrapCofferTab(ref save, ref saveAndClose);
|
DrawTrapCofferTab(ref save, ref saveAndClose);
|
||||||
DrawCommunityTab(ref saveAndClose);
|
DrawCommunityTab(ref saveAndClose);
|
||||||
//DrawImportTab();
|
DrawImportTab();
|
||||||
DrawExportTab();
|
DrawExportTab();
|
||||||
DrawDebugTab();
|
DrawDebugTab();
|
||||||
|
|
||||||
@ -196,15 +199,26 @@ namespace Pal.Client.Windows
|
|||||||
|
|
||||||
private void DrawImportTab()
|
private void DrawImportTab()
|
||||||
{
|
{
|
||||||
|
if (Service.Configuration.BetaKey != "import")
|
||||||
|
return;
|
||||||
|
|
||||||
if (ImGui.BeginTabItem("Import"))
|
if (ImGui.BeginTabItem("Import"))
|
||||||
{
|
{
|
||||||
|
ImGui.TextWrapped("Using an export is useful if you're unable to connect to the server, or don't wish to share your findings.");
|
||||||
|
ImGui.TextWrapped("Exports are (currently) generated manually, and they only include traps and hoard coffers encountered by 5 or more people. This may lead to higher floors having very sporadic coverage, but commonly run floors (e.g. PotD 51-60, HoH 21-30) are closer to complete.");
|
||||||
|
ImGui.TextWrapped("If you aren't offline, importing a file won't have any noticeable effect.");
|
||||||
|
ImGui.Separator();
|
||||||
|
ImGui.TextWrapped("Exports are available from https://github.com/carvelli/PalacePal/releases/ (as *.pal files).");
|
||||||
|
if (ImGui.Button("Visit GitHub"))
|
||||||
|
GenericHelpers.ShellStart("https://github.com/carvelli/PalacePal/releases/");
|
||||||
|
ImGui.Separator();
|
||||||
ImGui.Text("File to Import:");
|
ImGui.Text("File to Import:");
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
ImGui.InputTextWithHint("", "Path to *.pal file", ref _openImportPath, 260);
|
ImGui.InputTextWithHint("", "Path to *.pal file", ref _openImportPath, 260);
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
if (ImGuiComponents.IconButton(FontAwesomeIcon.Search))
|
if (ImGuiComponents.IconButton(FontAwesomeIcon.Search))
|
||||||
{
|
{
|
||||||
_importDialog.OpenFileDialog("Palace Pal - Import", "Palace Pal (*.pal) {*.pal}", (success, paths) =>
|
_importDialog.OpenFileDialog("Palace Pal - Import", "Palace Pal (*.pal) {.pal}", (success, paths) =>
|
||||||
{
|
{
|
||||||
if (success && paths.Count == 1)
|
if (success && paths.Count == 1)
|
||||||
{
|
{
|
||||||
@ -213,6 +227,11 @@ namespace Pal.Client.Windows
|
|||||||
}, selectionCountMax: 1, startPath: _openImportDialogStartPath, isModal: false);
|
}, selectionCountMax: 1, startPath: _openImportDialogStartPath, isModal: false);
|
||||||
_openImportDialogStartPath = null; // only use this once, FileDialogManager will save path between calls
|
_openImportDialogStartPath = null; // only use this once, FileDialogManager will save path between calls
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImGui.BeginDisabled(string.IsNullOrEmpty(_openImportPath) || !File.Exists(_openImportPath));
|
||||||
|
if (ImGui.Button("Start Import"))
|
||||||
|
DoImport(_openImportPath);
|
||||||
|
ImGui.EndDisabled();
|
||||||
ImGui.EndTabItem();
|
ImGui.EndTabItem();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -232,7 +251,7 @@ namespace Pal.Client.Windows
|
|||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
if (ImGuiComponents.IconButton(FontAwesomeIcon.Search))
|
if (ImGuiComponents.IconButton(FontAwesomeIcon.Search))
|
||||||
{
|
{
|
||||||
_importDialog.SaveFileDialog("Palace Pal - Export", "Palace Pal (*.pal) {*.pal}", todaysFileName, "pal", (success, path) =>
|
_importDialog.SaveFileDialog("Palace Pal - Export", "Palace Pal (*.pal) {.pal}", todaysFileName, "pal", (success, path) =>
|
||||||
{
|
{
|
||||||
if (success && !string.IsNullOrEmpty(path))
|
if (success && !string.IsNullOrEmpty(path))
|
||||||
{
|
{
|
||||||
@ -244,7 +263,7 @@ namespace Pal.Client.Windows
|
|||||||
|
|
||||||
ImGui.BeginDisabled(string.IsNullOrEmpty(_saveExportPath) || File.Exists(_saveExportPath));
|
ImGui.BeginDisabled(string.IsNullOrEmpty(_saveExportPath) || File.Exists(_saveExportPath));
|
||||||
if (ImGui.Button("Start Export"))
|
if (ImGui.Button("Start Export"))
|
||||||
this.DoExport(_saveExportPath);
|
DoExport(_saveExportPath);
|
||||||
ImGui.EndDisabled();
|
ImGui.EndDisabled();
|
||||||
|
|
||||||
ImGui.EndTabItem();
|
ImGui.EndTabItem();
|
||||||
@ -382,7 +401,12 @@ namespace Pal.Client.Windows
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void DoExport(string destination)
|
internal void DoImport(string sourcePath)
|
||||||
|
{
|
||||||
|
Service.Plugin.EarlyEventQueue.Enqueue(new QueuedImport(sourcePath));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void DoExport(string destinationPath)
|
||||||
{
|
{
|
||||||
Task.Run(async () =>
|
Task.Run(async () =>
|
||||||
{
|
{
|
||||||
@ -391,10 +415,10 @@ namespace Pal.Client.Windows
|
|||||||
(bool success, ExportRoot export) = await Service.RemoteApi.DoExport();
|
(bool success, ExportRoot export) = await Service.RemoteApi.DoExport();
|
||||||
if (success)
|
if (success)
|
||||||
{
|
{
|
||||||
using var output = File.Create(destination);
|
using var output = File.Create(destinationPath);
|
||||||
export.WriteTo(output);
|
export.WriteTo(output);
|
||||||
|
|
||||||
Service.Chat.Print($"Export saved as {destination}.");
|
Service.Chat.Print($"Export saved as {destinationPath}.");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
13
Pal.Common/ExportConfig.cs
Normal file
13
Pal.Common/ExportConfig.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Pal.Common
|
||||||
|
{
|
||||||
|
public static class ExportConfig
|
||||||
|
{
|
||||||
|
public static int ExportVersion { get; } = 1;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user