SplitScript reference / Decision guides
SplitScript decision guides
Use these short guides when the question is which language form owns a task. Open the linked language or standard-library symbol afterward for exact syntax, examples, and effects.
Choose a lifecycle block
| Action | Timing | Available context | Suspension | Result | Fallthrough |
|---|---|---|---|---|---|
| setup | Once, when the loaded module first updates | settings and initialized module globals | not allowed | None | complete setup |
| selectProcess | For each same-name candidate before provider setup and onAttach | the temporary process candidate, settings, and initialized module globals | not allowed; postfix ? and throw reject the candidate | bool with an implicit error boundary | false; reject this candidate |
| onStart | After an observed timer transition out of NotRunning | settings, module globals, and globals initialized here | not allowed | None | complete the event |
| onReset | After an observed timer transition into NotRunning | settings, module globals, and attempt globals | not allowed | None | complete, then clear attempt globals |
| onAttach | Once after acquiring and preparing a process | process, prepared provider roots, settings, and globals | await and retry allowed; ? and throw reject; cancelled on process close | None on success; implicit error boundary | finish attachment |
| onStateReady | Once after the first complete state snapshot | process, provider roots, globals, old, and current | not allowed | None | complete initialization |
| whileAttached | Every initialized attached update after state refresh | process, provider roots, globals, old, and current | await and retry allowed; one invocation is polled per update and cancelled on process close | bool | true; continue to timer decisions after completion; pending skips them |
| start | After whileAttached when the sampled timer is NotRunning | process, provider roots, globals, old, and current | not allowed | bool | false; do not start |
| isLoading | After start handling when the sampled timer is Running or Paused | process, provider roots, globals, old, and current | not allowed | bool? | None; retain the current loading state |
| gameTime | After isLoading when the sampled timer is Running or Paused | process, provider roots, globals, old, and current | not allowed | Duration? | None; retain the current game time |
| reset | After loading and game-time updates when the sampled timer is Running or Paused | process, provider roots, globals, old, and current | not allowed | bool | false; continue to split |
| split | After reset declines when the sampled timer is Running or Paused | process, provider roots, globals, old, and current | not allowed | bool | false; do not split |
| onDetach | Once after a successfully initialized state-provider attachment ends and its context is cleared | settings, module globals, and live attempt globals | not allowed | None | complete cleanup |
Use onAttach for cooperative process discovery, then onStateReady when
initialization needs the first committed old and current snapshots.
Use whileAttached only for per-update bookkeeping that should run before
the timer decisions. onStart and onReset observe timer transitions even
while detached, so they cannot use process or snapshot context.
Choose a state field form
- Use an
atfield when polling follows a fixed module-relative, absolute, or sibling-field pointer path. - Use an expression-backed
statefield when attachment discovery supplies an address, several local steps compute one value, or ordinary API calls read the value. - Keep the field as
Twhen a failed read should retain its last accepted value. UseT?only when absence itself should entercurrentandold. - Put values in one struct or fixed array when they must succeed and advance as one transaction.
- Use independent enum globals for independent build facts. Use one enum when
each build selects one complete alternative memory shape. Guard conditional
stateand managedclassfields with those ordinary values.
Choose absence, failure, retrying, or waiting
T?represents expected absence. Match it or useelsewhen a default is appropriate.T!preserves an error. Use postfix?to transfer it to the nearest fallible function, state field, orretryboundary.- Use
elsefor one local fallback that should happen immediately. - Use
retryfor bounded synchronous work that should be evaluated again from the beginning on a later attached update. - Use
awaitfor one existingasync Toperation that owns its polling progress. Constructing the future does no work; polling it makes progress.
Do not put await inside retry. Await asynchronous discovery first, then
retry a synchronous transaction if the resulting reads can still fail.
Choose the correct string unit
Stringstores immutable UTF-8 text. Equality compares text, not a host language's indexing unit.byteLength,byteAt, andsliceuse UTF-8 byte offsets. A character may occupy several bytes.charAtreads the Unicode scalar value beginning at a proven UTF-8 character boundary; the supplied index is still a byte offset.- Native state-field
utf8bounds bytes and rejects invalid UTF-8. - Native state-field
utf16lebounds two-byte UTF-16 code units and replaces unpaired surrogates while decoding. - Managed
Stringfields usemaxLengthmeasured in UTF-16 code units. This is an allocation/read bound, not a distinct string type.
When porting C# index arithmetic, first decide whether the source position is a UTF-16 code-unit index, a Unicode-scalar index, or a byte offset. Do not carry the number across unchanged until that unit is proven equivalent.