2
0
mirror of https://github.com/9ParsonsB/Pulsar.git synced 2025-04-05 17:39:39 -04:00
pulsar/Pulsar/Utils/JournalJsonConverter.cs
Ben Parsons 337a9d5ea0 Update Status Fuel Display
Start working on Explorer Panel
Backend now uses polymorphic de/serialization (net9)
2024-05-17 23:55:55 +10:00

364 lines
17 KiB
C#

using Observatory.Framework.Files.Journal.Combat;
using Observatory.Framework.Files.Journal.Exploration;
using Observatory.Framework.Files.Journal.Odyssey;
using Observatory.Framework.Files.Journal.Other;
using Observatory.Framework.Files.Journal.Powerplay;
using Observatory.Framework.Files.Journal.Startup;
using Observatory.Framework.Files.Journal.StationServices;
using Observatory.Framework.Files.Journal.Trade;
using Observatory.Framework.Files.Journal.Travel;
namespace Pulsar.Utils;
using Observatory.Framework.Files.Journal;
[Flags]
public enum JournalReaderState
{
/// <summary>
/// Have read the first character of the object
/// </summary>
Start,
/// <summary>
/// Have read the timestamp. Generally the first property in a journal entry.
/// </summary>
Timestamp,
/// <summary>
/// have read the event name. Generally the second property in a journal entry.
/// </summary>
Event,
/// <summary>
/// Have read the last character of the object, the next character should be a newline, whitespace, EOF, or another object.
/// </summary>
End,
}
/// <summary>
/// A JournalFile contains a collection of journal entries.
/// Each journal entry is a JSON object, delimited by a newline character.
/// all Journals can be deserialized into a JournalBase object for identification
/// and then deserialized into their respective types.
/// </summary>
public class JournalJsonConverter(ILogger<JournalJsonConverter> logger) : JsonConverter<JournalBase>
{
private JournalReaderState state = JournalReaderState.Start;
public override JournalBase? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
Utf8JsonReader clone = reader;
DateTimeOffset? timestamp = null;
string? eventName = null;
// for debug
int depth = 0;
do
{
depth++;
switch (clone.TokenType)
{
case JsonTokenType.StartObject:
state = JournalReaderState.Start;
break;
case JsonTokenType.EndObject:
state = JournalReaderState.End;
break;
case JsonTokenType.PropertyName:
var propertyName = clone.GetString();
// if we have not started reading the body, and we have not read the (timestamp or event name)
if ((state & JournalReaderState.Timestamp) == 0 || (state & JournalReaderState.Event) == 0)
{
switch (propertyName)
{
case "timestamp":
clone.Read();
timestamp = clone.GetDateTimeOffset();
state = JournalReaderState.Timestamp;
break;
case "event":
clone.Read();
eventName = clone.GetString();
state = JournalReaderState.Event;
break;
}
}
if ((state & JournalReaderState.Event) != 0)
{
// create destination type
return GetDestinationType(ref reader, eventName!);
}
break;
case JsonTokenType.Comment:
continue;
case JsonTokenType.None:
case JsonTokenType.StartArray:
case JsonTokenType.EndArray:
case JsonTokenType.String:
case JsonTokenType.Number:
case JsonTokenType.True:
case JsonTokenType.False:
case JsonTokenType.Null:
logger.LogWarning("Unexpected token type {TokenType} at depth {Depth}", clone.TokenType, depth);
break;
default:
throw new ArgumentOutOfRangeException();
}
} while (clone.Read());
logger.LogWarning("Failed to deserialize journal entry at depth: {Depth}. Event?: {EventName}, Timestamp?: {Timestamp}", depth, eventName, timestamp);
return null;
// TODO: handle inf (invalid data) in the journal files
// else if (typeof(TJournal) == typeof(Scan) && json.Contains("\"RotationPeriod\":inf"))
// {
// deserialized = JsonSerializer.Deserialize<TJournal>(json.Replace("\"RotationPeriod\":inf,", ""));
// }
}
private JournalBase GetDestinationType(ref Utf8JsonReader reader, string eventName)
{
switch (eventName)
{
case "Fileheader":
return JsonSerializer.Deserialize<FileHeader>(ref reader)!;
case "Commander":
return JsonSerializer.Deserialize<Commander>(ref reader)!;
case "Materials":
return JsonSerializer.Deserialize<Materials>(ref reader)!;
case "Rank":
return JsonSerializer.Deserialize<Rank>(ref reader)!;
case "Music":
return JsonSerializer.Deserialize<Music>(ref reader)!;
case "Cargo":
return JsonSerializer.Deserialize<Cargo>(ref reader)!;
case "Loadout":
return JsonSerializer.Deserialize<Loadout>(ref reader)!;
case "Missions":
return JsonSerializer.Deserialize<Missions>(ref reader)!;
case "FSSSignalDiscovered":
return JsonSerializer.Deserialize<FSSSignalDiscovered>(ref reader)!;
case "Reputation":
return JsonSerializer.Deserialize<Reputation>(ref reader)!;
case "LoadGame":
return JsonSerializer.Deserialize<LoadGame>(ref reader)!;
case "ReceiveText":
return JsonSerializer.Deserialize<ReceiveText>(ref reader)!;
case "ShipLocker":
return JsonSerializer.Deserialize<ShipLockerMaterials>(ref reader)!;
case "Location":
return JsonSerializer.Deserialize<Location>(ref reader)!;
case "Powerplay":
return JsonSerializer.Deserialize<Powerplay>(ref reader)!;
case "ReservoirReplenished":
return JsonSerializer.Deserialize<ReservoirReplenished>(ref reader)!;
case "Statistics":
return JsonSerializer.Deserialize<Statistics>(ref reader)!;
case "Scan":
return JsonSerializer.Deserialize<Scan>(ref reader)!;
case "Shipyard":
return JsonSerializer.Deserialize<Shipyard>(ref reader)!;
case "Docked":
return JsonSerializer.Deserialize<Docked>(ref reader)!;
case "LeaveBody":
return JsonSerializer.Deserialize<LeaveBody>(ref reader)!;
case "Progress":
return JsonSerializer.Deserialize<Progress>(ref reader)!;
case "SupercruiseExit":
return JsonSerializer.Deserialize<SupercruiseExit>(ref reader)!;
case "EngineerProgress":
return JsonSerializer.Deserialize<EngineerProgress>(ref reader)!;
case "DockingRequested":
return JsonSerializer.Deserialize<DockingRequested>(ref reader)!;
case "NpcCrewPaidWage":
return JsonSerializer.Deserialize<NpcCrewPaidWage>(ref reader)!;
case "SupercruiseEntry":
return JsonSerializer.Deserialize<SupercruiseEntry>(ref reader)!;
case "DockingGranted":
return JsonSerializer.Deserialize<DockingGranted>(ref reader)!;
case "StartJump":
return JsonSerializer.Deserialize<StartJump>(ref reader)!;
case "FSSAllBodiesFound":
return JsonSerializer.Deserialize<FSSAllBodiesFound>(ref reader)!;
case "FSSBodySignals":
return JsonSerializer.Deserialize<FSSBodySignals>(ref reader)!;
case "Liftoff":
return JsonSerializer.Deserialize<Liftoff>(ref reader)!;
case "SupercruiseDestinationDrop":
return JsonSerializer.Deserialize<SupercruiseDestinationDrop>(ref reader)!;
case "FSDTarget":
return JsonSerializer.Deserialize<FSDTarget>(ref reader)!;
case "FSDJump":
return JsonSerializer.Deserialize<FSDJump>(ref reader)!;
case "CodexEntry":
return JsonSerializer.Deserialize<CodexEntry>(ref reader)!;
case "HullDamage":
return JsonSerializer.Deserialize<HullDamage>(ref reader)!;
case "MaterialCollected":
return JsonSerializer.Deserialize<MaterialCollected>(ref reader)!;
case "NavRoute":
return JsonSerializer.Deserialize<NavRoute>(ref reader)!;
case "NavRouteClear":
return JsonSerializer.Deserialize<NavRouteClear>(ref reader)!;
case "ScanBaryCentre":
return JsonSerializer.Deserialize<ScanBaryCentre>(ref reader)!;
case "JetConeBoost":
return JsonSerializer.Deserialize<JetConeBoost>(ref reader)!;
case "Shutdown":
return JsonSerializer.Deserialize<Shutdown>(ref reader)!;
case "FuelScoop":
return JsonSerializer.Deserialize<FuelScoop>(ref reader)!;
case "FSSDiscoveryScan":
return JsonSerializer.Deserialize<FSSDiscoveryScan>(ref reader)!;
case "ModuleInfo":
return JsonSerializer.Deserialize<ModuleInfo>(ref reader)!;
case "ShipTargeted":
return JsonSerializer.Deserialize<ShipTargeted>(ref reader)!;
case "AfmuRepairs":
return JsonSerializer.Deserialize<AfmuRepairs>(ref reader)!;
case "HeatWarning":
return JsonSerializer.Deserialize<HeatWarning>(ref reader)!;
case "ModuleBuy":
return JsonSerializer.Deserialize<ModuleBuy>(ref reader)!;
case "BuyDrones":
return JsonSerializer.Deserialize<BuyDrones>(ref reader)!;
case "ShieldState":
return JsonSerializer.Deserialize<ShieldState>(ref reader)!;
case "BuyAmmo":
return JsonSerializer.Deserialize<BuyAmmo>(ref reader)!;
case "EjectCargo":
return JsonSerializer.Deserialize<EjectCargo>(ref reader)!;
case "ApproachBody":
return JsonSerializer.Deserialize<ApproachBody>(ref reader)!;
case "DockSRV":
return JsonSerializer.Deserialize<DockSRV>(ref reader)!;
case "Touchdown":
return JsonSerializer.Deserialize<Touchdown>(ref reader)!;
case "SAASignalsFound":
return JsonSerializer.Deserialize<SAASignalsFound>(ref reader)!;
case "EngineerCraft":
return JsonSerializer.Deserialize<EngineerCraft>(ref reader)!;
case "MaterialTrade":
return JsonSerializer.Deserialize<MaterialTrade>(ref reader)!;
case "Repair":
return JsonSerializer.Deserialize<Repair>(ref reader)!;
case "RefuelAll":
return JsonSerializer.Deserialize<RefuelAll>(ref reader)!;
case "StoredModules":
return JsonSerializer.Deserialize<StoredModules>(ref reader)!;
case "Synthesis":
return JsonSerializer.Deserialize<Synthesis>(ref reader)!;
case "Scanned":
return JsonSerializer.Deserialize<Scanned>(ref reader)!;
case "SendText":
return JsonSerializer.Deserialize<SendText>(ref reader)!;
case "Embark":
return JsonSerializer.Deserialize<Embark>(ref reader)!;
case "MultiSellExplorationData":
return JsonSerializer.Deserialize<MultiSellExplorationData>(ref reader)!;
case "Backpack":
return JsonSerializer.Deserialize<BackpackMaterials>(ref reader)!;
case "ModuleSell":
return JsonSerializer.Deserialize<ModuleSell>(ref reader)!;
case "Undocked":
return JsonSerializer.Deserialize<Undocked>(ref reader)!;
case "RepairAll":
return JsonSerializer.Deserialize<RepairAll>(ref reader)!;
case "Outfitting":
return JsonSerializer.Deserialize<Outfitting>(ref reader)!;
case "PowerplaySalary":
return JsonSerializer.Deserialize<PowerplaySalary>(ref reader)!;
case "RedeemVoucher":
return JsonSerializer.Deserialize<RedeemVoucher>(ref reader)!;
case "SAAScanComplete":
return JsonSerializer.Deserialize<SAAScanComplete>(ref reader)!;
case "Friends":
return JsonSerializer.Deserialize<Friends>(ref reader)!;
case "LaunchSRV":
return JsonSerializer.Deserialize<LaunchSRV>(ref reader)!;
case "SuitLoadout":
return JsonSerializer.Deserialize<SuitLoadout>(ref reader)!;
case "Disembark":
return JsonSerializer.Deserialize<Disembark>(ref reader)!;
case "MaterialDiscovered":
return JsonSerializer.Deserialize<MaterialDiscovered>(ref reader)!;
case "StoredShips":
return JsonSerializer.Deserialize<StoredShips>(ref reader)!;
case "ScanOrganic":
return JsonSerializer.Deserialize<ScanOrganic>(ref reader)!;
case "Market":
return JsonSerializer.Deserialize<Market>(ref reader)!;
case "MissionCompleted":
return JsonSerializer.Deserialize<MissionCompleted>(ref reader)!;
case "SellShipOnRebuy":
return JsonSerializer.Deserialize<SellShipOnRebuy>(ref reader)!;
case "MissionAccepted":
return JsonSerializer.Deserialize<MissionAccepted>(ref reader)!;
case "ApproachSettlement":
return JsonSerializer.Deserialize<ApproachSettlement>(ref reader)!;
case "Screenshot":
return JsonSerializer.Deserialize<Screenshot>(ref reader)!;
case "ModuleSwap":
return JsonSerializer.Deserialize<ModuleSwap>(ref reader)!;
case "UnderAttack":
return JsonSerializer.Deserialize<UnderAttack>(ref reader)!;
case "DataScanned":
return JsonSerializer.Deserialize<DataScanned>(ref reader)!;
case "DockingDenied":
return JsonSerializer.Deserialize<DockingDenied>(ref reader)!;
case "FetchRemoteModule":
return JsonSerializer.Deserialize<FetchRemoteModule>(ref reader)!;
case "EngineerContribution":
return JsonSerializer.Deserialize<EngineerContribution>(ref reader)!;
case "CollectCargo":
return JsonSerializer.Deserialize<CollectCargo>(ref reader)!;
case "ModuleRetrieve":
return JsonSerializer.Deserialize<ModuleRetrieve>(ref reader)!;
case "MarketBuy":
return JsonSerializer.Deserialize<MarketBuy>(ref reader)!;
case "SellDrones":
return JsonSerializer.Deserialize<SellDrones>(ref reader)!;
case "Interdicted":
return JsonSerializer.Deserialize<Interdicted>(ref reader)!;
case "SellOrganicData":
return JsonSerializer.Deserialize<SellOrganicData>(ref reader)!;
case "WingAdd":
return JsonSerializer.Deserialize<WingAdd>(ref reader)!;
case "WingInvite":
return JsonSerializer.Deserialize<WingInvite>(ref reader)!;
case "WingJoin":
return JsonSerializer.Deserialize<WingJoin>(ref reader)!;
case "WingLeave":
return JsonSerializer.Deserialize<WingLeave>(ref reader)!;
case "Bounty":
return JsonSerializer.Deserialize<Bounty>(ref reader)!;
case "CommitCrime":
return JsonSerializer.Deserialize<CommitCrime>(ref reader)!;
case "ModuleStore":
return JsonSerializer.Deserialize<ModuleStore>(ref reader)!;
case "FactionKillBond":
return JsonSerializer.Deserialize<FactionKillBond>(ref reader)!;
case "RebootRepair":
return JsonSerializer.Deserialize<RebootRepair>(ref reader)!;
case "LaunchDrone":
return JsonSerializer.Deserialize<LaunchDrone>(ref reader)!;
case "SellMicroResources":
return JsonSerializer.Deserialize<SellMicroResources>(ref reader)!;
case "NavBeaconScan":
return JsonSerializer.Deserialize<NavBeaconScan>(ref reader)!;
case "SearchAndRescue":
return JsonSerializer.Deserialize<SearchAndRescue>(ref reader)!;
case "MarketSell":
return JsonSerializer.Deserialize<MarketSell>(ref reader)!;
default:
logger.LogWarning("Unknown Journal event type {EventName}", eventName);
return JsonSerializer.Deserialize<JournalBase>(ref reader)!;
}
}
public override void Write(Utf8JsonWriter writer, JournalBase value, JsonSerializerOptions options)
{
throw new NotSupportedException();
}
}