Skip to content

Commit 8597e75

Browse files
committed
Updates logic to "reassemble" out of order status responses
1 parent 12870db commit 8597e75

1 file changed

Lines changed: 68 additions & 23 deletions

File tree

Integrations/Cod/CodRConConnection.cs

Lines changed: 68 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ public class CodRConConnection : IRConConnection
3434
private readonly ILogger _log;
3535
private readonly Encoding _gameEncoding;
3636
private readonly int _retryAttempts;
37-
private static readonly Server.Game[] RconDelayGames = [Server.Game.IW3, Server.Game.T4, Server.Game.T5, Server.Game.T6];
37+
38+
private static readonly Server.Game[] RconDelayGames =
39+
[Server.Game.IW3, Server.Game.T4, Server.Game.T5, Server.Game.T6];
3840

3941
public CodRConConnection(IPEndPoint ipEndpoint, string password, ILogger<CodRConConnection> log,
4042
Encoding gameEncoding, int retryAttempts)
@@ -168,7 +170,8 @@ string ConvertEncoding(string text)
168170
break;
169171
case StaticHelpers.QueryType.GET_STATUS:
170172
waitForResponse = true;
171-
payload = (_config.CommandPrefixes.RConGetStatus + '\0').Select(Helpers.SafeConversion).ToArray();
173+
payload = (_config.CommandPrefixes.RConGetStatus + '\0').Select(Helpers.SafeConversion)
174+
.ToArray();
172175
break;
173176
case StaticHelpers.QueryType.GET_INFO:
174177
waitForResponse = true;
@@ -197,13 +200,11 @@ string ConvertEncoding(string text)
197200
byte[][] response;
198201

199202
retrySend:
200-
using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)
201-
{
202-
DontFragment = false,
203-
Ttl = 100,
204-
ExclusiveAddressUse = true,
205-
})
203+
using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp))
206204
{
205+
socket.DontFragment = false;
206+
socket.Ttl = 100;
207+
socket.ExclusiveAddressUse = true;
207208
if (!token.IsCancellationRequested)
208209
{
209210
connectionState.ConnectionAttempts++;
@@ -309,7 +310,7 @@ string ConvertEncoding(string text)
309310
{
310311
_log.LogDebug("Received empty response for RCon request {@Query}",
311312
new { endpoint = Endpoint.ToString(), type, parameters });
312-
return Array.Empty<string>();
313+
return [];
313314
}
314315

315316
var responseString = type == StaticHelpers.QueryType.COMMAND_STATUS
@@ -370,7 +371,7 @@ private async Task<byte[][]> SendPayloadAsync(Socket rconSocket, byte[] payload,
370371

371372
if (!waitForResponse)
372373
{
373-
return Array.Empty<byte[]>();
374+
return [];
374375
}
375376

376377
_log.LogDebug("Waiting to asynchronously receive data on attempt #{ConnectionAttempts}",
@@ -380,7 +381,7 @@ private async Task<byte[][]> SendPayloadAsync(Socket rconSocket, byte[] payload,
380381

381382
if (RconDelayGames.Contains(_parser.GameName))
382383
{
383-
await Task.Delay(100, token);
384+
await Task.Delay(100, token);
384385
}
385386

386387
while (rconSocket.Available > 0)
@@ -447,7 +448,7 @@ private string[] ValidateResponse(StaticHelpers.QueryType type, string responseS
447448

448449
if (headerSplit.Length == 2)
449450
{
450-
return headerSplit.Last().Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries)
451+
return headerSplit.Last().Split(['\n'], StringSplitOptions.RemoveEmptyEntries)
451452
.Select(line => line.StartsWith("^7") ? line[2..] : line).ToArray();
452453
}
453454

@@ -462,31 +463,75 @@ private string[] ValidateResponse(StaticHelpers.QueryType type, string responseS
462463

463464
/// <summary>
464465
/// reassembles broken status segments into the 'correct' ordering
465-
/// <remarks>this is primarily for T7, and is really only reliable for 2 segments</remarks>
466+
/// <remarks>this is primarily for T7, CoD4x, and T6. Really only reliable for 3 segments</remarks>
466467
/// </summary>
467468
/// <param name="segments">array of segmented byte arrays</param>
468469
/// <returns></returns>
469470
private string ReassembleSegmentedStatus(IEnumerable<byte[]> segments)
470471
{
471-
var splitStatusStrings = new List<string>();
472+
var segmentStrings = segments
473+
.Select(seg => _gameEncoding.GetString(seg, 0, seg.Length).TrimEnd('\0'))
474+
.ToList();
475+
476+
if (segmentStrings.Count <= 1)
477+
{
478+
return string.Join("", segmentStrings);
479+
}
480+
481+
var headerIndex = segmentStrings.FindIndex(s => _config.StatusHeader.PatternMatcher.Match(s).Success);
472482

473-
foreach (var segment in segments)
483+
// when there's no header it'll likely fail anyway so we'll keep the original order
484+
if (headerIndex == -1)
474485
{
475-
var responseString = _gameEncoding.GetString(segment, 0, segment.Length);
476-
var statusHeaderMatch = _config.StatusHeader.PatternMatcher.Match(responseString);
477-
if (statusHeaderMatch.Success)
486+
return string.Join("", segmentStrings.Select(s => s.Replace(_config.CommandPrefixes.RConResponse, "")));
487+
}
488+
489+
var reassembledSegments = new List<string>();
490+
491+
// for T6 status the last packet contains a double new line
492+
var hasDefinitiveLastSegment = segmentStrings.Any(s => s.EndsWith("\n\n"));
493+
494+
if (hasDefinitiveLastSegment)
495+
{
496+
// T6 behavior
497+
var firstSegment = segmentStrings[headerIndex];
498+
string lastSegment = null;
499+
var middleSegments = new List<string>();
500+
501+
for (var i = 0; i < segmentStrings.Count; i++)
478502
{
479-
splitStatusStrings.Insert(0, responseString.TrimEnd('\0'));
503+
if (i == headerIndex)
504+
continue;
505+
506+
var currentSegment = segmentStrings[i];
507+
508+
if (currentSegment.EndsWith("\n\n") && lastSegment == null)
509+
{
510+
lastSegment = currentSegment.Replace(_config.CommandPrefixes.RConResponse, "");
511+
}
512+
else
513+
{
514+
middleSegments.Add(currentSegment.Replace(_config.CommandPrefixes.RConResponse, ""));
515+
}
480516
}
481517

482-
else
518+
reassembledSegments.Add(firstSegment);
519+
reassembledSegments.AddRange(middleSegments);
520+
521+
if (lastSegment != null)
483522
{
484-
splitStatusStrings.Add(responseString.Replace(_config.CommandPrefixes.RConResponse, "")
485-
.TrimEnd('\0'));
523+
reassembledSegments.Add(lastSegment);
486524
}
487525
}
526+
else
527+
{
528+
// other game (default) behavior
529+
reassembledSegments.Add(segmentStrings[headerIndex]);
530+
reassembledSegments.AddRange(segmentStrings.Where((_, i) => i != headerIndex)
531+
.Select(t => t.Replace(_config.CommandPrefixes.RConResponse, "")));
532+
}
488533

489-
return string.Join("", splitStatusStrings);
534+
return string.Join("", reassembledSegments);
490535
}
491536

492537
/// <summary>

0 commit comments

Comments
 (0)