using Pal.Common; using System; 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 = 3; 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 || (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(Markers.Where(x => x.Seen || !x.WasImported || x.Imports.Count > 0)); } 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) ?? new()), }; } else { var save = JsonSerializer.Deserialize(content, _jsonSerializerOptions); if (save == null) return null; 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(); if (Markers.Count == 0) File.Delete(path); else { 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 void UndoImport(List importIds) { // 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 Markers) marker.Imports.RemoveAll(id => importIds.Contains(id)); } public class SaveFile { public int Version { get; set; } public HashSet Markers { get; set; } = new(); } } }