2024-04-12 16:05:24 +02:00
|
|
|
using System.Diagnostics;
|
2024-05-13 01:23:34 +02:00
|
|
|
using System.Net;
|
2024-04-12 16:05:24 +02:00
|
|
|
using System.Net.Sockets;
|
2024-05-26 15:06:09 +02:00
|
|
|
using ServicePoint;
|
2024-04-12 16:05:24 +02:00
|
|
|
using TanksServer.GameLogic;
|
|
|
|
using TanksServer.Graphics;
|
|
|
|
|
|
|
|
namespace TanksServer.Interactivity;
|
|
|
|
|
2024-10-19 16:07:41 +02:00
|
|
|
internal sealed class SendToServicePointDisplay : IFrameConsumer, IDisposable
|
2024-04-12 16:05:24 +02:00
|
|
|
{
|
2024-04-13 14:08:51 +02:00
|
|
|
private const int ScoresWidth = 12;
|
|
|
|
private const int ScoresHeight = 20;
|
2024-04-19 13:41:53 +02:00
|
|
|
private const int ScoresPlayerRows = ScoresHeight - 6;
|
2024-04-13 14:08:51 +02:00
|
|
|
|
2024-05-13 01:23:34 +02:00
|
|
|
private readonly Connection _displayConnection;
|
2024-04-19 13:41:53 +02:00
|
|
|
private readonly MapService _mapService;
|
2024-04-12 16:05:24 +02:00
|
|
|
private readonly ILogger<SendToServicePointDisplay> _logger;
|
2024-04-13 14:08:51 +02:00
|
|
|
private readonly PlayerServer _players;
|
2024-11-12 18:27:04 +01:00
|
|
|
private readonly CharGrid _scoresBuffer;
|
2024-04-16 21:34:54 +02:00
|
|
|
private readonly TimeSpan _minFrameTime;
|
2024-05-08 01:00:11 +02:00
|
|
|
private readonly IOptionsMonitor<HostConfiguration> _options;
|
2024-04-16 21:34:54 +02:00
|
|
|
|
|
|
|
private DateTime _nextFailLogAfter = DateTime.Now;
|
|
|
|
private DateTime _nextFrameAfter = DateTime.Now;
|
2024-04-12 16:05:24 +02:00
|
|
|
|
|
|
|
public SendToServicePointDisplay(
|
|
|
|
PlayerServer players,
|
|
|
|
ILogger<SendToServicePointDisplay> logger,
|
2024-05-13 01:23:34 +02:00
|
|
|
Connection displayConnection,
|
2024-04-19 13:41:53 +02:00
|
|
|
IOptions<HostConfiguration> hostOptions,
|
2024-05-08 01:00:11 +02:00
|
|
|
MapService mapService,
|
2024-05-13 01:23:34 +02:00
|
|
|
IOptionsMonitor<HostConfiguration> options,
|
|
|
|
IOptions<DisplayConfiguration> displayConfig)
|
2024-04-12 16:05:24 +02:00
|
|
|
{
|
|
|
|
_players = players;
|
|
|
|
_logger = logger;
|
|
|
|
_displayConnection = displayConnection;
|
2024-04-19 13:41:53 +02:00
|
|
|
_mapService = mapService;
|
2024-04-16 21:34:54 +02:00
|
|
|
_minFrameTime = TimeSpan.FromMilliseconds(hostOptions.Value.ServicePointDisplayMinFrameTimeMs);
|
2024-05-08 01:00:11 +02:00
|
|
|
_options = options;
|
2024-04-12 16:05:24 +02:00
|
|
|
|
2024-05-13 01:23:34 +02:00
|
|
|
var localIp = GetLocalIPv4(displayConfig.Value).Split('.');
|
2024-04-12 16:05:24 +02:00
|
|
|
Debug.Assert(localIp.Length == 4);
|
2024-11-12 18:27:04 +01:00
|
|
|
_scoresBuffer = new CharGrid(12, 20);
|
2024-05-13 01:23:34 +02:00
|
|
|
|
2024-11-12 18:27:04 +01:00
|
|
|
_scoresBuffer.SetRow(00, "== TANKS! ==");
|
|
|
|
_scoresBuffer.SetRow(01, "-- scores --");
|
|
|
|
_scoresBuffer.SetRow(17, "-- join --");
|
|
|
|
_scoresBuffer.SetRow(18, string.Join('.', localIp[..2]));
|
|
|
|
_scoresBuffer.SetRow(19, string.Join('.', localIp[2..]));
|
2024-04-12 16:05:24 +02:00
|
|
|
}
|
|
|
|
|
2024-10-16 20:15:32 +02:00
|
|
|
public async Task OnFrameDoneAsync(GamePixelGrid gamePixelGrid, Bitmap observerPixels)
|
2024-04-12 16:05:24 +02:00
|
|
|
{
|
2024-05-08 01:00:11 +02:00
|
|
|
if (!_options.CurrentValue.EnableServicePointDisplay)
|
|
|
|
return;
|
|
|
|
|
2024-04-16 21:34:54 +02:00
|
|
|
if (DateTime.Now < _nextFrameAfter)
|
|
|
|
return;
|
2024-04-30 23:49:39 +02:00
|
|
|
|
2024-04-16 21:34:54 +02:00
|
|
|
_nextFrameAfter = DateTime.Now + _minFrameTime;
|
2024-04-30 23:49:39 +02:00
|
|
|
await Task.Yield();
|
2024-04-16 21:34:54 +02:00
|
|
|
|
2024-04-12 16:05:24 +02:00
|
|
|
RefreshScores();
|
2024-04-16 21:34:54 +02:00
|
|
|
|
2024-04-12 16:05:24 +02:00
|
|
|
try
|
|
|
|
{
|
2024-11-12 18:27:04 +01:00
|
|
|
_displayConnection.Send(Command.BitmapLinearWin(0, 0, observerPixels, CompressionCode.Lzma));
|
|
|
|
_displayConnection.Send(Command.Cp437Data(MapService.TilesPerRow, 0, _scoresBuffer.ToCp437()));
|
2024-04-12 16:05:24 +02:00
|
|
|
}
|
|
|
|
catch (SocketException ex)
|
|
|
|
{
|
2024-04-16 21:34:54 +02:00
|
|
|
if (DateTime.Now > _nextFailLogAfter)
|
2024-04-12 16:05:24 +02:00
|
|
|
{
|
|
|
|
_logger.LogWarning("could not send data to service point display: {}", ex.Message);
|
2024-04-16 21:34:54 +02:00
|
|
|
_nextFailLogAfter = DateTime.Now + TimeSpan.FromSeconds(5);
|
2024-04-12 16:05:24 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void RefreshScores()
|
|
|
|
{
|
2024-05-03 14:45:41 +02:00
|
|
|
var playersToDisplay = _players.Players
|
2024-04-13 18:35:36 +02:00
|
|
|
.OrderByDescending(p => p.Scores.Kills)
|
2024-04-12 16:05:24 +02:00
|
|
|
.Take(ScoresPlayerRows);
|
|
|
|
|
|
|
|
ushort row = 2;
|
|
|
|
foreach (var p in playersToDisplay)
|
|
|
|
{
|
2024-04-13 18:35:36 +02:00
|
|
|
var score = p.Scores.Kills.ToString();
|
2024-04-12 16:05:24 +02:00
|
|
|
var nameLength = Math.Min(p.Name.Length, ScoresWidth - score.Length - 1);
|
|
|
|
|
|
|
|
var name = p.Name[..nameLength];
|
|
|
|
var spaces = new string(' ', ScoresWidth - score.Length - nameLength);
|
|
|
|
|
2024-11-12 18:27:04 +01:00
|
|
|
_scoresBuffer.SetRow(row, name + spaces + score);
|
2024-04-12 16:05:24 +02:00
|
|
|
row++;
|
|
|
|
}
|
|
|
|
|
2024-04-19 13:41:53 +02:00
|
|
|
for (; row < 16; row++)
|
2024-11-12 18:27:04 +01:00
|
|
|
_scoresBuffer.SetRow(row, new string(' ', ScoresWidth));
|
2024-04-19 13:41:53 +02:00
|
|
|
|
2024-11-12 18:27:04 +01:00
|
|
|
_scoresBuffer.SetRow(16, _mapService.Current.Name[..(Math.Min(ScoresWidth, _mapService.Current.Name.Length) - 1)]);
|
2024-04-12 16:05:24 +02:00
|
|
|
}
|
2024-05-13 01:23:34 +02:00
|
|
|
|
|
|
|
private static string GetLocalIPv4(DisplayConfiguration configuration)
|
|
|
|
{
|
|
|
|
using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, 0);
|
|
|
|
socket.Connect(configuration.Hostname, configuration.Port);
|
|
|
|
var endPoint = socket.LocalEndPoint as IPEndPoint ?? throw new NotSupportedException();
|
|
|
|
return endPoint.Address.ToString();
|
|
|
|
}
|
2024-10-19 16:07:41 +02:00
|
|
|
|
|
|
|
public void Dispose()
|
|
|
|
{
|
|
|
|
_displayConnection.Dispose();
|
|
|
|
_scoresBuffer.Dispose();
|
|
|
|
}
|
2024-04-13 14:08:51 +02:00
|
|
|
}
|