do not allocate new memory each tick for player info

This commit is contained in:
Vinzenz Schroeter 2024-05-03 10:49:50 +02:00 committed by RobbersDaughter
parent abad2c95c8
commit b3bf62b391

View file

@ -1,3 +1,5 @@
using System.Buffers;
using System.IO;
using System.Net.WebSockets; using System.Net.WebSockets;
using System.Text.Json; using System.Text.Json;
using TanksServer.GameLogic; using TanksServer.GameLogic;
@ -6,11 +8,15 @@ namespace TanksServer.Interactivity;
internal sealed class PlayerInfoConnection : WebsocketServerConnection internal sealed class PlayerInfoConnection : WebsocketServerConnection
{ {
private int _wantsInfoOnTick = 1;
private byte[]? _lastMessage = null;
private byte[]? _nextMessage = null;
private readonly Player _player; private readonly Player _player;
private readonly MapEntityManager _entityManager; private readonly MapEntityManager _entityManager;
private readonly MemoryStream _tempStream = new();
private readonly MemoryPool<byte> _memoryPool = MemoryPool<byte>.Shared;
private int _wantsInfoOnTick = 1;
private Package? _lastMessage = null;
private Package? _nextMessage = null;
private sealed record class Package(IMemoryOwner<byte> Owner, Memory<byte> Memory);
public PlayerInfoConnection(Player player, public PlayerInfoConnection(Player player,
ILogger logger, ILogger logger,
@ -36,7 +42,7 @@ internal sealed class PlayerInfoConnection : WebsocketServerConnection
{ {
await Task.Yield(); await Task.Yield();
var response = GetMessageToSend(); var response = await GenerateMessageAsync();
var wantsNow = Interlocked.Exchange(ref _wantsInfoOnTick, 0) != 0; var wantsNow = Interlocked.Exchange(ref _wantsInfoOnTick, 0) != 0;
if (wantsNow) if (wantsNow)
@ -54,7 +60,7 @@ internal sealed class PlayerInfoConnection : WebsocketServerConnection
return ValueTask.CompletedTask; return ValueTask.CompletedTask;
} }
private byte[] GetMessageToSend() private async ValueTask<Package> GenerateMessageAsync()
{ {
var tank = _entityManager.GetCurrentTankOfPlayer(_player); var tank = _entityManager.GetCurrentTankOfPlayer(_player);
@ -72,13 +78,21 @@ internal sealed class PlayerInfoConnection : WebsocketServerConnection
tankInfo, tankInfo,
_player.OpenConnections); _player.OpenConnections);
// TODO: switch to async version with pre-allocated buffer / IMemoryOwner _tempStream.Position = 0;
return JsonSerializer.SerializeToUtf8Bytes(info, AppSerializerContext.Default.PlayerInfo); await JsonSerializer.SerializeAsync(_tempStream, info, AppSerializerContext.Default.PlayerInfo);
var messageLength = (int)_tempStream.Position;
var owner = _memoryPool.Rent(messageLength);
var package = new Package(owner, owner.Memory[..messageLength]);
_tempStream.Position = 0;
await _tempStream.ReadExactlyAsync(package.Memory);
return package;
} }
private async ValueTask SendAndDisposeAsync(byte[] data) private async ValueTask SendAndDisposeAsync(Package data)
{ {
await Socket.SendTextAsync(data); await Socket.SendTextAsync(data.Memory);
Interlocked.Exchange(ref _lastMessage, data); Interlocked.Exchange(ref _lastMessage, data)?.Owner.Dispose();
} }
} }