diff --git a/Pal.Client/LocalState.cs b/Pal.Client/LocalState.cs
new file mode 100644
index 0000000..312fd2b
--- /dev/null
+++ b/Pal.Client/LocalState.cs
@@ -0,0 +1,100 @@
+using Pal.Common;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text.Json;
+
+namespace Pal.Client
+{
+ ///
+ /// JSON for a single floor set (e.g. 51-60).
+ ///
+ internal class LocalState
+ {
+ private static readonly JsonSerializerOptions _jsonSerializerOptions = new JsonSerializerOptions { IncludeFields = true };
+ private static readonly int _currentVersion = 2;
+
+ public uint TerritoryType { get; set; }
+ public ConcurrentBag Markers { get; set; } = new();
+
+ public LocalState(uint territoryType)
+ {
+ TerritoryType = territoryType;
+ }
+
+ private void ApplyFilters()
+ {
+ if (Service.Configuration.Mode == Configuration.EMode.Offline)
+ Markers = new ConcurrentBag(Markers.Where(x => x.Seen));
+ }
+
+ public static LocalState Load(uint territoryType)
+ {
+ string path = GetSaveLocation(territoryType);
+ if (!File.Exists(path))
+ return null;
+
+ string content = File.ReadAllText(path);
+ if (content.Length == 0)
+ return null;
+
+ LocalState localState;
+ int version = 1;
+ if (content[0] == '[')
+ {
+ // v1 only had a list of markers, not a JSON object as root
+ localState = new LocalState(territoryType)
+ {
+ Markers = new ConcurrentBag(JsonSerializer.Deserialize>(content, _jsonSerializerOptions)),
+ };
+ }
+ else
+ {
+ var save = JsonSerializer.Deserialize(content, _jsonSerializerOptions);
+ localState = new LocalState(territoryType)
+ {
+ Markers = new ConcurrentBag(save.Markers),
+ };
+ version = save.Version;
+ }
+
+ localState.ApplyFilters();
+
+ if (version < _currentVersion)
+ localState.Save();
+
+ return localState;
+ }
+
+ public void Save()
+ {
+ string path = GetSaveLocation(TerritoryType);
+
+ ApplyFilters();
+ File.WriteAllText(path, JsonSerializer.Serialize(new SaveFile
+ {
+ Version = _currentVersion,
+ Markers = new HashSet(Markers)
+ }, _jsonSerializerOptions));
+ }
+
+ private static string GetSaveLocation(uint territoryType) => Path.Join(Service.PluginInterface.GetPluginConfigDirectory(), $"{territoryType}.json");
+
+ public static void UpdateAll()
+ {
+ foreach (ETerritoryType territory in typeof(ETerritoryType).GetEnumValues())
+ {
+ LocalState localState = Load((ushort)territory);
+ if (localState != null)
+ localState.Save();
+ }
+ }
+
+ public class SaveFile
+ {
+ public int Version { get; set; }
+ public HashSet Markers { get; set; } = new();
+ }
+ }
+}
diff --git a/Pal.Client/Pal.Client.csproj b/Pal.Client/Pal.Client.csproj
index f4cee03..4cce4a1 100644
--- a/Pal.Client/Pal.Client.csproj
+++ b/Pal.Client/Pal.Client.csproj
@@ -3,7 +3,7 @@
net6.0-windows
9.0
- 1.8.0.0
+ 1.9.0.0
diff --git a/Pal.Client/Plugin.cs b/Pal.Client/Plugin.cs
index 991d281..0af2afc 100644
--- a/Pal.Client/Plugin.cs
+++ b/Pal.Client/Plugin.cs
@@ -16,11 +16,9 @@ using Pal.Client.Windows;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
-using System.IO;
using System.Linq;
using System.Numerics;
using System.Runtime.InteropServices;
-using System.Text.Json;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
@@ -43,7 +41,7 @@ namespace Pal.Client
private bool _configUpdated = false;
private LocalizedChatMessages _localizedChatMessages = new();
- internal ConcurrentDictionary> FloorMarkers { get; } = new();
+ internal ConcurrentDictionary FloorMarkers { get; } = new();
internal ConcurrentBag EphemeralMarkers { get; set; } = new();
internal ushort LastTerritory { get; private set; }
public SyncState TerritorySyncState { get; set; }
@@ -114,15 +112,29 @@ namespace Pal.Client
return;
}
- switch (arguments)
+ try
{
- case "stats":
- Task.Run(async () => await FetchFloorStatistics());
- break;
+ switch (arguments)
+ {
+ case "stats":
+ Task.Run(async () => await FetchFloorStatistics());
+ break;
- default:
- Service.WindowSystem.GetWindow()?.Toggle();
- break;
+#if DEBUG
+ case "update-saves":
+ LocalState.UpdateAll();
+ Service.Chat.Print("Updated all locally cached marker files to latest version.");
+ break;
+#endif
+
+ default:
+ Service.WindowSystem.GetWindow()?.Toggle();
+ break;
+ }
+ }
+ catch (Exception e)
+ {
+ Service.Chat.PrintError($"[Palace Pal] {e}");
}
}
@@ -210,15 +222,7 @@ namespace Pal.Client
{
if (Service.Configuration.Mode == Configuration.EMode.Offline)
{
- foreach (var path in Directory.GetFiles(Service.PluginInterface.GetPluginConfigDirectory()))
- {
- if (path.EndsWith(".json"))
- {
- var markers = JsonSerializer.Deserialize>(File.ReadAllText(path), new JsonSerializerOptions { IncludeFields = true }).Where(x => x.Seen).ToList();
- File.WriteAllText(path, JsonSerializer.Serialize(markers, new JsonSerializerOptions { IncludeFields = true }));
- }
- }
-
+ LocalState.UpdateAll();
FloorMarkers.Clear();
EphemeralMarkers.Clear();
LastTerritory = 0;
@@ -234,7 +238,7 @@ namespace Pal.Client
TerritorySyncState = SyncState.NotAttempted;
if (IsInPotdOrHoh())
- FloorMarkers[LastTerritory] = new ConcurrentBag(LoadSavedMarkers());
+ FloorMarkers[LastTerritory] = LocalState.Load(LastTerritory) ?? new LocalState(LastTerritory);
EphemeralMarkers.Clear();
PomanderOfSight = PomanderState.Inactive;
PomanderOfIntuition = PomanderState.Inactive;
@@ -258,11 +262,11 @@ namespace Pal.Client
saveMarkers = true;
}
- if (!FloorMarkers.TryGetValue(LastTerritory, out var currentFloorMarkers))
- FloorMarkers[LastTerritory] = currentFloorMarkers = new ConcurrentBag();
+ if (!FloorMarkers.TryGetValue(LastTerritory, out var currentFloor))
+ FloorMarkers[LastTerritory] = currentFloor = new LocalState(LastTerritory);
IList visibleMarkers = GetRelevantGameObjects();
- HandlePersistentMarkers(currentFloorMarkers, 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)
@@ -271,9 +275,10 @@ namespace Pal.Client
}
}
- private void HandlePersistentMarkers(ConcurrentBag currentFloorMarkers, IList visibleMarkers, bool saveMarkers, bool recreateLayout)
+ private void HandlePersistentMarkers(LocalState currentFloor, IList visibleMarkers, bool saveMarkers, bool recreateLayout)
{
var config = Service.Configuration;
+ var currentFloorMarkers = currentFloor.Markers;
foreach (var visibleMarker in visibleMarkers)
{
@@ -320,7 +325,7 @@ namespace Pal.Client
if (saveMarkers)
{
- SaveMarkers();
+ currentFloor.Save();
if (TerritorySyncState == SyncState.Complete)
{
@@ -431,23 +436,6 @@ namespace Pal.Client
}
}
- public string GetSaveForCurrentTerritory() => Path.Join(Service.PluginInterface.GetPluginConfigDirectory(), $"{LastTerritory}.json");
-
- private List LoadSavedMarkers()
- {
- string path = GetSaveForCurrentTerritory();
- if (File.Exists(path))
- return JsonSerializer.Deserialize>(File.ReadAllText(path), new JsonSerializerOptions { IncludeFields = true }).Where(x => x.Seen || Service.Configuration.Mode == Configuration.EMode.Online).ToList();
- else
- return new List();
- }
-
- private void SaveMarkers()
- {
- string path = GetSaveForCurrentTerritory();
- File.WriteAllText(path, JsonSerializer.Serialize(FloorMarkers[LastTerritory], new JsonSerializerOptions { IncludeFields = true }));
- }
-
private async Task DownloadMarkersForTerritory(ushort territoryId)
{
try
@@ -498,18 +486,18 @@ namespace Pal.Client
while (_remoteDownloads.TryDequeue(out var download))
{
var (territoryId, success, downloadedMarkers) = download;
- if (Service.Configuration.Mode == Configuration.EMode.Online && success && FloorMarkers.TryGetValue(territoryId, out var currentFloorMarkers) && downloadedMarkers.Count > 0)
+ if (Service.Configuration.Mode == Configuration.EMode.Online && success && FloorMarkers.TryGetValue(territoryId, out var currentFloor) && downloadedMarkers.Count > 0)
{
foreach (var downloadedMarker in downloadedMarkers)
{
- Marker seenMarker = currentFloorMarkers.SingleOrDefault(x => x == downloadedMarker);
+ Marker seenMarker = currentFloor.Markers.SingleOrDefault(x => x == downloadedMarker);
if (seenMarker != null)
{
seenMarker.RemoteSeen = true;
continue;
}
- currentFloorMarkers.Add(downloadedMarker);
+ currentFloor.Markers.Add(downloadedMarker);
}
}
diff --git a/Pal.Client/Windows/ConfigWindow.cs b/Pal.Client/Windows/ConfigWindow.cs
index 7046f46..e9e2ac4 100644
--- a/Pal.Client/Windows/ConfigWindow.cs
+++ b/Pal.Client/Windows/ConfigWindow.cs
@@ -149,16 +149,16 @@ namespace Pal.Client.Windows
ImGui.Text($"{plugin.DebugMessage}");
ImGui.Indent();
- if (plugin.FloorMarkers.TryGetValue(plugin.LastTerritory, out var currentFloorMarkers))
+ if (plugin.FloorMarkers.TryGetValue(plugin.LastTerritory, out var currentFloor))
{
if (_showTraps)
{
- int traps = currentFloorMarkers.Count(x => x != null && x.Type == Marker.EType.Trap);
+ int traps = currentFloor.Markers.Count(x => x != null && x.Type == Marker.EType.Trap);
ImGui.Text($"{traps} known trap{(traps == 1 ? "" : "s")}");
}
if (_showHoard)
{
- int hoardCoffers = currentFloorMarkers.Count(x => x != null && x.Type == Marker.EType.Hoard);
+ int hoardCoffers = currentFloor.Markers.Count(x => x != null && x.Type == Marker.EType.Hoard);
ImGui.Text($"{hoardCoffers} known hoard coffer{(hoardCoffers == 1 ? "" : "s")}");
}
if (_showSilverCoffers)