Orrery Terrain · OrreryTerrainCore module · class
Native::
FJoinStreamWriter
ExperimentalThe server's half of one late join: a save pinned at S (PlanSave), cut into frames and reordered for the joiner.
Declaration
class FJoinStreamWriter- Header
#include "Native/Net/NetJoin.h"- Module
PublicDependencyModuleNames.Add("OrreryTerrainCore");in your module’s Build.cs.- Stability
- Experimental: supported, may change in a minor version, with a changelog line.
- Thread
- Construct and Prioritise on the game thread; Prepare on any thread (the save's work), at most once; after it, Produce on any thread, one call at a time, and Read on one thread at a time.
Description
Order. The head; then one start chunk per open stroke tile with a Flatten start, in the head's order; then one mask frame per brush mask the head lists, in its order; then the load set, which is every byte of the save but the sculpt codes (the header, the stack, the paint, the sculpt index with each tile's summary, and the trailer), so the joiner can load and show the sculpted shapes from the summaries; then the codes, a joiner page at a time: first the open strokes', then those the log after S touches (Prioritise), then by distance from the joiner's interest (ties by page row, then column); then the end frame.
Frames. U8 kind, U32 payload length, the payload, then a U64 H5 (HashBytesV5) over kind, length and payload, so any one changed byte is found. Head: the FJoinHead, each tile with a start flag but not its start (at most kJoinMaxHeadBytes). Start: U32 stroke index in the head, U32 tile index in its stroke, then the tile's start (at most kJoinMaxStartBytes in all). Mask: one BrushMask record's full payload (EncodeBrushMaskRecord), compressed, its hash checked after decompression. Bytes: U64 offset in the save, then at most kJoinFrameBytes of it. End: U64 Bytes frames sent, U64 save bytes sent. The head ends with U32 mask count and, per mask, U64 hash and U32 canonical size, then the limits: U32 MaxRadius_mm, I32 MaxStrength_mm, U32 KeepOutMargin_mm, U8 MaxPaintLayer, U8 flags (bit 0 bPaintGroundClass, 1 bPaintHoles, 2 bStamps, the rest 0), U32 MaxStampRadius_mm, I32 MaxStampHeight_mm, U32 MaxStampTiles.
Editing goes on while the join streams: the plan keeps the moment S alive (copy-on-write), and the joiner gets the records after S on the same ordered link.
Producing. Prepare writes the save into a store (FJoinStreamConfig::MakeSaveStore). Produce frames the stream from the store into a ready ring of about FJoinStreamConfig::ReadAheadBytes, and Read copies out of the ring. The frames are the same bytes whichever store holds the save and whenever Produce runs. Only the code pages' order depends on when Prioritise lands relative to Produce, which the reader allows: frames carry their offsets.
Tier 1. Construct and Prioritise on the game thread; Prepare on any thread (the save's work), at most once; after it, Produce on any thread, one call at a time, and Read on one thread at a time. The ring, under its own lock, is all Produce and Read share, and Produce reads the store outside that lock, so Read never waits on the store. Prioritise may run while Prepare, Produce or Read does: it only queues.
Functions
- FJoinStreamWriter (2 overloads)
Pin the save and the checkpoint at S.
- operator=
- Prepare
Check the head's size, encode the start chunks, write the save into the store (memory when
FJoinStreamConfig::MakeSaveStoreis empty), read its header and index back through the store's file and plan the frames. - Prioritise
A record after S is about to go to the joiner: the pages a brush step reads (its window, one tile around) are sent next, so the joiner's replica holds for as short a time as it can.
- Produce
Frame the stream ahead of Read: frames go into the ready ring until it holds
FJoinStreamConfig::ReadAheadBytesor the end frame is in. - WantsProduce
The ready ring holds under half of
FJoinStreamConfig::ReadAheadBytesand frames remain, and the stream is not waiting for the joiner's answer: a Produce is due. - WaitsForHeldCodes
Whether the stream framed everything up to the sculpt codes and waits for the joiner's answer (a HeldCodes stream only).
- HeldAnswerBytes
The exact size of the joiner's answer (
FJoinStreamReader::CacheAnswer): 0 unless this is a HeldCodes stream. - TakeHeldCodes
Take the joiner's answer, once: the stream then frames only the codes the joiner does not hold.
- Read
The next bytes of the stream, at most Capacity, copied out of the ready ring.
- Done
The end frame is out (read).
- Status
Ok while the stream goes; ReadFailed once a Produce read failed. Any thread.
- Head
Complete after Prepare (StateHash, FileSize, FileHash and the code area come from it).
- StreamBytes
The whole stream's size, frames included; 0 before Prepare and after ReadFailed. A HeldCodes stream counts every code until its answer is applied.
- BytesRead
Read so far.
- ReadyBytes
Stream bytes in the ready ring, which Read takes without framing. Any thread.
- ResidentBytes
The writer's own heap, bytes: the head, the page plan and the queues, the start chunks not yet framed and the ready ring, plus the save when it is kept in memory.