Compare commits
8 Commits
ec41c718b1
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 38807cc198 | |||
| 26bf6f5e02 | |||
| a3ddb00b69 | |||
| 82dc16fb1b | |||
| ed68876300 | |||
| f21bf5ea5e | |||
| f14c841e51 | |||
| 1b9635ccfe |
14
EDJournalWatcher/EDJournalWatcher.csproj
Normal file
14
EDJournalWatcher/EDJournalWatcher.csproj
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\EDPlayerJournal\EDPlayerJournal.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
22
EDJournalWatcher/Program.cs
Normal file
22
EDJournalWatcher/Program.cs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
using EDPlayerJournal;
|
||||||
|
using EDPlayerJournal.Entries;
|
||||||
|
|
||||||
|
namespace EDJournalWatcher;
|
||||||
|
|
||||||
|
public class Program {
|
||||||
|
public static void Main(string[] args) {
|
||||||
|
PlayerJournal journal = new();
|
||||||
|
JournalStream stream = new(journal);
|
||||||
|
|
||||||
|
stream.NewJournalEntry += Stream_NewJournalEntry;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
stream.ProcessQueues();
|
||||||
|
Thread.Sleep(100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void Stream_NewJournalEntry(Entry entry) {
|
||||||
|
Console.WriteLine(entry.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
34
EDPlayerJournal.sln
Normal file
34
EDPlayerJournal.sln
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.0.31903.59
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EDPlayerJournal", "EDPlayerJournal\EDPlayerJournal.csproj", "{29AD2532-805C-B878-9766-CF9FE22C41E9}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EDPlayerJournalTests", "EDPlayerJournalTests\EDPlayerJournalTests.csproj", "{FAFE4437-B1AE-0255-B1C1-6350A81816F1}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EDJournalWatcher", "EDJournalWatcher\EDJournalWatcher.csproj", "{DF502BDA-F888-476E-A3B9-CD857359FA7E}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{29AD2532-805C-B878-9766-CF9FE22C41E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{29AD2532-805C-B878-9766-CF9FE22C41E9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{29AD2532-805C-B878-9766-CF9FE22C41E9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{29AD2532-805C-B878-9766-CF9FE22C41E9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{FAFE4437-B1AE-0255-B1C1-6350A81816F1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{FAFE4437-B1AE-0255-B1C1-6350A81816F1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{FAFE4437-B1AE-0255-B1C1-6350A81816F1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{FAFE4437-B1AE-0255-B1C1-6350A81816F1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{DF502BDA-F888-476E-A3B9-CD857359FA7E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{DF502BDA-F888-476E-A3B9-CD857359FA7E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{DF502BDA-F888-476E-A3B9-CD857359FA7E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{DF502BDA-F888-476E-A3B9-CD857359FA7E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
56
EDPlayerJournal/CommanderContext/CommanderContext.cs
Normal file
56
EDPlayerJournal/CommanderContext/CommanderContext.cs
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
using EDPlayerJournal.Entries;
|
||||||
|
|
||||||
|
namespace EDPlayerJournal.CommanderContext;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A class that uses various journal entries to provide a (hopefully)
|
||||||
|
/// up to date summary of the current commander in game.
|
||||||
|
///
|
||||||
|
/// Data in this context may be null, in which case the current status of
|
||||||
|
/// that information is not yet known. For example if the player died, but
|
||||||
|
/// they have't respawned yet, their location might be null.
|
||||||
|
/// </summary>
|
||||||
|
public class CommanderContext {
|
||||||
|
private static Dictionary<string, ICommanderParser> parsers = new() {
|
||||||
|
{ Events.Commander, new CommanderParser() },
|
||||||
|
{ Events.Docked, new DockedParser() },
|
||||||
|
{ Events.FSDJump, new FSDJumpParser() },
|
||||||
|
{ Events.Location, new LocationParser() },
|
||||||
|
{ Events.Undocked, new UndockedParser() },
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Name of the commander.
|
||||||
|
/// </summary>
|
||||||
|
public string? Name { get; set; } = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the commander is currently docked.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsDocked { get; set; } = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Current station
|
||||||
|
/// </summary>
|
||||||
|
public Station? Station { get; set; } = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Current star system
|
||||||
|
/// </summary>
|
||||||
|
public StarSystem? System { get; set; } = null;
|
||||||
|
|
||||||
|
public void Update(Entry entry) {
|
||||||
|
if (string.IsNullOrEmpty(entry.Event)) {
|
||||||
|
throw new CommanderParserException("invalid or empty event given");
|
||||||
|
}
|
||||||
|
|
||||||
|
string ev = entry.Event;
|
||||||
|
|
||||||
|
if (!parsers.ContainsKey(ev)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ICommanderParser parser = parsers[ev];
|
||||||
|
parser.Parse(entry, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
15
EDPlayerJournal/CommanderContext/CommanderParser.cs
Normal file
15
EDPlayerJournal/CommanderContext/CommanderParser.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
using EDPlayerJournal.Entries;
|
||||||
|
|
||||||
|
namespace EDPlayerJournal.CommanderContext;
|
||||||
|
|
||||||
|
internal class CommanderParser : ICommanderParser {
|
||||||
|
public void Parse(Entry entry, CommanderContext ctx) {
|
||||||
|
CommanderEntry? e = entry as CommanderEntry;
|
||||||
|
|
||||||
|
if (e == null) {
|
||||||
|
throw new CommanderParserException("invalid entry");
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Name = e.Name;
|
||||||
|
}
|
||||||
|
}
|
||||||
16
EDPlayerJournal/CommanderContext/DockedParser.cs
Normal file
16
EDPlayerJournal/CommanderContext/DockedParser.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
using EDPlayerJournal.Entries;
|
||||||
|
|
||||||
|
namespace EDPlayerJournal.CommanderContext;
|
||||||
|
|
||||||
|
internal class DockedParser : ICommanderParser {
|
||||||
|
public void Parse(Entry entry, CommanderContext ctx) {
|
||||||
|
DockedEntry? e = entry as DockedEntry;
|
||||||
|
|
||||||
|
if (e == null) {
|
||||||
|
throw new CommanderParserException("invalid entry");
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Station = Station.ParseEntry(e);
|
||||||
|
ctx.IsDocked = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
15
EDPlayerJournal/CommanderContext/FSDJumpParser.cs
Normal file
15
EDPlayerJournal/CommanderContext/FSDJumpParser.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
using EDPlayerJournal.Entries;
|
||||||
|
|
||||||
|
namespace EDPlayerJournal.CommanderContext;
|
||||||
|
|
||||||
|
internal class FSDJumpParser : ICommanderParser {
|
||||||
|
public void Parse(Entry entry, CommanderContext ctx) {
|
||||||
|
FSDJumpEntry? e = entry as FSDJumpEntry;
|
||||||
|
|
||||||
|
if (e == null) {
|
||||||
|
throw new CommanderParserException("invalid entry");
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.System = StarSystem.ParseEntry(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
19
EDPlayerJournal/CommanderContext/ICommanderParser.cs
Normal file
19
EDPlayerJournal/CommanderContext/ICommanderParser.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using EDPlayerJournal;
|
||||||
|
using EDPlayerJournal.Entries;
|
||||||
|
|
||||||
|
namespace EDPlayerJournal.CommanderContext;
|
||||||
|
|
||||||
|
public class CommanderParserException : ApplicationException {
|
||||||
|
public CommanderParserException() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommanderParserException(string message) : base(message) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommanderParserException(string message, params object?[] args) : base(string.Format(message, args)) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal interface ICommanderParser {
|
||||||
|
void Parse(Entry entry, CommanderContext ctx);
|
||||||
|
}
|
||||||
17
EDPlayerJournal/CommanderContext/LocationParser.cs
Normal file
17
EDPlayerJournal/CommanderContext/LocationParser.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
using EDPlayerJournal.Entries;
|
||||||
|
|
||||||
|
namespace EDPlayerJournal.CommanderContext;
|
||||||
|
|
||||||
|
internal class LocationParser : ICommanderParser {
|
||||||
|
public void Parse(Entry entry, CommanderContext ctx) {
|
||||||
|
LocationEntry? e = entry as LocationEntry;
|
||||||
|
|
||||||
|
if (e == null) {
|
||||||
|
throw new CommanderParserException("invalid entry");
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Station = Station.ParseEntry(e);
|
||||||
|
ctx.System = StarSystem.ParseEntry(e);
|
||||||
|
ctx.IsDocked = e.Docked;
|
||||||
|
}
|
||||||
|
}
|
||||||
16
EDPlayerJournal/CommanderContext/UndockedParser.cs
Normal file
16
EDPlayerJournal/CommanderContext/UndockedParser.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
using EDPlayerJournal.Entries;
|
||||||
|
|
||||||
|
namespace EDPlayerJournal.CommanderContext;
|
||||||
|
|
||||||
|
internal class UndockedParser : ICommanderParser {
|
||||||
|
public void Parse(Entry entry, CommanderContext ctx) {
|
||||||
|
UndockedEntry? e = entry as UndockedEntry;
|
||||||
|
|
||||||
|
if (e == null) {
|
||||||
|
throw new CommanderParserException("invalid entry");
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Station = null;
|
||||||
|
ctx.IsDocked = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
namespace EDPlayerJournal.Entries;
|
||||||
|
|
||||||
|
public class ConstructionResource {
|
||||||
|
public string Name { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string NameLocalised { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public ulong RequiredAmount { get; set; } = 0;
|
||||||
|
|
||||||
|
public ulong ProvidedAmount { get; set; } = 0;
|
||||||
|
|
||||||
|
public ulong Payment { get; set; } = 0;
|
||||||
|
|
||||||
|
public static ConstructionResource FromJSON(JObject obj) {
|
||||||
|
ConstructionResource res = new();
|
||||||
|
|
||||||
|
res.Name = obj.Value<string?>("Name") ?? string.Empty;
|
||||||
|
res.NameLocalised = obj.Value<string?>("Name_Localised") ?? string.Empty;
|
||||||
|
res.RequiredAmount = obj.Value<ulong?>("RequiredAmount") ?? 0;
|
||||||
|
res.ProvidedAmount = obj.Value<ulong?>("ProvidedAmount") ?? 0;
|
||||||
|
res.Payment = obj.Value<ulong?>("Payment") ?? 0;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ColonisationConstructionDepotEntry : Entry {
|
||||||
|
public ulong MarketID { get; set; } = 0;
|
||||||
|
|
||||||
|
public double ConstructionProgress { get; set; } = 0.0;
|
||||||
|
|
||||||
|
public double ConstructionProgressInPercent {
|
||||||
|
get {
|
||||||
|
return ConstructionProgress * 100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ConstructionComplete { get; set; } = false;
|
||||||
|
|
||||||
|
public bool ConstructionFailed { get; set; } = false;
|
||||||
|
|
||||||
|
public List<ConstructionResource> ResourcesRequired { get; set; } = new();
|
||||||
|
|
||||||
|
protected override void Initialise() {
|
||||||
|
MarketID = JSON.Value<ulong?>("MarketID") ?? 0;
|
||||||
|
ConstructionProgress = JSON.Value<double?>("ConstructionProgress") ?? 0;
|
||||||
|
ConstructionComplete = JSON.Value<bool?>("ConstructionComplete") ?? false;
|
||||||
|
ConstructionFailed = JSON.Value<bool?>("ConstructionFailed") ?? false;
|
||||||
|
|
||||||
|
JArray? resources = JSON.Value<JArray?>("ResourcesRequired");
|
||||||
|
if (resources != null) {
|
||||||
|
foreach (JObject res in resources) {
|
||||||
|
ConstructionResource resource = ConstructionResource.FromJSON(res);
|
||||||
|
ResourcesRequired.Add(resource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
39
EDPlayerJournal/Entries/ColonisationContributionEntry.cs
Normal file
39
EDPlayerJournal/Entries/ColonisationContributionEntry.cs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
namespace EDPlayerJournal.Entries;
|
||||||
|
|
||||||
|
public class ConstructionContribution {
|
||||||
|
public string Name { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string NameLocalised { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public ulong Amount { get; set; } = 0;
|
||||||
|
|
||||||
|
public static ConstructionContribution FromJSON(JObject obj) {
|
||||||
|
ConstructionContribution res = new();
|
||||||
|
|
||||||
|
res.Name = obj.Value<string?>("Name") ?? string.Empty;
|
||||||
|
res.NameLocalised = obj.Value<string?>("Name_Localised") ?? string.Empty;
|
||||||
|
res.Amount = obj.Value<ulong?>("Amount") ?? 0;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ColonisationContributionEntry : Entry {
|
||||||
|
public ulong MarketID { get; set; } = 0;
|
||||||
|
|
||||||
|
public List<ConstructionContribution> Contributions { get; set; } = new();
|
||||||
|
|
||||||
|
protected override void Initialise() {
|
||||||
|
MarketID = JSON.Value<ulong?>("MarketID") ?? 0;
|
||||||
|
|
||||||
|
JArray? resources = JSON.Value<JArray?>("Contributions");
|
||||||
|
if (resources != null) {
|
||||||
|
foreach (JObject res in resources) {
|
||||||
|
ConstructionContribution resource = ConstructionContribution.FromJSON(res);
|
||||||
|
Contributions.Add(resource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,6 +16,8 @@ public class Entry {
|
|||||||
{ Events.Bounty, typeof(BountyEntry) },
|
{ Events.Bounty, typeof(BountyEntry) },
|
||||||
{ Events.CapShipBond, typeof(CapShipBondEntry) },
|
{ Events.CapShipBond, typeof(CapShipBondEntry) },
|
||||||
{ Events.CarrierJump, typeof(CarrierJump) },
|
{ Events.CarrierJump, typeof(CarrierJump) },
|
||||||
|
{ Events.ColonisationConstructionDepot, typeof(ColonisationConstructionDepotEntry) },
|
||||||
|
{ Events.ColonisationContribution, typeof(ColonisationContributionEntry) },
|
||||||
{ Events.Commander, typeof(CommanderEntry) },
|
{ Events.Commander, typeof(CommanderEntry) },
|
||||||
{ Events.CommitCrime, typeof(CommitCrimeEntry) },
|
{ Events.CommitCrime, typeof(CommitCrimeEntry) },
|
||||||
{ Events.Died, typeof(DiedEntry) },
|
{ Events.Died, typeof(DiedEntry) },
|
||||||
@@ -57,6 +59,7 @@ public class Entry {
|
|||||||
{ Events.SupercruiseEntry, typeof(SupercruiseEntryEntry) },
|
{ Events.SupercruiseEntry, typeof(SupercruiseEntryEntry) },
|
||||||
{ Events.SupercruiseExit, typeof(SupercruiseExitEntry) },
|
{ Events.SupercruiseExit, typeof(SupercruiseExitEntry) },
|
||||||
{ Events.UnderAttack, typeof(UnderAttackEntry) },
|
{ Events.UnderAttack, typeof(UnderAttackEntry) },
|
||||||
|
{ Events.Undocked, typeof(UndockedEntry) },
|
||||||
};
|
};
|
||||||
|
|
||||||
private string? eventtype = null;
|
private string? eventtype = null;
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ public class Events {
|
|||||||
public static readonly string Bounty = "Bounty";
|
public static readonly string Bounty = "Bounty";
|
||||||
public static readonly string CapShipBond = "CapShipBond";
|
public static readonly string CapShipBond = "CapShipBond";
|
||||||
public static readonly string CarrierJump = "CarrierJump";
|
public static readonly string CarrierJump = "CarrierJump";
|
||||||
|
public static readonly string ColonisationConstructionDepot = "ColonisationConstructionDepot";
|
||||||
|
public static readonly string ColonisationContribution = "ColonisationContribution";
|
||||||
public static readonly string Commander = "Commander";
|
public static readonly string Commander = "Commander";
|
||||||
public static readonly string CommitCrime = "CommitCrime";
|
public static readonly string CommitCrime = "CommitCrime";
|
||||||
public static readonly string Died = "Died";
|
public static readonly string Died = "Died";
|
||||||
@@ -48,4 +50,5 @@ public class Events {
|
|||||||
public static readonly string SupercruiseEntry = "SupercruiseEntry";
|
public static readonly string SupercruiseEntry = "SupercruiseEntry";
|
||||||
public static readonly string SupercruiseExit = "SupercruiseExit";
|
public static readonly string SupercruiseExit = "SupercruiseExit";
|
||||||
public static readonly string UnderAttack = "UnderAttack";
|
public static readonly string UnderAttack = "UnderAttack";
|
||||||
|
public static readonly string Undocked = "Undocked";
|
||||||
}
|
}
|
||||||
|
|||||||
12
EDPlayerJournal/Entries/UndockedEntry.cs
Normal file
12
EDPlayerJournal/Entries/UndockedEntry.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
namespace EDPlayerJournal.Entries;
|
||||||
|
|
||||||
|
public class UndockedEntry : Entry {
|
||||||
|
/// <summary>
|
||||||
|
/// Name of the station
|
||||||
|
/// </summary>
|
||||||
|
public string? StationName { get; set; }
|
||||||
|
|
||||||
|
protected override void Initialise() {
|
||||||
|
StationName = JSON.Value<string>("StationName");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,150 @@
|
|||||||
using EDPlayerJournal.Entries;
|
using EDPlayerJournal.Entries;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace EDPlayerJournal;
|
namespace EDPlayerJournal;
|
||||||
|
|
||||||
|
public class SyncQueue<T> {
|
||||||
|
private Queue<T> queue = new();
|
||||||
|
|
||||||
|
public void Enqueue(T item) {
|
||||||
|
Monitor.Enter(queue);
|
||||||
|
try {
|
||||||
|
queue.Enqueue(item);
|
||||||
|
} finally {
|
||||||
|
Monitor.Exit(queue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public T? TryDequeue() {
|
||||||
|
if (Monitor.TryEnter(queue)) {
|
||||||
|
T retval;
|
||||||
|
|
||||||
|
try {
|
||||||
|
queue.TryDequeue(out retval);
|
||||||
|
} finally {
|
||||||
|
Monitor.Exit(queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
return default(T);
|
||||||
|
}
|
||||||
|
|
||||||
|
public T Dequeue() {
|
||||||
|
T retval;
|
||||||
|
|
||||||
|
Monitor.Enter(queue);
|
||||||
|
try {
|
||||||
|
retval = queue.Dequeue();
|
||||||
|
} finally {
|
||||||
|
Monitor.Exit(queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class FileWatcherThread : IDisposable {
|
||||||
|
private string? fullPath;
|
||||||
|
private StreamReader? stream;
|
||||||
|
private FileStream? file;
|
||||||
|
private Thread? thread;
|
||||||
|
private bool done = false;
|
||||||
|
private EventWaitHandle wait = new(false, EventResetMode.AutoReset);
|
||||||
|
|
||||||
|
public string? FullPath => fullPath;
|
||||||
|
|
||||||
|
private SyncQueue<Entry> queue;
|
||||||
|
|
||||||
|
public SyncQueue<Entry> Queue => queue;
|
||||||
|
|
||||||
|
public bool Finished => done;
|
||||||
|
|
||||||
|
public FileWatcherThread(SyncQueue<Entry> queue) {
|
||||||
|
this.queue = queue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FileWatcherThread(string fullpath, SyncQueue<Entry> queue) {
|
||||||
|
this.queue = queue;
|
||||||
|
Start(fullpath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Signal() {
|
||||||
|
wait.Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Start(string fullpath) {
|
||||||
|
fullPath = fullpath;
|
||||||
|
file = new FileStream(fullPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||||
|
stream = new StreamReader(file);
|
||||||
|
|
||||||
|
done = false;
|
||||||
|
thread = new Thread(ReaderThread);
|
||||||
|
thread.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReaderThread(object? args) {
|
||||||
|
while (!done) {
|
||||||
|
wait.WaitOne();
|
||||||
|
|
||||||
|
if (done || stream == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!done) {
|
||||||
|
var task = stream.ReadLineAsync();
|
||||||
|
if (!task.Wait(TimeSpan.FromMilliseconds(10))) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
string? line = task.Result;
|
||||||
|
if (line == null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Entry? entry = null;
|
||||||
|
try {
|
||||||
|
entry = Entry.Parse(line);
|
||||||
|
} catch (Exception) {
|
||||||
|
// TODO: error handling on wrong entries
|
||||||
|
}
|
||||||
|
if (entry != null) {
|
||||||
|
queue.Enqueue(entry);
|
||||||
|
if (entry.Is(Events.Shutdown)) {
|
||||||
|
done = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Close() {
|
||||||
|
if (thread != null) {
|
||||||
|
done = true;
|
||||||
|
Signal();
|
||||||
|
thread.Join();
|
||||||
|
thread = null;
|
||||||
|
}
|
||||||
|
if (stream != null) {
|
||||||
|
stream.Close();
|
||||||
|
stream = null;
|
||||||
|
}
|
||||||
|
if (file != null) {
|
||||||
|
file.Close();
|
||||||
|
file = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void IDisposable.Dispose() {
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public class JournalStream {
|
public class JournalStream {
|
||||||
private FileSystemWatcher? watcher = null;
|
private FileSystemWatcher? watcher = null;
|
||||||
|
|
||||||
private Dictionary<string, StreamReader> streams = new Dictionary<string, StreamReader>();
|
private Dictionary<string, FileWatcherThread> streams = new();
|
||||||
|
|
||||||
public delegate void NewJournalEntryDelegate(Entry entry);
|
public delegate void NewJournalEntryDelegate(Entry entry);
|
||||||
|
|
||||||
@@ -12,6 +152,10 @@ public class JournalStream {
|
|||||||
|
|
||||||
public PlayerJournal? Journal;
|
public PlayerJournal? Journal;
|
||||||
|
|
||||||
|
private SyncQueue<Entry> queue = new();
|
||||||
|
|
||||||
|
public SyncQueue<Entry> Queue => queue;
|
||||||
|
|
||||||
public JournalStream() {
|
public JournalStream() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -21,14 +165,54 @@ public class JournalStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void AddFileToStreams(string path, bool seekend = false) {
|
private void AddFileToStreams(string path, bool seekend = false) {
|
||||||
if (!streams.ContainsKey(path) && JournalFile.VerifyFile(path)) {
|
if (!JournalFile.VerifyFile(path)) {
|
||||||
var filestream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
return;
|
||||||
streams[path] = new StreamReader(filestream);
|
}
|
||||||
|
|
||||||
if (seekend) {
|
if (streams.ContainsKey(path)) {
|
||||||
streams[path].BaseStream.Seek(0, SeekOrigin.End);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileWatcherThread thread = new(path, queue);
|
||||||
|
streams[path] = thread;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CleanupStreams() {
|
||||||
|
var toRemove = streams
|
||||||
|
.Where(x => (x.Value != null && x.Value.Finished) || x.Value == null)
|
||||||
|
.Select(x => x)
|
||||||
|
;
|
||||||
|
foreach (var stream in toRemove) {
|
||||||
|
if (stream.Value != null) {
|
||||||
|
stream.Value.Close();
|
||||||
|
}
|
||||||
|
streams.Remove(stream.Key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Processes all file watcher thread queues in the current thread. This must be
|
||||||
|
/// called from the main event loop sometimes to make sure all NewJournalEntry
|
||||||
|
/// delegates fire correctly.
|
||||||
|
/// </summary>
|
||||||
|
public void ProcessQueues() {
|
||||||
|
Entry? entry;
|
||||||
|
|
||||||
|
while ((entry = queue.TryDequeue()) != null) {
|
||||||
|
NewJournalEntry?.Invoke(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
CleanupStreams();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ProcessQueuesWait() {
|
||||||
|
Entry? entry;
|
||||||
|
|
||||||
|
while ((entry = queue.Dequeue()) != null) {
|
||||||
|
NewJournalEntry?.Invoke(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
CleanupStreams();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Open() {
|
public void Open() {
|
||||||
@@ -64,6 +248,9 @@ public class JournalStream {
|
|||||||
watcher.EnableRaisingEvents = false;
|
watcher.EnableRaisingEvents = false;
|
||||||
}
|
}
|
||||||
watcher = null;
|
watcher = null;
|
||||||
|
foreach (var stream in streams) {
|
||||||
|
stream.Value.Close();
|
||||||
|
}
|
||||||
streams.Clear();
|
streams.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,16 +266,6 @@ public class JournalStream {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
string? line;
|
stream.Signal();
|
||||||
|
|
||||||
while ((line = stream.ReadLine()) != null) {
|
|
||||||
try {
|
|
||||||
Entry? entry = Entry.Parse(line);
|
|
||||||
if (entry != null) {
|
|
||||||
NewJournalEntry?.Invoke(entry);
|
|
||||||
}
|
|
||||||
} catch (Exception) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
40
EDPlayerJournal/StarSystem.cs
Normal file
40
EDPlayerJournal/StarSystem.cs
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
using EDPlayerJournal.Entries;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
namespace EDPlayerJournal;
|
||||||
|
|
||||||
|
public class StarSystem {
|
||||||
|
public string Name { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public double[] Position { get; set; } = new double[3] { 0.0, 0.0, 0.0 };
|
||||||
|
|
||||||
|
public ulong Address { get; set; } = 0;
|
||||||
|
|
||||||
|
public ulong Population { get; set; } = 0;
|
||||||
|
|
||||||
|
public string Allegiance { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public static StarSystem? ParseEntry(Entry e) {
|
||||||
|
if (!(e.Is(Events.Location) || e.Is(Events.FSDJump))) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return ParseJSON(e.JSON);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static StarSystem? ParseJSON(JObject obj) {
|
||||||
|
StarSystem s = new();
|
||||||
|
|
||||||
|
s.Name = obj.Value<string?>("StarSystem") ?? string.Empty;
|
||||||
|
var pos = obj.Value<JArray?>("StarPos");
|
||||||
|
if (pos != null) {
|
||||||
|
s.Position = pos.Select(x => (double)x).ToArray();
|
||||||
|
}
|
||||||
|
s.Address = obj.Value<ulong?>("SystemAddress") ?? 0;
|
||||||
|
s.Population = obj.Value<ulong?>("Population") ?? 0;
|
||||||
|
|
||||||
|
// TODO: more info
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
65
EDPlayerJournal/Station.cs
Normal file
65
EDPlayerJournal/Station.cs
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
using EDPlayerJournal.Entries;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
namespace EDPlayerJournal;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a station ingame
|
||||||
|
/// </summary>
|
||||||
|
public class Station {
|
||||||
|
public string SystemName { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public ulong SystemAddress { get; set; } = 0;
|
||||||
|
|
||||||
|
public string Name { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string Type { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public ulong MarketID { get; set; } = 0;
|
||||||
|
|
||||||
|
public string Faction { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string Government { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string GovernmentLocalised { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public List<string> Services { get; set; } = new();
|
||||||
|
|
||||||
|
public static Station? ParseEntry(Entry e) {
|
||||||
|
if (!(e.Is(Events.Docked) || e.Is(Events.Location))) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var s = ParseJSON(e.JSON);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Station? ParseJSON(JObject obj) {
|
||||||
|
Station s = new();
|
||||||
|
|
||||||
|
s.Name = obj.Value<string?>("StationName") ?? string.Empty;
|
||||||
|
s.Type = obj.Value<string?>("StationType") ?? string.Empty;
|
||||||
|
s.MarketID = obj.Value<ulong?>("MarketID") ?? 0;
|
||||||
|
|
||||||
|
var faction = obj.Value<JObject?>("StationFaction");
|
||||||
|
if (faction != null) {
|
||||||
|
s.Faction = faction.Value<string?>("Name") ?? string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Government = obj.Value<string?>("StationGovernment") ?? string.Empty;
|
||||||
|
s.GovernmentLocalised = obj.Value<string?>("StationGovernment_Localised") ?? string.Empty;
|
||||||
|
|
||||||
|
var services = obj.Value<JArray?>("StationServices");
|
||||||
|
if (services != null) {
|
||||||
|
s.Services = services
|
||||||
|
.Select(x => x.ToString())
|
||||||
|
.ToList()
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: more info
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user