-
Notifications
You must be signed in to change notification settings - Fork 85
/
ConnectionManager.cs
163 lines (141 loc) · 6.48 KB
/
ConnectionManager.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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Net.Http;
using WebSocketSharp;
using System.Text;
namespace Conduit
{
/**
* Class responsible for monitoring League and automatically connecting to Rift when needed.
* This class is also responsible for requesting/storing JWTs from rift and ensuring that they
* remain valid. All actual messaging is left to Hub/MobileConnectionHandler.
*/
class ConnectionManager
{
private static readonly HttpClient httpClient = new HttpClient();
private App app;
private LeagueConnection league;
private HubConnectionHandler hubConnectionHandler;
private bool isNewLaunch = true;
private bool hasTriedImmediateReconnect = false; // after a DC, we first try to reconnect immediately. If that fails, do a 5s backoff
private CancellationTokenSource reconnectCancellationTokenSource;
public ConnectionManager(App app)
{
this.app = app;
this.league = new LeagueConnection();
// Hook up league events.
league.OnConnected += () =>
{
DebugLogger.Global.WriteMessage($"ConnectionManager is connected to League of Legends.");
isNewLaunch = true;
Connect();
};
league.OnDisconnected += () =>
{
DebugLogger.Global.WriteMessage($"ConnectionManager is disconnected from League of Legends.");
Close();
};
}
/**
* Connects to the hub. Errors if there is already a hub connection.
* Will cancel a pending reconnection if there is one. This method is
* not guaranteed to connect on first try.
*/
public async void Connect()
{
if (hubConnectionHandler != null) throw new Exception("Already connected.");
if (!league.IsConnected) return;
try
{
DebugLogger.Global.WriteMessage("Connecting to Rift...");
// Cancel pending reconnect if there is one.
if (reconnectCancellationTokenSource != null)
{
DebugLogger.Global.WriteMessage($"Canceling older reconnect to Rift.");
reconnectCancellationTokenSource.Cancel();
reconnectCancellationTokenSource = null;
}
// Ensure that our token is still valid...
bool valid = false; // in case first startup and hub token is empty
if (!Persistence.GetHubToken().IsNullOrEmpty())
{
DebugLogger.Global.WriteMessage("Requesting hub token..");
var response = await httpClient.GetStringAsync(Program.HUB + "/check?token=" + Persistence.GetHubToken());
valid = response == "true";
DebugLogger.Global.WriteMessage($"Hub token validity: {(valid ? "valid" : "invalid")}.");
}
// ... and request a new one if it isn't.
if (!valid)
{
DebugLogger.Global.WriteMessage($"Requesting hub token..");
var payload = "{\"pubkey\":\"" + CryptoHelpers.ExportPublicKey() + "\"}";
var responseBlob = await httpClient.PostAsync(Program.HUB + "/register", new StringContent(payload, Encoding.UTF8, "application/json"));
var response = SimpleJson.DeserializeObject<dynamic>(await responseBlob.Content.ReadAsStringAsync());
if (!response["ok"]) throw new Exception("Could not receive JWT from Rift");
Persistence.SetHubToken(response["token"]);
DebugLogger.Global.WriteMessage($"Hub token: {response["token"]}.");
}
// Connect to hub. Will error if token is invalid or server is down, which will prompt a reconnection.
hubConnectionHandler = new HubConnectionHandler(league);
hubConnectionHandler.OnClose += CloseAndReconnect;
// We assume to be connected.
if (isNewLaunch)
{
DebugLogger.Global.WriteMessage($"Creating New Launch popup.");
app.ShowNotification("Connected to League. Click here for instructions on how to control your League client from your phone.");
isNewLaunch = false;
}
hasTriedImmediateReconnect = false;
}
catch (Exception e)
{
DebugLogger.Global.WriteError($"Connection to Rift failed, an exception occurred: {e.ToString()}");
// Something happened that we didn't anticipate for.
// Just try again in a bit.
CloseAndReconnect();
}
}
/**
* Closes all connections _without_ queueing a reconnect.
*/
public void Close()
{
DebugLogger.Global.WriteMessage("Disconnecting from rift.");
// Already closed, don't error but just ignore.
if (hubConnectionHandler == null) return;
// Clear entries.
hubConnectionHandler.Close();
league.ClearAllListeners();
hubConnectionHandler = null;
reconnectCancellationTokenSource = null;
}
/**
* Does the same as Close, but queues a reconnect in 5 seconds.
*/
public async void CloseAndReconnect()
{
Close();
try
{
reconnectCancellationTokenSource = new CancellationTokenSource();
// If this is an immediate reconnect,
if (hasTriedImmediateReconnect)
{
DebugLogger.Global.WriteWarning("Could not connect to Rift, retrying to connect to Rift in 5s...");
await Task.Delay(5000, reconnectCancellationTokenSource.Token);
} else
{
DebugLogger.Global.WriteWarning("Could not connect to Rift, retrying to connect to Rift immediately...");
hasTriedImmediateReconnect = true;
}
Connect();
}
catch (TaskCanceledException e)
{
DebugLogger.Global.WriteError($"Our reconnect to Rift got canceled, reason: {e.ToString()}");
// Delay got cancelled. Ignore error.
}
}
}
}