private readonly Timer _reliablePacketTimer;
private readonly SemaphoreSlim _connectWaitLock;
- private readonly ArrayPool<byte> _pool;
- private readonly Channel<byte[]> _channel;
private Task _listenTask;
- private Task _handleTask;
/// <summary>
/// Creates a new UdpClientConnection.
_reliablePacketTimer = new Timer(ManageReliablePacketsInternal, null, 100, Timeout.Infinite);
_connectWaitLock = new SemaphoreSlim(1, 1);
- _pool = ArrayPool<byte>.Shared;
- _channel = Channel.CreateUnbounded<byte[]>(new UnboundedChannelOptions
- {
- SingleReader = true,
- SingleWriter = true
- });
}
~UdpClientConnection()
using System.IO;
using System.Net;
using System.Threading;
+using System.Threading.Channels;
using System.Threading.Tasks;
using Impostor.Api.Games;
using Impostor.Api.Net.Messages;
using Impostor.Server.Config;
using Impostor.Server.Net;
+using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.ObjectPool;
using Microsoft.Extensions.Options;
/// <summary>
/// Records all packets received in <see cref="ClientRecorder.HandleMessageAsync"/>.
/// </summary>
- internal class PacketRecorder : IDisposable
+ internal class PacketRecorder : BackgroundService
{
+ private readonly string _path;
private readonly ILogger<PacketRecorder> _logger;
private readonly ObjectPool<PacketSerializationContext> _pool;
- private readonly SemaphoreSlim _writerLock;
- private readonly FileStream _writer;
+ private readonly Channel<byte[]> _channel;
public PacketRecorder(ILogger<PacketRecorder> logger, IOptions<DebugConfig> options, ObjectPool<PacketSerializationContext> pool)
{
var name = $"session_{DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}.dat";
- var path = Path.Combine(options.Value.GameRecorderPath, name);
+ _path = Path.Combine(options.Value.GameRecorderPath, name);
_logger = logger;
- _logger.LogInformation("PacketRecorder is enabled, writing packets to {0}.", path);
_pool = pool;
- _writerLock = new SemaphoreSlim(1, 1);
- _writer = File.Open(path, FileMode.CreateNew, FileAccess.Write, FileShare.Read);
+
+ _channel = Channel.CreateUnbounded<byte[]>(new UnboundedChannelOptions
+ {
+ SingleReader = true,
+ SingleWriter = false,
+ });
+ }
+
+ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
+ {
+ _logger.LogInformation("PacketRecorder is enabled, writing packets to {0}.", _path);
+
+ var writer = File.Open(_path, FileMode.CreateNew, FileAccess.Write, FileShare.Read);
+
+ // Handle messages.
+ while (!stoppingToken.IsCancellationRequested)
+ {
+ try
+ {
+ var result = await _channel.Reader.ReadAsync(stoppingToken);
+
+ await writer.WriteAsync(result, stoppingToken);
+ await writer.FlushAsync(stoppingToken);
+ }
+ catch (TaskCanceledException)
+ {
+ break;
+ }
+ }
+
+ // Clean up.
+ await writer.DisposeAsync();
}
public async Task WriteConnectAsync(ClientRecorder client)
context.Stream.Position = length;
}
- private async Task WriteAsync(Stream data)
- {
- var hasLock = false;
-
- try
- {
- hasLock = await _writerLock.WaitAsync(TimeSpan.FromMinutes(1));
-
- if (hasLock)
- {
- data.Position = 0;
-
- await data.CopyToAsync(_writer);
- await _writer.FlushAsync();
- }
- }
- finally
- {
- if (hasLock)
- {
- _writerLock.Release();
- }
- }
- }
-
- public void Dispose()
+ private async Task WriteAsync(MemoryStream data)
{
- _writer.Dispose();
- _writerLock.Dispose();
+ await _channel.Writer.WriteAsync(data.ToArray());
}
}
}