Update .gitignore to exclude TypeScript build info and add dist directory. Expand README with project overview, technology stack, prerequisites, and instructions for running and testing the application.
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
using System.Net.WebSockets;
|
||||
using System.Threading.Channels;
|
||||
|
||||
namespace HSchool.Server.Net;
|
||||
|
||||
/// <summary>
|
||||
/// One connected browser. Frames are queued instead of written inline so a slow client
|
||||
/// can never stall the game loop; when the outbox overflows the oldest snapshot is dropped,
|
||||
/// which is exactly what you want for state that is resent 20 times a second.
|
||||
/// </summary>
|
||||
internal sealed class GameClient(uint playerId, WebSocket socket)
|
||||
{
|
||||
private const int OutboxCapacity = 32;
|
||||
|
||||
private readonly Channel<ReadOnlyMemory<byte>> _outbox =
|
||||
Channel.CreateBounded<ReadOnlyMemory<byte>>(new BoundedChannelOptions(OutboxCapacity)
|
||||
{
|
||||
FullMode = BoundedChannelFullMode.DropOldest,
|
||||
SingleReader = true,
|
||||
SingleWriter = false,
|
||||
});
|
||||
|
||||
private bool _ready;
|
||||
|
||||
public uint PlayerId { get; } = playerId;
|
||||
|
||||
public WebSocket Socket { get; } = socket;
|
||||
|
||||
public string Name { get; set; } = $"player-{playerId}";
|
||||
|
||||
/// <summary>
|
||||
/// Set once the welcome frame is out. Snapshots are only queued for ready clients, so a
|
||||
/// connection never sees world state before it knows its own entity id.
|
||||
/// </summary>
|
||||
public bool IsReady => Volatile.Read(ref _ready);
|
||||
|
||||
public void MarkReady() => Volatile.Write(ref _ready, true);
|
||||
|
||||
/// <summary>Queues a frame. Returns false once the connection is shutting down.</summary>
|
||||
public bool TrySend(ReadOnlyMemory<byte> frame) => _outbox.Writer.TryWrite(frame);
|
||||
|
||||
/// <summary>Pumps queued frames to the socket until cancelled or the outbox completes.</summary>
|
||||
public async Task RunSendLoopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
await foreach (var frame in _outbox.Reader.ReadAllAsync(cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
if (Socket.State != WebSocketState.Open)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
await Socket.SendAsync(frame, WebSocketMessageType.Binary, endOfMessage: true, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void CompleteOutbox() => _outbox.Writer.TryComplete();
|
||||
}
|
||||
Reference in New Issue
Block a user