Restructure save format to be more expandable if needed

rendering v1.9
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>
<TargetFramework>net6.0-windows</TargetFramework>
<LangVersion>9.0</LangVersion>
<Version>1.8.0.0</Version>
<Version>1.9.0.0</Version>
</PropertyGroup>
<PropertyGroup>

View File

@ -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<ushort, ConcurrentBag<Marker>> FloorMarkers { get; } = new();
internal ConcurrentDictionary<ushort, LocalState> FloorMarkers { get; } = new();
internal ConcurrentBag<Marker> EphemeralMarkers { get; set; } = new();
internal ushort LastTerritory { get; private set; }
public SyncState TerritorySyncState { get; set; }
@ -114,17 +112,31 @@ namespace Pal.Client
return;
}
try
{
switch (arguments)
{
case "stats":
Task.Run(async () => await FetchFloorStatistics());
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<ConfigWindow>()?.Toggle();
break;
}
}
catch (Exception e)
{
Service.Chat.PrintError($"[Palace Pal] {e}");
}
}
#region IDisposable Support
protected virtual void Dispose(bool disposing)
@ -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<List<Marker>>(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<Marker>(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<Marker>();
if (!FloorMarkers.TryGetValue(LastTerritory, out var currentFloor))
FloorMarkers[LastTerritory] = currentFloor = new LocalState(LastTerritory);
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);
}
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 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<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)
{
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);
}
}

View File

@ -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)