fix server does not shut down
This commit is contained in:
parent
53cbdd8440
commit
8b44168b66
|
@ -7,13 +7,11 @@ internal sealed class GameTickWorker(
|
||||||
IEnumerable<ITickStep> steps,
|
IEnumerable<ITickStep> steps,
|
||||||
IHostApplicationLifetime lifetime,
|
IHostApplicationLifetime lifetime,
|
||||||
ILogger<GameTickWorker> logger
|
ILogger<GameTickWorker> logger
|
||||||
) : IHostedLifecycleService, IDisposable
|
) : BackgroundService, IDisposable
|
||||||
{
|
{
|
||||||
private readonly CancellationTokenSource _cancellation = new();
|
|
||||||
private readonly TaskCompletionSource _shutdownCompletion = new();
|
|
||||||
private readonly List<ITickStep> _steps = steps.ToList();
|
private readonly List<ITickStep> _steps = steps.ToList();
|
||||||
|
|
||||||
public async Task StartedAsync(CancellationToken cancellationToken)
|
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
await Task.Yield();
|
await Task.Yield();
|
||||||
|
|
||||||
|
@ -23,7 +21,7 @@ internal sealed class GameTickWorker(
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
while (!_cancellation.IsCancellationRequested)
|
while (!cancellationToken.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
var delta = sw.Elapsed;
|
var delta = sw.Elapsed;
|
||||||
sw.Restart();
|
sw.Restart();
|
||||||
|
@ -37,19 +35,5 @@ internal sealed class GameTickWorker(
|
||||||
logger.LogError(ex, "game tick service crashed");
|
logger.LogError(ex, "game tick service crashed");
|
||||||
lifetime.StopApplication();
|
lifetime.StopApplication();
|
||||||
}
|
}
|
||||||
|
|
||||||
_shutdownCompletion.SetResult();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task StoppingAsync(CancellationToken cancellationToken) => _cancellation.CancelAsync();
|
|
||||||
|
|
||||||
public Task StopAsync(CancellationToken cancellationToken) => _shutdownCompletion.Task;
|
|
||||||
|
|
||||||
public void Dispose() => _cancellation.Dispose();
|
|
||||||
|
|
||||||
public Task StartAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
|
||||||
|
|
||||||
public Task StartingAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
|
||||||
|
|
||||||
public Task StoppedAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
using ServicePoint;
|
|
||||||
using TanksServer.GameLogic;
|
using TanksServer.GameLogic;
|
||||||
using TanksServer.Interactivity;
|
using TanksServer.Interactivity;
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
using ServicePoint;
|
|
||||||
|
|
||||||
namespace TanksServer.Graphics;
|
namespace TanksServer.Graphics;
|
||||||
|
|
||||||
internal interface IFrameConsumer
|
internal interface IFrameConsumer
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
using System.Net.WebSockets;
|
using System.Net.WebSockets;
|
||||||
using ServicePoint;
|
|
||||||
using TanksServer.Graphics;
|
using TanksServer.Graphics;
|
||||||
|
|
||||||
namespace TanksServer.Interactivity;
|
namespace TanksServer.Interactivity;
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
using System.Net.WebSockets;
|
using System.Net.WebSockets;
|
||||||
using ServicePoint;
|
|
||||||
using TanksServer.Graphics;
|
using TanksServer.Graphics;
|
||||||
|
|
||||||
namespace TanksServer.Interactivity;
|
namespace TanksServer.Interactivity;
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
|
||||||
|
internal class LoggingLifecycleService(ILogger logger) : IHostedLifecycleService
|
||||||
|
{
|
||||||
|
private protected readonly ILogger Logger = logger;
|
||||||
|
|
||||||
|
public virtual Task StartAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
Logger.LogDebug("StartAsync");
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual Task StartedAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
Logger.LogDebug("StartedAsync");
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual Task StartingAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
Logger.LogDebug("StartingAsync");
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual Task StopAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
Logger.LogDebug("StopAsync");
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual Task StoppedAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
Logger.LogDebug("StoppedAsync");
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual Task StoppingAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
Logger.LogDebug("StoppingAsync");
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,6 @@
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using ServicePoint;
|
|
||||||
using TanksServer.GameLogic;
|
using TanksServer.GameLogic;
|
||||||
using TanksServer.Graphics;
|
using TanksServer.Graphics;
|
||||||
|
|
||||||
|
@ -42,13 +41,13 @@ internal sealed class SendToServicePointDisplay : IFrameConsumer, IDisposable
|
||||||
|
|
||||||
var localIp = GetLocalIPv4(displayConfig.Value).Split('.');
|
var localIp = GetLocalIPv4(displayConfig.Value).Split('.');
|
||||||
Debug.Assert(localIp.Length == 4);
|
Debug.Assert(localIp.Length == 4);
|
||||||
_scoresBuffer = new CharGrid(12, 20);
|
_scoresBuffer = new CharGrid(ScoresWidth, ScoresHeight);
|
||||||
|
|
||||||
_scoresBuffer.SetRow(00, "== TANKS! ==");
|
_scoresBuffer.SetRow(00, "== TANKS! ==");
|
||||||
_scoresBuffer.SetRow(01, "-- scores --");
|
_scoresBuffer.SetRow(01, "-- scores --");
|
||||||
_scoresBuffer.SetRow(17, "-- join --");
|
_scoresBuffer.SetRow(17, "-- join --");
|
||||||
_scoresBuffer.SetRow(18, string.Join('.', localIp[..2]));
|
_scoresBuffer.SetRow(18, $"{localIp[0]}.{localIp[1]}".PadRight(ScoresWidth));
|
||||||
_scoresBuffer.SetRow(19, string.Join('.', localIp[2..]));
|
_scoresBuffer.SetRow(19, $"{localIp[2]}.{localIp[3]}".PadRight(ScoresWidth));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task OnFrameDoneAsync(GamePixelGrid gamePixelGrid, Bitmap observerPixels)
|
public async Task OnFrameDoneAsync(GamePixelGrid gamePixelGrid, Bitmap observerPixels)
|
||||||
|
@ -101,7 +100,7 @@ internal sealed class SendToServicePointDisplay : IFrameConsumer, IDisposable
|
||||||
for (; row < 16; row++)
|
for (; row < 16; row++)
|
||||||
_scoresBuffer.SetRow(row, new string(' ', ScoresWidth));
|
_scoresBuffer.SetRow(row, new string(' ', ScoresWidth));
|
||||||
|
|
||||||
_scoresBuffer.SetRow(16, _mapService.Current.Name[..(Math.Min(ScoresWidth, _mapService.Current.Name.Length) - 1)]);
|
_scoresBuffer.SetRow(16, _mapService.Current.Name[..(Math.Min(ScoresWidth, _mapService.Current.Name.Length) - 1)].PadRight(ScoresWidth));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetLocalIPv4(DisplayConfiguration configuration)
|
private static string GetLocalIPv4(DisplayConfiguration configuration)
|
||||||
|
|
|
@ -1,23 +1,23 @@
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using Microsoft.Extensions.Hosting;
|
|
||||||
|
|
||||||
namespace TanksServer.Interactivity;
|
namespace TanksServer.Interactivity;
|
||||||
|
|
||||||
internal abstract class WebsocketServer<T>(
|
internal abstract class WebsocketServer<T>(
|
||||||
ILogger logger
|
ILogger logger
|
||||||
) : IHostedLifecycleService
|
) : LoggingLifecycleService(logger)
|
||||||
where T : WebsocketServerConnection
|
where T : WebsocketServerConnection
|
||||||
{
|
{
|
||||||
private bool _closing;
|
private bool _closing;
|
||||||
private readonly ConcurrentDictionary<T, byte> _connections = [];
|
private readonly ConcurrentDictionary<T, byte> _connections = [];
|
||||||
|
|
||||||
public async Task StoppingAsync(CancellationToken cancellationToken)
|
public async override Task StoppingAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
|
await base.StoppingAsync(cancellationToken);
|
||||||
_closing = true;
|
_closing = true;
|
||||||
logger.LogInformation("closing connections");
|
Logger.LogInformation("closing connections");
|
||||||
await _connections.Keys.Select(c => c.CloseAsync())
|
await _connections.Keys.Select(c => c.CloseAsync())
|
||||||
.WhenAll();
|
.WhenAll();
|
||||||
logger.LogInformation("closed connections");
|
Logger.LogInformation("closed connections");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected IEnumerable<T> Connections => _connections.Keys;
|
protected IEnumerable<T> Connections => _connections.Keys;
|
||||||
|
@ -26,7 +26,7 @@ internal abstract class WebsocketServer<T>(
|
||||||
{
|
{
|
||||||
if (_closing)
|
if (_closing)
|
||||||
{
|
{
|
||||||
logger.LogWarning("refusing connection because server is shutting down");
|
Logger.LogWarning("refusing connection because server is shutting down");
|
||||||
await connection.CloseAsync();
|
await connection.CloseAsync();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -39,14 +39,4 @@ internal abstract class WebsocketServer<T>(
|
||||||
_ = _connections.TryRemove(connection, out _);
|
_ = _connections.TryRemove(connection, out _);
|
||||||
connection.Dispose();
|
connection.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task StartAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
|
||||||
|
|
||||||
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
|
||||||
|
|
||||||
public Task StartedAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
|
||||||
|
|
||||||
public Task StartingAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
|
||||||
|
|
||||||
public Task StoppedAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,8 +67,8 @@ public static class Program
|
||||||
builder.Services.AddSingleton<UpdatesPerSecondCounter>();
|
builder.Services.AddSingleton<UpdatesPerSecondCounter>();
|
||||||
|
|
||||||
builder.Services.AddHostedService<GameTickWorker>();
|
builder.Services.AddHostedService<GameTickWorker>();
|
||||||
builder.Services.AddHostedService(sp => sp.GetRequiredService<ControlsServer>());
|
builder.Services.AddHostedService(FromServices<ControlsServer>);
|
||||||
builder.Services.AddHostedService(sp => sp.GetRequiredService<ClientScreenServer>());
|
builder.Services.AddHostedService(FromServices<ClientScreenServer>);
|
||||||
|
|
||||||
builder.Services.AddSingleton<ITickStep, ChangeToRequestedMap>(sp =>
|
builder.Services.AddSingleton<ITickStep, ChangeToRequestedMap>(sp =>
|
||||||
sp.GetRequiredService<ChangeToRequestedMap>());
|
sp.GetRequiredService<ChangeToRequestedMap>());
|
||||||
|
@ -113,4 +113,6 @@ public static class Program
|
||||||
|
|
||||||
return app;
|
return app;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static T FromServices<T>(IServiceProvider sp) where T : notnull => sp.GetRequiredService<T>();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue