Skip to content

Commit fa37d73

Browse files
committed
✨ event handling system
1 parent 3155bba commit fa37d73

6 files changed

Lines changed: 212 additions & 42 deletions

File tree

Console/Program.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
using EliteApi;
1+
using EliteAPI;
22
using EliteAPI.Journals;
3-
using EliteAPI.Json;
3+
4+
var api = new EliteDangerousApi();
5+
api.OnAllJson(e => Console.WriteLine($"{e.eventName}: {e.json}"));
46

57
var json = await File.ReadAllTextAsync("./EliteAPI.Tests/TestFiles/Status/StatusCombat.json");
68
var paths = JournalUtils.ToPaths(json);

EliteAPI/EliteDangerousApi.cs

Lines changed: 158 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,23 @@
11
using System;
22
using System.Collections.Generic;
3-
using System.Collections.ObjectModel;
43
using System.Linq;
4+
using EliteAPI.Events;
55
using EliteAPI.Journals;
66
using EliteAPI.Json;
77
using EliteAPI.Watcher;
8+
using Newtonsoft.Json;
89

9-
namespace EliteApi;
10+
namespace EliteAPI;
1011

1112
public class EliteDangerousApi
1213
{
1314
private readonly List<FileWatcher> _watchers;
1415

16+
private readonly List<Action<IEvent>> _typedGlobalEventHandlers = [];
17+
private readonly List<Action<(string eventName, string json)>> _untypedGlobalEventHandlers = [];
18+
private readonly Dictionary<string, List<Action<IEvent>>> _typedEventHandlers = [];
19+
private readonly Dictionary<string, List<Action<(string eventName, string json)>>> _untypedEventHandlers = [];
20+
1521
public EliteDangerousApi()
1622
{
1723
var journalDirectory = JournalUtils.GetJournalsDirectory();
@@ -34,20 +40,162 @@ public EliteDangerousApi()
3440

3541
public void Start()
3642
{
43+
_watchers.ForEach(w => w.OnContentChanged(json => Invoke(json)));
3744
_watchers.ForEach(w => w.StartWatching());
3845
}
3946

40-
public void OnPathsChanged(Action<(string command, List<JsonPath> variables)> onPathsChanged)
47+
/// <summary>
48+
/// Listens for a specific event of <typeparamref name="TEvent"/>.
49+
/// </summary>
50+
public void On<TEvent>(Action<TEvent> handler)
51+
where TEvent : IEvent
52+
{
53+
var eventName = typeof(TEvent).GetType().Name;
54+
55+
// remove "Event" suffix
56+
if (eventName.EndsWith("Event"))
57+
eventName = eventName[..^5];
58+
59+
// initialize list if not exists
60+
if (!_typedEventHandlers.ContainsKey(eventName))
61+
_typedEventHandlers[eventName] = [];
62+
63+
// add handler
64+
_typedEventHandlers[eventName].Add(e => handler((TEvent)e));
65+
}
66+
67+
/// <summary>
68+
/// Listens for a specific event with the event name of <paramref name="eventName"/> and provides the raw JSON.
69+
/// </summary>
70+
public void OnJson(string eventName, Action<(string eventName, string json)> handler)
71+
{
72+
// remove "Event" suffix
73+
if (eventName.EndsWith("Event"))
74+
eventName = eventName[..^5];
75+
76+
// initialize list if not exists
77+
if (!_untypedEventHandlers.ContainsKey(eventName))
78+
_untypedEventHandlers[eventName] = [];
79+
80+
// add handler
81+
_untypedEventHandlers[eventName].Add(handler);
82+
}
83+
84+
/// <summary>
85+
/// Listens for all events which are typed.
86+
/// </summary>
87+
public void OnAll(Action<IEvent> handler)
88+
{
89+
_typedGlobalEventHandlers.Add(handler);
90+
}
91+
92+
/// <summary>
93+
/// Listens for all events with raw JSON
94+
/// </summary>
95+
public void OnAllJson(Action<(string eventName, string json)> handler)
96+
{
97+
_untypedGlobalEventHandlers.Add(handler);
98+
}
99+
100+
internal void Invoke(IEvent @event)
101+
{
102+
Invoke(JsonConvert.SerializeObject(@event), @event);
103+
}
104+
105+
internal void Invoke(string json) => Invoke(json, null);
106+
107+
internal void Invoke(string json, IEvent? @event)
41108
{
42-
_watchers.ForEach(w =>
109+
var eventName = JsonUtils.GetEventName(json);
110+
if (string.IsNullOrEmpty(eventName))
111+
{
112+
// TODO: proper logging system for this
113+
Console.WriteLine("Skipping invalid event with no name.");
114+
return;
115+
}
116+
117+
if (@event == null && _typedEventHandlers.ContainsKey(eventName))
118+
{
119+
var eventType = _typedEventHandlers[eventName].First().Method.GetParameters()[0].ParameterType;
120+
@event = JsonConvert.DeserializeObject(json, eventType) as IEvent;
121+
122+
if (@event == null)
123+
{
124+
// TODO: proper logging
125+
Console.WriteLine($"Failed to deserialize event {eventName} to type {eventType.Name}.");
126+
return;
127+
}
128+
}
129+
130+
// invoke untyped handlers
131+
if (_untypedEventHandlers.ContainsKey(eventName))
132+
{
133+
foreach (var handler in _untypedEventHandlers[eventName])
134+
{
135+
try
136+
{
137+
handler((eventName, json));
138+
}
139+
catch (Exception ex)
140+
{
141+
// TODO: proper logging
142+
Console.WriteLine($"Error invoking handler for event {eventName}: {ex}");
143+
}
144+
}
145+
}
146+
147+
// invoke global untyped handlers
148+
foreach (var handler in _untypedGlobalEventHandlers)
149+
{
150+
try
151+
{
152+
handler((eventName, json));
153+
}
154+
catch (Exception ex)
155+
{
156+
// TODO: proper logging
157+
Console.WriteLine($"Error invoking global untyped handler: {ex}");
158+
}
159+
}
160+
161+
if (@event != null)
43162
{
44-
w.OnContentChanged(async json =>
163+
// invoke typed handlers
164+
if (_typedEventHandlers.ContainsKey(eventName))
45165
{
46-
var paths = JournalUtils.ToPaths(json);
47-
var command = JournalUtils.GetEventName(json);
166+
foreach (var handler in _typedEventHandlers[eventName])
167+
{
168+
try
169+
{
170+
handler(@event);
171+
}
172+
catch (Exception ex)
173+
{
174+
// TODO: proper logging
175+
Console.WriteLine($"Error invoking handler for event {eventName}: {ex}");
176+
}
177+
}
178+
}
48179

49-
onPathsChanged((command, paths));
50-
});
51-
});
180+
// invoke global typed handlers
181+
foreach (var handler in _typedGlobalEventHandlers)
182+
{
183+
try
184+
{
185+
handler(@event);
186+
}
187+
catch (Exception ex)
188+
{
189+
// TODO: proper logging
190+
Console.WriteLine($"Error invoking global typed handler: {ex}");
191+
}
192+
}
193+
}
194+
}
195+
196+
public void OnJson(Action<object, object> value)
197+
{
198+
throw new NotImplementedException();
52199
}
53200
}
201+

