Report CultNet failures during connection

This commit is contained in:
Meta Vens 2026-07-12 10:38:45 +01:00
commit 455dcd8f9e
2 changed files with 39 additions and 4 deletions

View file

@ -764,7 +764,11 @@ namespace GameCult.Mesh
var (host, port) = CultNetSchemaWriteForwarder.ParseEndpoint(endpoint);
client.Connect(host, port);
await WaitForSnapshotClientConnectionAsync(client, endpoint, resolvedOptions.ConnectTimeout)
await WaitForSnapshotClientConnectionAsync(
client,
endpoint,
resolvedOptions.ConnectTimeout,
(client as ICultNetSchemaClientHealth)?.BackgroundFailure)
.ConfigureAwait(false);
client.SendCultNet(new CultNetSnapshotRequestMessage
{
@ -871,11 +875,19 @@ namespace GameCult.Mesh
internal static async Task WaitForSnapshotClientConnectionAsync(
ICultNetSchemaClient client,
string endpoint,
TimeSpan timeout)
TimeSpan timeout,
Task<Exception>? backgroundFailure = null)
{
var deadline = DateTimeOffset.UtcNow + timeout;
while (!client.Connected)
{
if (backgroundFailure?.IsCompleted == true)
{
var error = await backgroundFailure.ConfigureAwait(false);
throw new InvalidOperationException(
$"CultNet snapshot client failed while connecting to {endpoint}.",
error);
}
if (DateTimeOffset.UtcNow >= deadline)
throw new TimeoutException($"Timed out connecting to CultNet snapshot endpoint {endpoint}.");

View file

@ -2674,6 +2674,26 @@ public sealed class CultMeshStreamingTests
failure.Which.Message.Should().Contain("127.0.0.1:3075");
}
[Test]
public async Task SnapshotHelpers_SurfaceBackgroundClientFailureWhileConnecting()
{
var options = new CultMeshSnapshotRequestOptions
{
ConnectTimeout = TimeSpan.FromSeconds(30),
CreateClient = () => new FailedBackgroundSchemaClient(
new FormatException("malformed handshake"),
connected: false)
};
Func<Task> act = async () => await CultMesh.FetchSnapshotAsync(
"rudp://127.0.0.1:3075",
options);
var failure = await act.Should().ThrowAsync<InvalidOperationException>();
failure.Which.InnerException.Should().BeOfType<FormatException>();
failure.Which.Message.Should().Contain("connecting");
}
[Test]
public async Task SnapshotHelpers_DecodeSameSchemaAliasFromForeignSchemaId()
{
@ -3838,15 +3858,18 @@ internal sealed class MeshSnapshotSchemaClient : ICultNetSchemaClient
internal sealed class FailedBackgroundSchemaClient : ICultNetSchemaClient, ICultNetSchemaClientHealth
{
public FailedBackgroundSchemaClient(Exception error)
private readonly bool _connected;
public FailedBackgroundSchemaClient(Exception error, bool connected = true)
{
_connected = connected;
BackgroundFailure = Task.FromResult(error);
}
public bool Connected { get; private set; }
public Task<Exception> BackgroundFailure { get; }
public void Connect(string host, int port) => Connected = true;
public void Connect(string host, int port) => Connected = _connected;
public void SendCultNet<T>(T message) where T : ICultNetSchemaMessage
{