1
- using System ;
2
- using System . Net . Http ;
3
- using System . Text ;
4
- using System . Text . Json ;
5
- using System . Threading ;
6
- using System . Threading . Tasks ;
7
-
8
1
namespace IntelOrca . Biohazard . BioRand
9
2
{
10
3
public class RandomizerAgent
@@ -18,10 +11,10 @@ public class RandomizerAgent
18
11
{
19
12
PropertyNamingPolicy = JsonNamingPolicy . CamelCase
20
13
} ;
21
- private bool _generating ;
22
- private TimeSpan _pollTime = TimeSpan . FromSeconds ( 5 ) ;
23
- private TimeSpan _restartTime = TimeSpan . FromSeconds ( 5 ) ;
24
14
private readonly IRandomizerAgentHandler _handler ;
15
+ private readonly SemaphoreSlim _semaphore = new SemaphoreSlim ( 1 ) ;
16
+ private DateTime _lastHeartbeatTime ;
17
+ private bool _shownWaitingMessage ;
25
18
26
19
public IRandomizer Randomizer => _handler . Randomizer ;
27
20
public string BaseUri { get ; }
@@ -30,6 +23,10 @@ public class RandomizerAgent
30
23
public Guid Id { get ; private set ; }
31
24
public string Status { get ; private set ; } = StatusIdle ;
32
25
26
+ public TimeSpan HeartbeatTime { get ; set ; } = TimeSpan . FromSeconds ( 5 ) ;
27
+ public TimeSpan PollTime { get ; set ; } = TimeSpan . FromSeconds ( 5 ) ;
28
+ public TimeSpan RestartTime { get ; set ; } = TimeSpan . FromSeconds ( 5 ) ;
29
+
33
30
public RandomizerAgent ( string baseUri , string apiKey , int gameId , IRandomizerAgentHandler handler )
34
31
{
35
32
BaseUri = baseUri ;
@@ -44,21 +41,18 @@ public async Task RunAsync(CancellationToken ct = default)
44
41
{
45
42
while ( ! ct . IsCancellationRequested )
46
43
{
47
- await RegisterAsync ( ) ;
48
- while ( ! ct . IsCancellationRequested )
44
+ if ( await RegisterAsync ( ) )
49
45
{
50
- await SendStatusAsync ( ) ;
51
- if ( ! _generating )
52
- {
53
- await ProcessNextRandomizer ( ) ;
54
- }
55
- await Task . Delay ( _pollTime ) ;
46
+ await Task . WhenAny (
47
+ RunStatusLoopAsync ( ct ) ,
48
+ RunProcessLoopAsync ( ct ) ) ;
49
+ await UnregisterAsync ( ) ;
56
50
}
57
- await Task . Delay ( _restartTime ) ;
51
+ await Task . Delay ( RestartTime , ct ) ;
58
52
}
59
53
}
60
54
61
- private async Task RegisterAsync ( )
55
+ private async Task < bool > RegisterAsync ( )
62
56
{
63
57
_handler . LogInfo ( $ "Registering agent at { BaseUri } ...") ;
64
58
try
@@ -71,10 +65,59 @@ private async Task RegisterAsync()
71
65
} ) ;
72
66
Id = response . Id ;
73
67
_handler . LogInfo ( $ "Registered as agent { Id } ") ;
68
+ return true ;
74
69
}
75
70
catch ( Exception ex )
76
71
{
77
72
_handler . LogError ( ex , "Failed to register agent" ) ;
73
+ return false ;
74
+ }
75
+ }
76
+
77
+ private async Task UnregisterAsync ( )
78
+ {
79
+ _handler . LogInfo ( $ "Unregistering agent...") ;
80
+ try
81
+ {
82
+ await PostAsync < object > ( "generator/unregister" , new { Id } ) ;
83
+ _handler . LogInfo ( $ "Unregistered agent { Id } ") ;
84
+ }
85
+ catch ( Exception ex )
86
+ {
87
+ _handler . LogError ( ex , "Failed to unregister agent" ) ;
88
+ }
89
+ }
90
+
91
+ private async Task RunStatusLoopAsync ( CancellationToken ct )
92
+ {
93
+ while ( ! ct . IsCancellationRequested )
94
+ {
95
+ await _semaphore . WaitAsync ( ) ;
96
+ try
97
+ {
98
+ var timeSinceLastHeartbeat = DateTime . UtcNow - _lastHeartbeatTime ;
99
+ if ( timeSinceLastHeartbeat >= HeartbeatTime )
100
+ {
101
+ await SendStatusAsync ( ) ;
102
+ }
103
+ }
104
+ finally
105
+ {
106
+ _semaphore . Release ( ) ;
107
+ }
108
+ await Task . Delay ( HeartbeatTime , ct ) ;
109
+ }
110
+ }
111
+
112
+ private async Task RunProcessLoopAsync ( CancellationToken ct )
113
+ {
114
+ _shownWaitingMessage = false ;
115
+ while ( ! ct . IsCancellationRequested )
116
+ {
117
+ if ( ! await ProcessNextRandomizer ( ct ) )
118
+ {
119
+ await Task . Delay ( PollTime , ct ) ;
120
+ }
78
121
}
79
122
}
80
123
@@ -87,51 +130,81 @@ private async Task SendStatusAsync()
87
130
Id ,
88
131
Status
89
132
} ) ;
133
+ _lastHeartbeatTime = DateTime . UtcNow ;
90
134
}
91
135
catch ( Exception ex )
92
136
{
93
137
_handler . LogError ( ex , "Failed to send heartbeat" ) ;
94
138
}
95
139
}
96
140
97
- private async Task ProcessNextRandomizer ( )
141
+ private async Task SetStatusAsync ( string status )
142
+ {
143
+ await _semaphore . WaitAsync ( ) ;
144
+ try
145
+ {
146
+ Status = status ;
147
+ await SendStatusAsync ( ) ;
148
+ }
149
+ finally
150
+ {
151
+ _semaphore . Release ( ) ;
152
+ }
153
+ }
154
+
155
+ private async Task < bool > ProcessNextRandomizer ( CancellationToken ct )
98
156
{
99
157
try
100
158
{
159
+ if ( ct . IsCancellationRequested )
160
+ return false ;
161
+
162
+ if ( ! _shownWaitingMessage )
163
+ {
164
+ _shownWaitingMessage = true ;
165
+ _handler . LogInfo ( $ "Waiting for next rando to generate...") ;
166
+ }
101
167
var queue = await GetAsync < QueueResponseItem [ ] > ( "generator/queue" ) ;
102
168
foreach ( var q in queue )
103
169
{
170
+ if ( ct . IsCancellationRequested )
171
+ return false ;
104
172
if ( q . GameId != GameId )
105
173
continue ;
106
174
107
175
if ( await _handler . CanGenerateAsync ( q ) )
108
176
{
177
+ if ( ct . IsCancellationRequested )
178
+ return false ;
179
+
109
180
try
110
181
{
111
182
_handler . LogInfo ( $ "Generating rando { q . Id } ...") ;
183
+ _shownWaitingMessage = false ;
112
184
await PostAsync < object > ( "generator/begin" , new
113
185
{
114
186
Id ,
115
187
RandoId = q . Id ,
116
188
Version = Randomizer . BuildVersion
117
189
} ) ;
118
- await BeginGeneration ( q ) ;
190
+ await GenerateRandomizer ( q ) ;
191
+ return true ;
119
192
}
120
193
catch ( Exception ex )
121
194
{
122
195
_handler . LogError ( ex , "Failed to begin generating randomizer" ) ;
123
196
}
124
- break ;
125
197
}
126
198
}
127
199
}
128
200
catch ( Exception ex )
129
201
{
130
202
_handler . LogError ( ex , "Failed to get randomizer queue" ) ;
131
203
}
204
+ return false ;
132
205
}
133
206
134
- private async Task BeginGeneration ( QueueResponseItem q )
207
+ private async Task GenerateRandomizer ( QueueResponseItem q )
135
208
{
136
209
var randomizerInput = new RandomizerInput ( )
137
210
{
@@ -142,37 +215,39 @@ private async Task BeginGeneration(QueueResponseItem q)
142
215
Seed = q . Seed
143
216
} ;
144
217
145
- _generating = true ;
146
218
try
147
219
{
148
- await Task . Run ( async ( ) =>
220
+ await SetStatusAsync ( StatusGenerating ) ;
221
+ RandomizerOutput output ;
222
+ try
149
223
{
150
- try
224
+ output = await _handler . GenerateAsync ( q , randomizerInput ) ;
225
+ }
226
+ catch ( Exception ex )
227
+ {
228
+ await PostAsync < object > ( "generator/fail" , new
151
229
{
152
- Status = StatusGenerating ;
153
- var output = await _handler . GenerateAsync ( q , randomizerInput ) ;
230
+ Id ,
231
+ RandoId = q . Id
232
+ } ) ;
233
+ _handler . LogError ( ex , "Failed to generate randomizer" ) ;
234
+ return ;
235
+ }
154
236
155
- Status = StatusUploading ;
156
- _handler . LogInfo ( $ "Uploading rando { q . Id } ...") ;
157
- await PostAsync < object > ( "generator/end" , new
158
- {
159
- Id ,
160
- RandoId = q . Id ,
161
- output . PakOutput ,
162
- output . FluffyOutput
163
- } ) ;
164
- _handler . LogInfo ( $ "Uploaded rando { q . Id } ") ;
165
- }
166
- finally
167
- {
168
- Status = StatusIdle ;
169
- _generating = false ;
170
- }
237
+ await SetStatusAsync ( StatusUploading ) ;
238
+ _handler . LogInfo ( $ "Uploading rando { q . Id } ...") ;
239
+ await PostAsync < object > ( "generator/end" , new
240
+ {
241
+ Id ,
242
+ RandoId = q . Id ,
243
+ output . PakOutput ,
244
+ output . FluffyOutput
171
245
} ) ;
246
+ _handler . LogInfo ( $ "Uploaded rando { q . Id } ") ;
172
247
}
173
- catch
248
+ finally
174
249
{
175
- _generating = false ;
250
+ await SetStatusAsync ( StatusIdle ) ;
176
251
}
177
252
}
178
253
0 commit comments