Restructure save format to be more expandable if needed

This commit is contained in:
Liza 2022-10-30 00:21:17 +02:00
parent 8ec3e5b5b0
commit 2adf030ec5
4 changed files with 137 additions and 49 deletions

100
Pal.Client/LocalState.cs Normal file
View File

@ -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
{
/// <summary>
/// JSON for a single floor set (e.g. 51-60).
/// </summary>
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<Marker> Markers { get; set; } = new();
public LocalState(uint territoryType)
{
TerritoryType = territoryType;
}
private void ApplyFilters()
{
if (Service.Configuration.Mode == Configuration.EMode.Offline)
Markers = new ConcurrentBag<Marker>(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<Marker>(JsonSerializer.Deserialize<HashSet<Marker>>(content, _jsonSerializerOptions)),
};
}
else
{
var save = JsonSerializer.Deserialize<SaveFile>(content, _jsonSerializerOptions);
localState = new LocalState(territoryType)
{
Markers = new ConcurrentBag<Marker>(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<Marker>(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<Marker> Markers { get; set; } = new();
}
}
}

View File

@ -3,7 +3,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0-windows</TargetFramework> <TargetFramework>net6.0-windows</TargetFramework>
<LangVersion>9.0</LangVersion> <LangVersion>9.0</LangVersion>
<Version>1.8.0.0</Version> <Version>1.9.0.0</Version>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>

View File

@ -16,11 +16,9 @@ using Pal.Client.Windows;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text.Json;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -43,7 +41,7 @@ namespace Pal.Client
private bool _configUpdated = false; private bool _configUpdated = false;
private LocalizedChatMessages _localizedChatMessages = new(); private LocalizedChatMessages _localizedChatMessages = new();
internal ConcurrentDictionary<ushort, ConcurrentBag<Marker>> 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; private set; }
public SyncState TerritorySyncState { get; set; } public SyncState TerritorySyncState { get; set; }
@ -114,17 +112,31 @@ namespace Pal.Client
return; return;
} }
try
{
switch (arguments) switch (arguments)
{ {
case "stats": case "stats":
Task.Run(async () => await FetchFloorStatistics()); Task.Run(async () => await FetchFloorStatistics());
break; break;
#if DEBUG
case "update-saves":
LocalState.UpdateAll();
Service.Chat.Print("Updated all locally cached marker files to latest version.");
break;
#endif
default: default:
Service.WindowSystem.GetWindow<ConfigWindow>()?.Toggle(); Service.WindowSystem.GetWindow<ConfigWindow>()?.Toggle();
break; break;
} }
} }
catch (Exception e)
{
Service.Chat.PrintError($"[Palace Pal] {e}");
}
}
#region IDisposable Support #region IDisposable Support
protected virtual void Dispose(bool disposing) protected virtual void Dispose(bool disposing)
@ -210,15 +222,7 @@ namespace Pal.Client
{ {
if (Service.Configuration.Mode == Configuration.EMode.Offline) if (Service.Configuration.Mode == Configuration.EMode.Offline)
{ {
foreach (var path in Directory.GetFiles(Service.PluginInterface.GetPluginConfigDirectory())) LocalState.UpdateAll();
{
if (path.EndsWith(".json"))
{
var markers = JsonSerializer.Deserialize<List<Marker>>(File.ReadAllText(path), new JsonSerializerOptions { IncludeFields = true }).Where(x => x.Seen).ToList();
File.WriteAllText(path, JsonSerializer.Serialize(markers, new JsonSerializerOptions { IncludeFields = true }));
}
}
FloorMarkers.Clear(); FloorMarkers.Clear();
EphemeralMarkers.Clear(); EphemeralMarkers.Clear();
LastTerritory = 0; LastTerritory = 0;
@ -234,7 +238,7 @@ namespace Pal.Client
TerritorySyncState = SyncState.NotAttempted; TerritorySyncState = SyncState.NotAttempted;
if (IsInPotdOrHoh()) if (IsInPotdOrHoh())
FloorMarkers[LastTerritory] = new ConcurrentBag<Marker>(LoadSavedMarkers()); FloorMarkers[LastTerritory] = LocalState.Load(LastTerritory) ?? new LocalState(LastTerritory);
EphemeralMarkers.Clear(); EphemeralMarkers.Clear();
PomanderOfSight = PomanderState.Inactive; PomanderOfSight = PomanderState.Inactive;
PomanderOfIntuition = PomanderState.Inactive; PomanderOfIntuition = PomanderState.Inactive;
@ -258,11 +262,11 @@ namespace Pal.Client
saveMarkers = true; saveMarkers = true;
} }
if (!FloorMarkers.TryGetValue(LastTerritory, out var currentFloorMarkers)) if (!FloorMarkers.TryGetValue(LastTerritory, out var currentFloor))
FloorMarkers[LastTerritory] = currentFloorMarkers = new ConcurrentBag<Marker>(); FloorMarkers[LastTerritory] = currentFloor = new LocalState(LastTerritory);
IList<Marker> visibleMarkers = GetRelevantGameObjects(); IList<Marker> 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); HandleEphemeralMarkers(visibleMarkers.Where(x => !x.IsPermanent()).ToList(), recreateLayout);
} }
catch (Exception e) catch (Exception e)
@ -271,9 +275,10 @@ namespace Pal.Client
} }
} }
private void HandlePersistentMarkers(ConcurrentBag<Marker> currentFloorMarkers, 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;
var currentFloorMarkers = currentFloor.Markers;
foreach (var visibleMarker in visibleMarkers) foreach (var visibleMarker in visibleMarkers)
{ {
@ -320,7 +325,7 @@ namespace Pal.Client
if (saveMarkers) if (saveMarkers)
{ {
SaveMarkers(); currentFloor.Save();
if (TerritorySyncState == SyncState.Complete) if (TerritorySyncState == SyncState.Complete)
{ {
@ -431,23 +436,6 @@ namespace Pal.Client
} }
} }
public string GetSaveForCurrentTerritory() => Path.Join(Service.PluginInterface.GetPluginConfigDirectory(), $"{LastTerritory}.json");
private List<Marker> LoadSavedMarkers()
{
string path = GetSaveForCurrentTerritory();
if (File.Exists(path))
return JsonSerializer.Deserialize<List<Marker>>(File.ReadAllText(path), new JsonSerializerOptions { IncludeFields = true }).Where(x => x.Seen || Service.Configuration.Mode == Configuration.EMode.Online).ToList();
else
return new List<Marker>();
}
private void SaveMarkers()
{
string path = GetSaveForCurrentTerritory();
File.WriteAllText(path, JsonSerializer.Serialize(FloorMarkers[LastTerritory], new JsonSerializerOptions { IncludeFields = true }));
}
private async Task DownloadMarkersForTerritory(ushort territoryId) private async Task DownloadMarkersForTerritory(ushort territoryId)
{ {
try try
@ -498,18 +486,18 @@ namespace Pal.Client
while (_remoteDownloads.TryDequeue(out var download)) while (_remoteDownloads.TryDequeue(out var download))
{ {
var (territoryId, success, downloadedMarkers) = 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) foreach (var downloadedMarker in downloadedMarkers)
{ {
Marker seenMarker = currentFloorMarkers.SingleOrDefault(x => x == downloadedMarker); Marker seenMarker = currentFloor.Markers.SingleOrDefault(x => x == downloadedMarker);
if (seenMarker != null) if (seenMarker != null)
{ {
seenMarker.RemoteSeen = true; seenMarker.RemoteSeen = true;
continue; continue;
} }
currentFloorMarkers.Add(downloadedMarker); currentFloor.Markers.Add(downloadedMarker);
} }
} }

View File

@ -149,16 +149,16 @@ namespace Pal.Client.Windows
ImGui.Text($"{plugin.DebugMessage}"); ImGui.Text($"{plugin.DebugMessage}");
ImGui.Indent(); ImGui.Indent();
if (plugin.FloorMarkers.TryGetValue(plugin.LastTerritory, out var currentFloorMarkers)) if (plugin.FloorMarkers.TryGetValue(plugin.LastTerritory, out var currentFloor))
{ {
if (_showTraps) 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")}"); ImGui.Text($"{traps} known trap{(traps == 1 ? "" : "s")}");
} }
if (_showHoard) 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")}"); ImGui.Text($"{hoardCoffers} known hoard coffer{(hoardCoffers == 1 ? "" : "s")}");
} }
if (_showSilverCoffers) if (_showSilverCoffers)