Skip to content

Commit 43f80fa

Browse files
committed
Add retries to connect attempts for EOBot (#407)
* Add retry to botframework init * Fix dispose pattern in BotFramework * Make initAttempts a command-line argument
1 parent 8b2f936 commit 43f80fa

File tree

3 files changed

+65
-47
lines changed

3 files changed

+65
-47
lines changed

EOBot/ArgumentsParser.cs

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
using System.Collections.Generic;
22
using System.IO;
33
using System.Linq;
4-
using EOBot.Interpreter.Variables;
54

65
namespace EOBot
76
{
@@ -19,7 +18,8 @@ public enum ArgsError
1918
InvalidInitDelay,
2019
InvalidPath,
2120
InvalidScriptArgs,
22-
AutoConnectRequired
21+
AutoConnectRequired,
22+
InvalidInitRetryAttempts,
2323
}
2424

2525
public class ArgumentsParser
@@ -42,6 +42,8 @@ public class ArgumentsParser
4242

4343
public bool AutoConnect { get; private set; } = true;
4444

45+
public int InitRetryAttempts { get; private set; } = 5;
46+
4547
public List<string> UserArgs { get; internal set; }
4648

4749
public bool ExtendedHelp { get; private set; }
@@ -119,6 +121,10 @@ public ArgumentsParser(string[] args)
119121
case "character":
120122
Character = pair[1];
121123
break;
124+
case "initattempts":
125+
if (!ParseRetries(pair[1]))
126+
return;
127+
break;
122128
default:
123129
Error = ArgsError.BadFormat;
124130
return;
@@ -150,8 +156,7 @@ private void ParseHost(string hostStr)
150156

151157
private bool ParsePort(string portStr)
152158
{
153-
ushort port;
154-
if (!ushort.TryParse(portStr, out port))
159+
if (!ushort.TryParse(portStr, out var port))
155160
{
156161
Error = ArgsError.InvalidPort;
157162
return false;
@@ -172,8 +177,8 @@ private bool ParseNumBots(string[] pair)
172177

173178
bool needSimul = both.Length == 2;
174179

175-
int numBots, simul = -1;
176-
if (!int.TryParse(both[0], out numBots))
180+
int simul = -1;
181+
if (!int.TryParse(both[0], out var numBots))
177182
{
178183
Error = ArgsError.InvalidNumberOfBots;
179184
return false;
@@ -201,8 +206,7 @@ private bool ParseNumBots(string[] pair)
201206

202207
private bool ParseInitDelay(string initDelay)
203208
{
204-
int delay;
205-
if (!int.TryParse(initDelay, out delay) || delay < 1100)
209+
if (!int.TryParse(initDelay, out var delay) || delay < 1100)
206210
{
207211
Error = ArgsError.InvalidInitDelay;
208212
return false;
@@ -212,5 +216,18 @@ private bool ParseInitDelay(string initDelay)
212216

213217
return true;
214218
}
219+
220+
private bool ParseRetries(string retries)
221+
{
222+
if (!int.TryParse(retries, out var attempts) || attempts < 1)
223+
{
224+
Error = ArgsError.InvalidInitRetryAttempts;
225+
return false;
226+
}
227+
228+
InitRetryAttempts = attempts;
229+
230+
return true;
231+
}
215232
}
216233
}

EOBot/BotFramework.cs

Lines changed: 35 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
using System;
22
using System.Collections.Generic;
3-
using System.Linq;
43
using System.Threading;
54
using System.Threading.Tasks;
6-
using EOLib.Net.Handlers;
75

86
namespace EOBot
97
{
@@ -15,6 +13,7 @@ sealed class BotFramework : IDisposable
1513
private readonly List<IBot> _botsList;
1614
private readonly string _host;
1715
private readonly ushort _port;
16+
private readonly int _initRetryAttempts;
1817

1918
private Semaphore _doneSignal;
2019
private bool _initialized;
@@ -30,8 +29,6 @@ public BotFramework(ArgumentsParser parsedArgs)
3029

3130
var numberOfBots = parsedArgs.NumBots;
3231
var simultaneousBots = parsedArgs.SimultaneousBots;
33-
var host = parsedArgs.Host;
34-
var port = parsedArgs.Port;
3532

3633
if (numberOfBots > NUM_BOTS_MAX || simultaneousBots > NUM_BOTS_MAX || simultaneousBots > numberOfBots)
3734
throw new ArgumentException("Too many bots requested");
@@ -40,8 +37,9 @@ public BotFramework(ArgumentsParser parsedArgs)
4037
throw new ArgumentException("Not enough bots requested");
4138

4239
_numBots = numberOfBots;
43-
_host = host;
44-
_port = port;
40+
_host = parsedArgs.Host;
41+
_port = parsedArgs.Port;
42+
_initRetryAttempts = parsedArgs.InitRetryAttempts;
4543

4644
_botsList = new List<IBot>(numberOfBots);
4745

@@ -54,27 +52,41 @@ public async Task InitializeAsync(IBotFactory botFactory, int delayBetweenInitsM
5452
throw new InvalidOperationException("Unable to initialize bot framework a second time.");
5553

5654
int numFailed = 0;
57-
for (int i = 0; i < _numBots; ++i)
55+
for (int i = 0; i < _numBots; i++)
5856
{
5957
if (_terminating)
6058
throw new BotException("Received termination signal; initialization has been cancelled");
6159

62-
try
63-
{
64-
var bot = botFactory.CreateBot(i);
65-
bot.WorkCompleted += () => _doneSignal.Release();
66-
await bot.InitializeAsync(_host, _port);
67-
_botsList.Add(bot);
68-
}
69-
catch (Exception ex)
60+
for (int attempt = 1; attempt <= _initRetryAttempts; attempt++)
7061
{
71-
ConsoleHelper.WriteMessage(ConsoleHelper.Type.Error, ex.Message, ConsoleColor.DarkRed);
72-
numFailed++;
73-
continue;
62+
ConsoleHelper.WriteMessage(ConsoleHelper.Type.None, $"Initializing bot {i} [attempt {attempt}/{_initRetryAttempts}]");
63+
try
64+
{
65+
var bot = botFactory.CreateBot(i);
66+
bot.WorkCompleted += () => _doneSignal.Release();
67+
await bot.InitializeAsync(_host, _port);
68+
_botsList.Add(bot);
69+
70+
ConsoleHelper.WriteMessage(ConsoleHelper.Type.None, $"Bot {i} initialized.");
71+
await Task.Delay(delayBetweenInitsMS); // minimum for this is 1sec server-side
72+
}
73+
catch (Exception ex)
74+
{
75+
ConsoleHelper.WriteMessage(ConsoleHelper.Type.Error, ex.Message, ConsoleColor.DarkRed);
76+
77+
if (attempt == _initRetryAttempts)
78+
{
79+
ConsoleHelper.WriteMessage(ConsoleHelper.Type.Error, $"Bot {i} failed to initialize after {_initRetryAttempts} attempts.", ConsoleColor.DarkRed);
80+
numFailed++;
81+
}
82+
else
83+
{
84+
var retryDelayTime = TimeSpan.FromMilliseconds(delayBetweenInitsMS + (1000 * attempt * attempt));
85+
ConsoleHelper.WriteMessage(ConsoleHelper.Type.Warning, $"Bot {i} failed to initialize. Retrying in {retryDelayTime.TotalMilliseconds}ms...", ConsoleColor.DarkYellow);
86+
await Task.Delay(retryDelayTime);
87+
}
88+
}
7489
}
75-
76-
ConsoleHelper.WriteMessage(ConsoleHelper.Type.None, $"Bot {i} initialized.");
77-
await Task.Delay(delayBetweenInitsMS); //minimum for this is 1sec server-side
7890
}
7991

8092
if (numFailed > 0)
@@ -129,24 +141,10 @@ public void TerminateBots()
129141
_cancellationTokenSource.Cancel();
130142
}
131143

132-
~BotFramework()
133-
{
134-
Dispose(false);
135-
GC.SuppressFinalize(this);
136-
}
137-
138144
public void Dispose()
139145
{
140-
Dispose(true);
141-
}
142-
143-
private void Dispose(bool disposing)
144-
{
145-
if (disposing)
146-
{
147-
_doneSignal?.Dispose();
148-
_cancellationTokenSource?.Dispose();
149-
}
146+
_doneSignal?.Dispose();
147+
_cancellationTokenSource?.Dispose();
150148
}
151149
}
152150
}

EOBot/Program.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,8 @@
66
using EOBot.Interpreter;
77
using EOLib;
88
using EOLib.Domain.Character;
9-
using EOLib.Domain.Extensions;
109
using EOLib.Domain.Map;
1110
using EOLib.Domain.Notifiers;
12-
using EOLib.Domain.NPC;
1311
using EOLib.Domain.Spells;
1412
using EOLib.IO.Repositories;
1513
using Optional;
@@ -305,6 +303,9 @@ static void ShowError(ArgumentsParser args)
305303
case ArgsError.AutoConnectRequired:
306304
Console.WriteLine("Invalid: AutoConnect is required when using a script with more than 1 bot due to eoserv connection throttling.");
307305
break;
306+
case ArgsError.InvalidInitRetryAttempts:
307+
Console.WriteLine("Invalid: Retry attempts must be >= 1.");
308+
break;
308309
}
309310

310311
Console.WriteLine("\n\nUsage: (enter arguments in any order) (angle brackets is entry) (square brackets is optional)");
@@ -317,6 +318,7 @@ static void ShowError(ArgumentsParser args)
317318
" character=<character>\n" +
318319
" script=<file>\n" +
319320
" autoconnect=<true|false>" +
321+
" initAttempts=<numRetries>\n" +
320322
" [-- arg1, [arg2..argn]]");
321323
Console.WriteLine("\t host: hostname or IP address");
322324
Console.WriteLine("\t port: port to connect on (probably 8078)");
@@ -327,6 +329,7 @@ static void ShowError(ArgumentsParser args)
327329
Console.WriteLine("\t character: character to use (created if it does not exist)");
328330
Console.WriteLine("\t script: script file to execute\n\t if script is not specified, default trainer bot will be used");
329331
Console.WriteLine("\t autoconnect: (default true) true to automatically connect/disconnect to server with initDelay timeout between connection attempts for bots, false otherwise");
332+
Console.WriteLine("\t initAttempts: (default 5) number of attempts to make when initializing each bot (uses exponential backoff retry strategy)");
330333
Console.WriteLine("\t --: Any arguments passed after '--' will be available in a script under the '$args' array");
331334

332335
if (!args.ExtendedHelp)

0 commit comments

Comments
 (0)