DataSenderReceiver
Tsvrc.DataTransfer.DataSenderReceiver sits on top of
DataChunkReceiver and is purely an ergonomics layer: it turns the
receiver's raw hooks (OnTransferStarted, OnChunksAssembled, OnChunkStored, ...) into a
clean, receiver-focused public API: LastData, LastChunkIndex, LastTotalChunks, and
four events (OnDataReceptionStartedEvent, ...StoppedEvent, ...CompletedEvent,
OnDataChunkReceivedEvent) with matching protected virtual hooks a subclass overrides
without ever touching the raw chunk arrays underneath. OnDataReceptionStartedEvent and
...StoppedEvent don't update any Last* property; LastChunkIndex/LastTotalChunks are
set before OnDataChunkReceivedEvent fires, and LastData is set before
OnDataReceptionCompletedEvent fires, so both are always safe to read from inside their
matching subscriber.
The same deferred-emission pattern, one layer up
Just as DataSender defers its stop/complete broadcasts by a frame, DataSenderReceiver
defers its own OnDataReceptionStopped/Completed emission the same way and for the same
reason: at the point these fire, ExecuteStop/ExecuteComplete is still on the call stack,
so a subscriber that reacts by immediately starting a new transfer would corrupt the new
transfer's state if the emission happened synchronously. The assembled data itself is
staged in a private field (_pendingCompletionData) rather than written straight to
LastData, specifically so a new transfer starting inside that one-frame gap, which resets
receiver state, can't clobber it before the deferred emit reads it. LastData is only ever
assigned atomically with the deferred completion event actually firing.
Both pending flags (_pendingCompletion, _pendingStop) are cleared by
ResetReceiverState, which runs when a new transfer starts. That's what suppresses a stale
emission for a transfer that's already been superseded: the deferred call still fires, finds
its flag already cleared, and returns without emitting anything.
What this layer adds vs. what it doesn't
There's no new validation here; every correctness guarantee (sender verification, duplicate
suppression, chunk-count bounds) lives one layer down in DataChunkReceiver and isn't
repeated here.
Usage
Most consumers use DataTransferer directly and never touch this layer.
Subclass DataSenderReceiver itself only if you want the sender/receiver plumbing without
DataTransferer's transfer-specific event names. Override OnDataReceptionCompleted and
read LastData:
protected override void OnDataReceptionCompleted()
{
string received = LastData;
// ...
}