EliteAPI/Events/IEvent.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
using System;
2+
3+
namespace EliteAPI.Events;
4+
5+
public interface IEvent
6+
{
7+
public string Name { get; }
8+
public DateTime Timestamp { get; }
9+
}

EliteAPI/Journals/JournalUtils.cs

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,9 @@ namespace EliteAPI.Journals;
1010

1111
public static class JournalUtils
1212
{
13-
public static string GetEventName(string json)
14-
{
15-
var obj = JObject.Parse(json);
16-
if (obj.TryGetValue("event", out var eventNameToken))
17-
return eventNameToken.ToString();
18-
19-
return string.Empty;
20-
}
21-
2213
public static List<JsonPath> ToPaths(string json)
2314
{
24-
var eventName = GetEventName(json);
15+
var eventName = JsonUtils.GetEventName(json);
2516
var paths = JsonUtils.FlattenJson(json);
2617

2718
if (eventName == "Status")

EliteAPI/Json/JsonUtils.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,23 @@ namespace EliteAPI.Json;
77

88
public static class JsonUtils
99
{
10+
public static string GetEventName(string json)
11+
{
12+
var obj = JObject.Parse(json);
13+
14+
{
15+
if (obj.TryGetValue("event", out var eventName))
16+
return eventName.ToString();
17+
}
18+
19+
{
20+
if (obj.TryGetValue("Event", out var eventName))
21+
return eventName.ToString();
22+
}
23+
24+
return string.Empty;
25+
}
26+
1027
public static List<JsonPath> FlattenJson(string json)
1128
{
1229
// TODO: call flattening function

EliteVA/EliteVA.cs

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
using System;
22
using System.Linq;
33
using System.Threading.Tasks;
4-
using EliteApi;
4+
using EliteAPI;
5+
using EliteAPI.Journals;
56
using EliteAPI.Json;
67
using EliteVA.Abstractions;
78
using EliteVA.Logging;
@@ -16,26 +17,28 @@ public override async Task OnStart(IVoiceAttackProxy proxy)
1617
{
1718
_api = new EliteDangerousApi();
1819

19-
_api.OnPathsChanged(e =>
20+
_api.OnAllJson((eventName, json) =>
2021
{
21-
e.variables.ForEach(v =>
22-
{
23-
proxy.Variables.Set(v.Path, v.Value, v.Type switch
24-
{
25-
JsonType.String => TypeCode.String,
26-
JsonType.Number => TypeCode.Int32,
27-
JsonType.Decimal => TypeCode.Decimal,
28-
JsonType.Boolean => TypeCode.Boolean,
29-
JsonType.DateTime => TypeCode.DateTime,
30-
_ => TypeCode.String
31-
});
32-
33-
var command = $"(({e.command}))";
34-
if (proxy.Commands.Exists(command))
35-
proxy.Commands.Invoke(command);
36-
});
37-
38-
Log(VoiceAttackColor.Yellow, $"{e.command}");
22+
var paths = JournalUtils.ToPaths(json);
23+
24+
// json.variables.ForEach(v =>
25+
// {
26+
// proxy.Variables.Set(v.Path, v.Value, v.Type switch
27+
// {
28+
// JsonType.String => TypeCode.String,
29+
// JsonType.Number => TypeCode.Int32,
30+
// JsonType.Decimal => TypeCode.Decimal,
31+
// JsonType.Boolean => TypeCode.Boolean,
32+
// JsonType.DateTime => TypeCode.DateTime,
33+
// _ => TypeCode.String
34+
// });
35+
36+
// var command = $"(({e.command}))";
37+
// if (proxy.Commands.Exists(command))
38+
// proxy.Commands.Invoke(command);
39+
// });
40+
41+
// Log(VoiceAttackColor.Yellow, $"{e.command}");
3942
});
4043

4144
_api.Start();

0 commit comments

Comments
 (0)