using System.Text.Json; using Ananta.Server.Configuration; using Ananta.Server.Protocol.Client4229938; namespace Ananta.Server.Handlers.Game; /// /// Persists per-player vehicle tuning (bought parts / suits / session unlocks) to /// save/vehicles/<pid>.json so purchases survive a server restart. /// internal static class VehicleTuningStore { private static string DirFor(ulong pid) { string baseDir; try { baseDir = PrivateServerConfigStore.ProjectRoot; } catch { baseDir = AppContext.BaseDirectory; } return Path.Combine(baseDir, "save", "vehicles"); } private static string FileFor(ulong pid) => Path.Combine(DirFor(pid), $"{pid}.json"); public static void Save(ulong pid, WorldEntryState world) { try { var dir = DirFor(pid); Directory.CreateDirectory(dir); var dto = new TuningDto { Unlocked = world.SessionUnlockedVehicleConfigs.ToList(), Parts = world.VehiclePartsByConfig.ToDictionary( kv => kv.Key, kv => kv.Value.Select(p => new PartDto { Type = p.Type, PartId = p.PartId }).ToList()), Suits = new Dictionary(world.VehicleSuitByConfig), }; var json = JsonSerializer.Serialize(dto, new JsonSerializerOptions { WriteIndented = true }); File.WriteAllText(FileFor(pid), json); } catch (Exception ex) { Console.WriteLine($"[TUNING] save failed pid={pid}: {ex.Message}"); } } /// Loads saved tuning into the (fresh) world state — called on world entry. public static void LoadInto(ulong pid, WorldEntryState world) { try { var path = FileFor(pid); if (!File.Exists(path)) return; var dto = JsonSerializer.Deserialize(File.ReadAllText(path)); if (dto is null) return; lock (world.SyncRoot) { foreach (var id in dto.Unlocked) world.SessionUnlockedVehicleConfigs.Add(id); foreach (var kv in dto.Parts) world.VehiclePartsByConfig[kv.Key] = kv.Value.Select(p => (p.Type, p.PartId)).ToList(); foreach (var kv in dto.Suits) world.VehicleSuitByConfig[kv.Key] = kv.Value; } Console.WriteLine($"[TUNING] loaded saved tuning pid={pid} vehicles={dto.Unlocked.Count} parts={dto.Parts.Count}"); } catch (Exception ex) { Console.WriteLine($"[TUNING] load failed pid={pid}: {ex.Message}"); } } private sealed class TuningDto { public List Unlocked { get; set; } = []; public Dictionary> Parts { get; set; } = []; public Dictionary Suits { get; set; } = []; } private sealed class PartDto { public uint Type { get; set; } public uint PartId { get; set; } } }