The live link
The live link connects Studio to a running game through local shared memory. It has no network listener, starts no game thread and writes nothing into game memory. It adds no Activity Host inputs. Connect to a running game covers the steps.
How it attaches
The game side is off by default. core.studio_attach turns it on. See
Settings, logs and in-game tools. Only a program in the same
Windows session on the same machine can open the mappings.
Attach creates the camera and world mappings. Studio creates the others when it first needs them.
The game only opens a mapping that already exists. The game’s existing camera callback publishes into
them while Studio keeps a fresh heartbeat. The CUI mapping is the exception: a hook on the UI frame
publishes it. See CUI.
Detach releases them. The animation hook and the UI frame hook are installed whenever
studio_attach is on. They do nothing until Studio opens their mapping.
| mapping | carries | version |
|---|---|---|
Local\SunriseStudio.Live.<pid> | the camera frame | 1 |
Local\SunriseStudio.World.<pid> | world and object rows | 4 |
Local\SunriseStudio.Pose.<pid> | animation | 5 |
Local\SunriseStudio.Object.<pid> | the selected object | 1 |
Local\SunriseStudio.Cui.<pid> | open CUI screens, one widget tree, one widget record | 1 |
Local\SunriseStudio.Command.<pid> | commands | 1 |
Studio reports a missing or mismatched bridge with an update and restart note and ignores its data.
What the game sends
| surface | data | when |
|---|---|---|
| camera | position, forward and up, horizontal field of view, aspect | every camera callback |
| player | physics position and the controlled handle | every camera callback |
| world | loaded map root and definition, active scenario, activity package name | every 500 ms, or at once on entering or leaving the world |
| bubbles | resident and current slice identities | every 500 ms |
| object rows | identity, handle and generation, class, position, rotation, scale, raw flags, the placed-not-hidden bit | a scan at most every 50 ms |
| animation | world-space skinning palettes of the owners Studio asks for | at most every 50 ms, while on |
| selected object | parent, root, attached objects, drawn models, raw datum bytes | once per request |
| CUI screens | the open screens of both layers, in list order, and each layer’s top screen | at most every 100 ms, only when they change |
| CUI tree | the widgets of the screen Studio watches | at most every 100 ms, only when they change |
| CUI widget | one widget’s record | once per request |
A placement scan yields after half a millisecond of each game frame. Each row carries its own read time. World changes invalidate unfinished scans and earlier placement data.
Placed and not hidden
The game side has one test, is_placed_and_not_hidden. Its result is a bit on each object row. Studio
draws a full model only for rows with the bit set. Other rows are markers. The test reads the datum
flags at +4 of the object and of every ancestor up to its root.
| condition | datum flags |
|---|---|
| built | flags 1 and 2 of the object are clear |
| placed in the world | the root has 0x20: it is linked into spatial cells |
| not hidden | the object lacks 0x800, and no datum up the chain has 0x400, 0x80 or 4 |
Rows are filtered before the test.
| case | effect |
|---|---|
| handle, generation or placement have changed | left out and counted as excluded |
| flag 4 (destroyed) on the object | left out and counted as excluded |
| the parent chain cannot be read | the bit is unknown; the row is treated as placed |
| flag 1 (not yet built) | the position is refused, so the marker has no position |
The test says nothing about active or visible. No per-object active bit is known. Visibility is a
property of each render object, not of the datum. A root with no spatial record never gets 0x20.
Whether such an object is ever drawn is unverified.
Placements
A mission scene can hold the same placement once per state. Copies with the same list, entry and placement identity are one game object, and one live row moves all of them. A tuple that still has several candidates is ambiguous and does not move.
Placements hides authored placements the game has not spawned, but only from an exhaustive snapshot: complete, not cut off, and its rows plus its excluded rows equal the registry total. A busy or cut-off registry hides nothing. A placed row whose bit is clear hides its authored copy and draws as a marker.
Only objects more than a centimeter from their authored pose move. A moved static mesh draws with the diagnostic shader, because the game shader path cannot move it. Observed uniform scale replaces the authored scale. Live transforms never enter exports or project edits.
Spawned entities
A class prototype loads only for a spawned row whose bit is set. Other rows share one marker prototype and load nothing. Prototypes load once and share GPU resources. Classes, drawn models and the marker share a limit of 256 prototypes, and a scene shows at most 2048 pooled instances.
The selected object
Selecting a live object sends one request with its handle and generation. The game answers once, on its next camera callback, with one record. Nothing is pushed again. Refresh sends a new request.
| field | source |
|---|---|
| identity, class, flags, placed-not-hidden bit | the object’s datum at answer time |
| position, rotation, scale | the object’s datum at answer time |
| parent handle | the object’s datum |
| root handle, root flags, chain flags | the parent chain at answer time |
| attached objects | rows of the last scan whose parent is this object, at most 64 |
| drawn models | draws of this owner in the last animation capture, at most 32 |
| raw datum | the 224 bytes of the datum layout the reader knows |
Animation
Studio writes an owner filter into the pose control block: one bit per object slot, for the owners it draws a model for, the player and the selection. The game copies a completed skinning palette only for owners in that filter. It never calls the animation evaluator. The palette is already in world space and includes inverse bind transforms.
A part is posed only when its loaded model constants match the captured ones and a matching skinning shader exists. A draw that stops appearing expires after 250 ms. A capture expires after 500 ms. A draw holds at most 512 bones. One capture holds at most 1024 draws and 131,072 palette vectors. Draws match full handles and generations, so a reused slot never inherits an older pose.
CUI
The CUI link mirrors the game’s open UI screens and their widgets. It is read only. It has not run in a game yet.
| field | value |
|---|---|
| mapping | Local\SunriseStudio.Cui.<pid> |
| magic | 0x55435353, ASCII SSCU |
| version | 1 |
| size | 460,736 bytes |
| created by | Studio, on first use |
| opened by | the game |
Studio creates the mapping when the Screens tab, CUI screen sync or the CUI page’s Game state first needs it. It writes the version, size, its process id and the heartbeat, then the magic last. The game writes its own version when it connects, and its process id and a ready flag on every capture. From the mapping it reads only the header, the heartbeat and Studio’s requests.
Where the game reads
The game reads in a hook on its UI frame function. That function runs the UI update, then builds the UI draw list. The hook calls it first, then copies. The copy therefore runs on the thread that just updated the screens and widgets, after the update. The camera callback is not used for CUI. Which thread runs the frame function is unverified.
Every read is a guarded copy. A fault fails that read only. Walks are bounded: at most 32 screens, 8,192 widget rows, and 16,384 visited nodes. No screen or widget pointer is kept past one frame.
What Studio asks
| request | target | answer |
|---|---|---|
| watch | hierarchy tag, ordinal | the widget tree of that screen, while the watch holds |
| widget | hierarchy tag, ordinal, node id | one record of that widget |
The ordinal picks among open screens with the same hierarchy tag. 0 is the first. Studio writes the target, then a new non-zero request id. The game reads the id, the target, then the id again, and skips a torn read.
Capture and publish
The game captures at most once every 100 ms.
- It checks the header and the heartbeat. A heartbeat older than 3 seconds drops the link.
- It writes
checkedTime. - It walks the open screens. It rewrites the screen section only when its content changed.
- If Studio watches a screen, it walks that tree. It rewrites the tree section only when the rows, the status or the watch id changed.
- If Studio sent a new widget request id, it answers it once.
checkedTime moves on every capture, even when nothing else changes. Studio calls the mirror
Stale when checkedTime is older than 2 seconds. Refresh in the inspector sends a new
widget request id. Auto sends one 500 ms after each answer while the link is alive. Nothing is
pushed again for an answered id.
The tree walks every CUI object under the screen. A row flag marks the objects that are widgets. Only a widget row carries the state word, slots and text hash. Studio never reads a row without the flag as hidden.
Each section is written between two increments of its own sequence number. Studio copies a section only when the number is even, new, and the same after the copy. It tries three times. It copies the tree header first, then only the rows it counts.
A request answers with one status.
| status | meaning |
|---|---|
| complete | the rows or the record are valid |
| absent | no open screen has that tag and ordinal, or no widget has that node id |
| unavailable | the screen manager is not ready, or a read faulted |
Diagnostics
Studio drops all CUI data while the game’s version differs or is missing.
| condition | message |
|---|---|
| the game wrote another version | CUI bridge version differs. Update Sunrise and restart the game. |
| the game wrote no version after 5 seconds | CUI bridge unavailable. Update Sunrise and restart the game. |
| the game’s ready flag is clear | The game has not built its screen list yet. |
| a fast re-attach still holds the old mapping | CUI link busy; retrying |
The last two clear by themselves, so the CUI page shows them as Not ready, not as No link. A bad widget record marks only that record unavailable. It does not change the link status.
Detach sets the heartbeat to 0 and releases the mapping. A minimized Studio stops the heartbeat too. The game drops the link after 3 seconds and connects again within 1 second of the next heartbeat. It then republishes the screens, the watched tree and the last widget answer without a new request.
Command link
Update running game uses the command mapping. It does not need Attach, and it is the one link that changes game state. Studio creates the mapping for one request and releases it after the reply.
It sends one command, reload_mod. The game prepares replacements for the started, bound mission
programs owned by that mod or by mods that depend on it. The game answers once, after the mission
reaches a safe point. Studio waits up to 30 seconds.
- New source, loaded modules and
on_loadeffects stay private until the whole batch commits. - A preparation or commit failure returns an error and keeps the old programs running.
- An unbound or not yet started mission needs an activity restart.
- A disconnect or timeout cancels a pending request. A publication that has started cannot be undone.
The mapping is 320 bytes, magic 0x4D435353, ASCII SSCM. Studio creates it as new, so a second
editor cannot replace a running request. If the game still holds the last mapping, Studio retries for
3 seconds. The game answers with one status.
| status | Studio shows |
|---|---|
| ok | how many running missions reloaded |
| invalid | the game refused the request |
| disabled | mission scripting is off in the game |
| not loaded | the mod did not load in this game session |
| compile error | a script did not compile; the old program keeps running |
| failed | the game could not reload the mod |
Only scripts reload. The game side may not work with Studio.
Performance
A new placement snapshot copies only its header and the rows it counts. A snapshot refreshes only
objects whose pose changed. The game reads object memory in place under a fault guard. The CUI
link writes the cost of its last capture to captureMicros.
Viewport performance has the Studio
frame cap.