ChunkedTransferSession
Tsvrc.DataTransfer.ChunkedTransferSession sends a message split by DataChunker
one chunk at a time: each chunk runs its own ready
check, in which every target player acknowledges it,
before the next chunk starts. It only owns the chunk sequence and sends nothing over the network
itself. It's one layer of the data transfer chain, which names the type
to use.
The sequence
TransferData(data, playerIds) stores the data and starts a ready check over the target players.
Each completed ready check (OnProcessCompleted) starts the ready check of the next chunk, or, after
the last chunk, fires OnChunkSequenceCompleted. A subclass overrides four protected virtual hooks
to broadcast the transitions:
OnChunkSequenceStarted: chunk 1 is about to go out.OnChunkSequenceStopped: the transfer was cancelled, or every target left.OnChunkSequenceCompleted: the ready check of the last chunk completed.OnDataChunkSendRequested(dataChunk, chunkIndex, totalChunks, playerIds): fires for every chunk, and is where a subclass sends it over the network.
If every tracked player leaves or is suspended during a ready check, the check can't
complete. ChunkedTransferSession
overrides OnProcessUpdate to find a running process with no tracked players and calls
CancelDataTransfer(), so the transfer doesn't wait forever.
Between chunks
Between two chunks, the underlying Process isn't running, and a stopped ReadyCheckProcess looks
the same whether the transfer ended or the next chunk hasn't started yet. ChunkedTransferSession
marks that gap with the synced _pendingNextChunk flag, and starts the next ready check one frame
later (SendCustomEventDelayedSeconds(..., 0f)) so the tick loop of the previous chunk ends first.
The flag is synced so a new owner can tell it took over in the gap.
When the next ready check starts, the list of targets is filtered down to players who are present
and not suspended. OnPlayerLeft and OnPlayerSuspendChanged don't remove anyone during the gap,
since both check IsProcessRunning().
A new owner during a transfer
Transfer state (_dataChunks and the chunk indices) isn't synced. Only the owner needs it, and
keeping it off the network means the periodic resync
never sends the rest of the message again. A new owner therefore starts with no chunk state.
OnBecameProcessOwner handles that. If a ready check is running, it stops it, since a check on
zeroed indices could otherwise read as a completed transfer. If the handover happened in the gap
between chunks, it broadcasts the stopped event so receivers clean up instead of waiting for a chunk
that won't come.
OnDeserialization handles two late packets from the previous owner. A packet that says the
transfer is between chunks, arriving after the new owner already handled the gap, is turned into a
stop. A packet that marks the process running again with no chunk data behind it stops the ready
check. The comments in the source describe both orderings.
Cancellation
CancelDataTransfer() can be called at any point, including between chunks, and does nothing when
no transfer is active. Between chunks it resets the state and fires the stopped hook right away.
After a ready check completed but before its cleanup ran, StopReadyCheck would do nothing, so it
sets an internal flag that the next OnProcessCleanup reads to stop instead of starting the next
chunk. After the ready check of the last chunk, cancelling does nothing, since the transfer is
already completing.
Adding players during a transfer
CanAcceptTrackedPlayerAdditions rejects new tracked players with a warning once
_currentChunkIndex > 0, which covers chunks in flight and the gaps between them. A player added
after the first chunk would fail the player-list check of every later chunk, could never call
SetReady, and would block the ready check forever. Removing a tracked player during a transfer is
allowed, since that's how departures are handled.