fix(runtime): close Campaign LA7b review findings

This commit is contained in:
Erik 2026-08-14 18:55:48 +02:00
parent 0e82cbf700
commit 1b9e7e41f9
11 changed files with 700 additions and 28 deletions

View file

@ -1125,11 +1125,12 @@ public sealed class WorldSession : IDisposable
{
var opcodes = new List<uint>();
ProcessDatagram(datagram.Memory, opcodes);
SweepTransport();
return opcodes.Contains(0xF7DFu)
|| _lastCharacterSelectionError is not null;
},
ReturnInboundDatagram);
ReturnInboundDatagram,
SweepTransport,
TimeSpan.FromMilliseconds(25));
}
if (_lastCharacterSelectionError is { } selectionError)
{
@ -1817,6 +1818,12 @@ public sealed class WorldSession : IDisposable
{
continue;
}
// CharacterError::NumErrors is the enum-count sentinel, not
// a server rejection. Retail never presents it, and treating
// it as an EnterWorld failure would abort either handshake
// pump before a valid ServerReady later in the same packet.
if (parsed.AsCode == CharacterError.Code.NumErrors)
continue;
_lastCharacterSelectionError = parsed;
CharacterErrorReceived?.Invoke(parsed);
}
@ -3339,10 +3346,15 @@ public sealed class WorldSession : IDisposable
ChannelReader<T> reader,
TimeSpan timeout,
Func<T, bool> processAndCheckConfirmation,
Action<T>? release = null)
Action<T>? release = null,
Action? periodicWork = null,
TimeSpan? periodicInterval = null)
{
ArgumentNullException.ThrowIfNull(reader);
ArgumentNullException.ThrowIfNull(processAndCheckConfirmation);
TimeSpan cadence = periodicInterval ?? TimeSpan.FromMilliseconds(25);
if (periodicWork is not null && cadence <= TimeSpan.Zero)
throw new ArgumentOutOfRangeException(nameof(periodicInterval));
using var timeoutSource = new CancellationTokenSource(timeout);
// The deadline is also read straight off the monotonic clock, not only
@ -3382,10 +3394,52 @@ public sealed class WorldSession : IDisposable
return true;
}
bool canRead = reader.WaitToReadAsync(timeoutSource.Token)
.AsTask()
.GetAwaiter()
.GetResult();
periodicWork?.Invoke();
if (timeoutSource.IsCancellationRequested || Expired())
return false;
bool canRead;
if (periodicWork is null)
{
canRead = reader.WaitToReadAsync(timeoutSource.Token)
.AsTask()
.GetAwaiter()
.GetResult();
}
else
{
TimeSpan wait = cadence;
if (bounded)
{
TimeSpan remaining = timeout
- Stopwatch.GetElapsedTime(started);
if (remaining <= TimeSpan.Zero)
return false;
if (remaining < wait)
wait = remaining;
}
using var sliceSource =
CancellationTokenSource.CreateLinkedTokenSource(
timeoutSource.Token);
sliceSource.CancelAfter(wait);
try
{
canRead = reader.WaitToReadAsync(sliceSource.Token)
.AsTask()
.GetAwaiter()
.GetResult();
}
catch (OperationCanceledException)
when (!timeoutSource.IsCancellationRequested
&& !Expired())
{
// This cadence is the paused selector's frame edge:
// keep reliable transport work moving even when no
// datagram arrives to wake the inbound queue.
continue;
}
}
if (!canRead)
return false;
}