-
Notifications
You must be signed in to change notification settings - Fork 0
/
LiveSplitIntegration.cs
142 lines (128 loc) · 4.02 KB
/
LiveSplitIntegration.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
using LiveSplitInterop;
using LiveSplitInterop.Clients;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Terraria.ModLoader;
namespace LiveSplitIntegration
{
public class LiveSplitIntegration : Mod
{
/// <summary>
/// The client that communicates with LiveSplit.
/// </summary>
private NamedPipeCommandClient Client;
/// <summary>
/// A lock to prevent multiple threads from using <see cref="Client"/> simultaneously.
/// </summary>
private object ClientLock;
/// <summary>
/// A <see cref="CancellationTokenSource"/> used to stop pending connections when unloading the mod.
/// </summary>
private CancellationTokenSource connectSource;
/// <summary>
/// Connect to LiveSplit asynchronously. Cancel <see cref="connectSource"/> to stop the operation.
/// </summary>
private async Task Connect()
{
Client = new();
await Client.ConnectAsync(connectSource.Token);
}
public override void Load()
{
ClientLock = new();
connectSource = new();
Task.Run(Connect);
}
public override void Unload()
{
connectSource.Cancel();
connectSource.Dispose();
Client?.Dispose();
}
/// <summary>
/// Attempt to send one or more messages to LiveSplit.
/// </summary>
/// <param name="msgAction">
/// The action to perform with the LiveSplit client.
/// </param>
/// <returns>
/// A <see cref="bool"/> indicating whether or not <paramref name="msgAction"/> completed successfully.
/// </returns>
internal bool TrySendLsMsg(Action<ILiveSplitCommandClient> msgAction)
{
lock (ClientLock)
{
if (!Client?.IsConnected ?? false)
{
return false;
}
try
{
msgAction(Client);
return true;
}
catch (IOException)
{
//TODO: log
}
catch (ObjectDisposedException)
{
return false;
}
if (!connectSource.IsCancellationRequested)
{
Client.Dispose();
Task.Run(Connect);
}
return false;
}
}
/// <summary>
/// Attempt to send one or more messages to LiveSplit, at least one of which returns a value.
/// </summary>
/// <typeparam name="T">
/// The type of data returned.
/// </typeparam>
/// <param name="res">
/// The returned data. In case of failure, this value will be invalid.
/// </param>
/// <inheritdoc cref="TrySendLsMsg(Action{ILiveSplitCommandClient})"/>
internal bool TrySendLsMsg<T>(Func<ILiveSplitCommandClient, T> msgAction, out T res)
{
lock (ClientLock)
{
if (!Client?.IsConnected ?? false)
{
res = default;
return false;
}
try
{
res = msgAction(Client);
return true;
}
catch (IOException)
{
//TODO: log
}
catch (ObjectDisposedException)
{
res = default;
return false;
}
if (!connectSource.IsCancellationRequested)
{
Client.Dispose();
Task.Run(Connect);
}
res = default;
return false;
}
}
}
}