Replay
Record broadcasts server-side (archive) + play a .cdrp recording back through the live client dispatch with play/pause/speed/seek.
Seams you implement 1
Crossplay calls these; your game supplies them. Each ships an inert or permissive default, so register yours before services.AddCrossplayReplayArchive(); and it wins.
IReplaySpectatorHost game SPI
Where a ReplayRecorder spectates from: it seats the recorder as an invisible, receive-only session in a zone, so the recorder is handed exactly the frames a watching client would get. Why this is a seam. Replay's core job is the archive and client playback, which need no world. Only RECORDING a zone needs a broadcast source, so that half lives outside the piece: the Hosting composition answers this seam from World spectating (WorldReplaySpectatorHost) when World is installed, and a game without World can seat the recorder in whatever broadcasts it has. Replay itself references no spatial piece — the same shape as Voice's IVoiceProximityAudience.
-
void Enter(ISession spectator, string zone, float x, float z)Seats spectator in zone around (x, z), receive-only.
-
void Leave(ISession spectator)Removes spectator; nothing more is delivered to it.
Providers you can swap 1
Infrastructure seams. Crossplay ships a working implementation of each — replacing one changes where data lives or how it moves, never a game rule.
IReplayArchive provider
Durable storage for recordings: upload a blob under a game key, list what exists, fetch it back, delete it. Blobs are OPAQUE here — .cdrp broadcast recordings and lockstep input streams both ride the same shelf (the Kind tag tells them apart).
-
bool Delete(string id)Removes a recording.
-
IReadOnlyList<ReplayInfo> List(string key = null)Metadata for every archived recording (optionally only one key's), newest first.
-
string Save(string key, string kind, byte[] blob, long createdMs)Stores a recording blob under a game key.
-
bool TryLoad(string id, out byte[] blob)Fetches a stored blob.
Unity seams you implement 1
The client half's sockets. Bind your implementation in the client context and Crossplay's client calls it — this is where your models, animators, UI and controls plug in.
IReplayPlayer unity SPI
Replay playback control: load a .cdrp recording and play it back through the live client dispatch. The game binds its own scrubber UI to these members. Absent if the piece isn't installed.
-
bool IsLoaded -
bool IsPlaying -
float SpeedPlayback speed (1 = real time). Persists across a Load.
-
double PositionSeconds -
double DurationSeconds -
float Progress0..1 through the recording.
-
event Action FinishedRaised once when playback reaches the end.
-
void Load(byte[] cdrp)Loads a .cdrp blob (parsed via ReplayData), paused at the start.
-
void Play() -
void Pause() -
void Seek(double seconds)Jumps the cursor to a time in seconds (clamped to 0..duration).
UI views you can replace 1
Each ships a working uGUI panel you can drag into a scene. Substitute your own view to keep the logic and change the look — the presenter never knows.
ICrossplayReplayView
Narrow contract the presenter drives. UI-only: render calls in, user intents out.