⚗️ Import/Export: Import, event rework
This commit is contained in:
parent
bb721bc37f
commit
4c22334761
@ -1,5 +1,6 @@
|
||||
using Dalamud.Configuration;
|
||||
using Dalamud.Logging;
|
||||
using Pal.Client.Scheduled;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@ -25,6 +26,8 @@ namespace Pal.Client
|
||||
public Dictionary<string, Guid> AccountIds { private 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 Vector4 TrapColor { get; set; } = new Vector4(1, 0, 0, 0.4f);
|
||||
public bool OnlyVisibleTrapsAfterPomander { get; set; } = true;
|
||||
@ -36,10 +39,12 @@ namespace Pal.Client
|
||||
public bool ShowSilverCoffers { get; set; } = false;
|
||||
public Vector4 SilverCofferColor { get; set; } = new Vector4(1, 1, 1, 0.4f);
|
||||
public bool FillSilverCoffers { get; set; } = true;
|
||||
#endregion
|
||||
|
||||
public delegate void OnSaved();
|
||||
public event OnSaved? Saved;
|
||||
/// <summary>
|
||||
/// Needs to be manually set.
|
||||
/// </summary>
|
||||
public string BetaKey { get; set; } = "";
|
||||
#endregion
|
||||
|
||||
#pragma warning disable CS0612 // Type or member is obsolete
|
||||
public void Migrate()
|
||||
@ -75,7 +80,7 @@ namespace Pal.Client
|
||||
public void Save()
|
||||
{
|
||||
Service.PluginInterface.SavePluginConfig(this);
|
||||
Saved?.Invoke();
|
||||
Service.Plugin.EarlyEventQueue.Enqueue(new QueuedConfigUpdate());
|
||||
}
|
||||
|
||||
public enum EMode
|
||||
@ -105,5 +110,17 @@ namespace Pal.Client
|
||||
/// </summary>
|
||||
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()
|
||||
{
|
||||
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)
|
||||
|
@ -41,6 +41,15 @@ namespace Pal.Client
|
||||
[JsonIgnore]
|
||||
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]
|
||||
public Element? SplatoonElement { get; set; }
|
||||
|
||||
|
@ -15,6 +15,7 @@ using Grpc.Core;
|
||||
using ImGuiNET;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
using Pal.Client.Net;
|
||||
using Pal.Client.Scheduled;
|
||||
using Pal.Client.Windows;
|
||||
using Pal.Common;
|
||||
using System;
|
||||
@ -35,23 +36,23 @@ namespace Pal.Client
|
||||
private const string SPLATOON_TRAP_HOARD = "PalacePal.TrapHoard";
|
||||
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>
|
||||
{
|
||||
{ Marker.EType.Trap, new MarkerConfig { Radius = 1.7f } },
|
||||
{ Marker.EType.Hoard, new MarkerConfig { Radius = 1.7f, OffsetY = -0.03f } },
|
||||
{ Marker.EType.SilverCoffer, new MarkerConfig { Radius = 1f } },
|
||||
};
|
||||
private bool _configUpdated = false;
|
||||
private LocalizedChatMessages _localizedChatMessages = new();
|
||||
|
||||
internal ConcurrentDictionary<ushort, LocalState> FloorMarkers { get; } = 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 PomanderState PomanderOfSight { get; set; } = PomanderState.Inactive;
|
||||
public PomanderState PomanderOfIntuition { get; set; } = PomanderState.Inactive;
|
||||
public string? DebugMessage { get; set; }
|
||||
internal Queue<IQueueOnFrameworkThread> EarlyEventQueue { get; } = new();
|
||||
internal Queue<IQueueOnFrameworkThread> LateEventQueue { get; } = new();
|
||||
|
||||
public string Name => "Palace Pal";
|
||||
|
||||
@ -101,7 +102,6 @@ namespace Pal.Client
|
||||
pluginInterface.UiBuilder.Draw += Service.WindowSystem.Draw;
|
||||
pluginInterface.UiBuilder.OpenConfigUi += OnOpenConfigUi;
|
||||
Service.Framework.Update += OnFrameworkUpdate;
|
||||
Service.Configuration.Saved += OnConfigSaved;
|
||||
Service.Chat.ChatMessage += OnChatMessage;
|
||||
Service.CommandManager.AddHandler("/pal", new CommandInfo(OnCommand)
|
||||
{
|
||||
@ -182,7 +182,6 @@ namespace Pal.Client
|
||||
Service.PluginInterface.UiBuilder.Draw -= Service.WindowSystem.Draw;
|
||||
Service.PluginInterface.UiBuilder.OpenConfigUi -= OnOpenConfigUi;
|
||||
Service.Framework.Update -= OnFrameworkUpdate;
|
||||
Service.Configuration.Saved -= OnConfigSaved;
|
||||
Service.Chat.ChatMessage -= OnChatMessage;
|
||||
|
||||
Service.WindowSystem.RemoveAllWindows();
|
||||
@ -208,11 +207,6 @@ namespace Pal.Client
|
||||
}
|
||||
#endregion
|
||||
|
||||
private void OnConfigSaved()
|
||||
{
|
||||
_configUpdated = true;
|
||||
}
|
||||
|
||||
private void OnChatMessage(XivChatType type, uint senderId, ref SeString sender, ref SeString seMessage, ref bool isHandled)
|
||||
{
|
||||
if (Service.Configuration.FirstUse)
|
||||
@ -259,27 +253,18 @@ namespace Pal.Client
|
||||
try
|
||||
{
|
||||
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;
|
||||
|
||||
while (EarlyEventQueue.TryDequeue(out IQueueOnFrameworkThread? queued))
|
||||
queued?.Run(this, ref recreateLayout, ref saveMarkers);
|
||||
|
||||
if (LastTerritory != Service.ClientState.TerritoryType)
|
||||
{
|
||||
LastTerritory = Service.ClientState.TerritoryType;
|
||||
TerritorySyncState = SyncState.NotAttempted;
|
||||
|
||||
if (IsInDeepDungeon())
|
||||
FloorMarkers[LastTerritory] = LocalState.Load(LastTerritory) ?? new LocalState(LastTerritory);
|
||||
GetFloorMarkers(LastTerritory);
|
||||
EphemeralMarkers.Clear();
|
||||
PomanderOfSight = PomanderState.Inactive;
|
||||
PomanderOfIntuition = PomanderState.Inactive;
|
||||
@ -296,15 +281,10 @@ namespace Pal.Client
|
||||
Task.Run(async () => await DownloadMarkersForTerritory(LastTerritory));
|
||||
}
|
||||
|
||||
if (_pendingSyncResponses.Count > 0)
|
||||
{
|
||||
HandleSyncResponses();
|
||||
recreateLayout = true;
|
||||
saveMarkers = true;
|
||||
}
|
||||
while (LateEventQueue.TryDequeue(out IQueueOnFrameworkThread? queued))
|
||||
queued?.Run(this, ref recreateLayout, ref saveMarkers);
|
||||
|
||||
if (!FloorMarkers.TryGetValue(LastTerritory, out var currentFloor))
|
||||
FloorMarkers[LastTerritory] = currentFloor = new LocalState(LastTerritory);
|
||||
var currentFloor = GetFloorMarkers(LastTerritory);
|
||||
|
||||
IList<Marker> visibleMarkers = GetRelevantGameObjects();
|
||||
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)
|
||||
{
|
||||
var config = Service.Configuration;
|
||||
@ -403,7 +388,7 @@ namespace Pal.Client
|
||||
List<Element> elements = new List<Element>();
|
||||
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)
|
||||
{
|
||||
@ -503,7 +488,7 @@ namespace Pal.Client
|
||||
try
|
||||
{
|
||||
var (success, downloadedMarkers) = await Service.RemoteApi.DownloadRemoteMarkers(territoryId);
|
||||
_pendingSyncResponses.Enqueue(new Sync
|
||||
LateEventQueue.Enqueue(new QueuedSyncResponse
|
||||
{
|
||||
Type = SyncType.Download,
|
||||
TerritoryType = territoryId,
|
||||
@ -522,7 +507,7 @@ namespace Pal.Client
|
||||
try
|
||||
{
|
||||
var (success, uploadedMarkers) = await Service.RemoteApi.UploadMarker(territoryId, markersToUpload);
|
||||
_pendingSyncResponses.Enqueue(new Sync
|
||||
LateEventQueue.Enqueue(new QueuedSyncResponse
|
||||
{
|
||||
Type = SyncType.Upload,
|
||||
TerritoryType = territoryId,
|
||||
@ -541,7 +526,7 @@ namespace Pal.Client
|
||||
try
|
||||
{
|
||||
var success = await Service.RemoteApi.MarkAsSeen(territoryId, markersToUpdate);
|
||||
_pendingSyncResponses.Enqueue(new Sync
|
||||
LateEventQueue.Enqueue(new QueuedSyncResponse
|
||||
{
|
||||
Type = SyncType.MarkSeen,
|
||||
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()
|
||||
{
|
||||
List<Marker> result = new();
|
||||
@ -729,30 +650,6 @@ namespace Pal.Client
|
||||
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
|
||||
{
|
||||
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.Windowing;
|
||||
using Dalamud.Logging;
|
||||
using ECommons;
|
||||
using ECommons.Reflection;
|
||||
using ECommons.SplatoonAPI;
|
||||
using Google.Protobuf;
|
||||
using ImGuiNET;
|
||||
using Pal.Client.Net;
|
||||
using Pal.Client.Scheduled;
|
||||
using Pal.Common;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
@ -88,7 +91,7 @@ namespace Pal.Client.Windows
|
||||
{
|
||||
DrawTrapCofferTab(ref save, ref saveAndClose);
|
||||
DrawCommunityTab(ref saveAndClose);
|
||||
//DrawImportTab();
|
||||
DrawImportTab();
|
||||
DrawExportTab();
|
||||
DrawDebugTab();
|
||||
|
||||
@ -196,15 +199,26 @@ namespace Pal.Client.Windows
|
||||
|
||||
private void DrawImportTab()
|
||||
{
|
||||
if (Service.Configuration.BetaKey != "import")
|
||||
return;
|
||||
|
||||
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.SameLine();
|
||||
ImGui.InputTextWithHint("", "Path to *.pal file", ref _openImportPath, 260);
|
||||
ImGui.SameLine();
|
||||
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)
|
||||
{
|
||||
@ -213,6 +227,11 @@ namespace Pal.Client.Windows
|
||||
}, selectionCountMax: 1, startPath: _openImportDialogStartPath, isModal: false);
|
||||
_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();
|
||||
}
|
||||
}
|
||||
@ -232,7 +251,7 @@ namespace Pal.Client.Windows
|
||||
ImGui.SameLine();
|
||||
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))
|
||||
{
|
||||
@ -244,7 +263,7 @@ namespace Pal.Client.Windows
|
||||
|
||||
ImGui.BeginDisabled(string.IsNullOrEmpty(_saveExportPath) || File.Exists(_saveExportPath));
|
||||
if (ImGui.Button("Start Export"))
|
||||
this.DoExport(_saveExportPath);
|
||||
DoExport(_saveExportPath);
|
||||
ImGui.EndDisabled();
|
||||
|
||||
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 () =>
|
||||
{
|
||||
@ -391,10 +415,10 @@ namespace Pal.Client.Windows
|
||||
(bool success, ExportRoot export) = await Service.RemoteApi.DoExport();
|
||||
if (success)
|
||||
{
|
||||
using var output = File.Create(destination);
|
||||
using var output = File.Create(destinationPath);
|
||||
export.WriteTo(output);
|
||||
|
||||
Service.Chat.Print($"Export saved as {destination}.");
|
||||
Service.Chat.Print($"Export saved as {destinationPath}.");
|
||||
}
|
||||
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