SUNRISE STUDIO

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.

mappingcarriesversion
Local\SunriseStudio.Live.<pid>the camera frame1
Local\SunriseStudio.World.<pid>world and object rows4
Local\SunriseStudio.Pose.<pid>animation5
Local\SunriseStudio.Object.<pid>the selected object1
Local\SunriseStudio.Cui.<pid>open CUI screens, one widget tree, one widget record1
Local\SunriseStudio.Command.<pid>commands1

Studio reports a missing or mismatched bridge with an update and restart note and ignores its data.

What the game sends

surfacedatawhen
cameraposition, forward and up, horizontal field of view, aspectevery camera callback
playerphysics position and the controlled handleevery camera callback
worldloaded map root and definition, active scenario, activity package nameevery 500 ms, or at once on entering or leaving the world
bubblesresident and current slice identitiesevery 500 ms
object rowsidentity, handle and generation, class, position, rotation, scale, raw flags, the placed-not-hidden bita scan at most every 50 ms
animationworld-space skinning palettes of the owners Studio asks forat most every 50 ms, while on
selected objectparent, root, attached objects, drawn models, raw datum bytesonce per request
CUI screensthe open screens of both layers, in list order, and each layer’s top screenat most every 100 ms, only when they change
CUI treethe widgets of the screen Studio watchesat most every 100 ms, only when they change
CUI widgetone widget’s recordonce 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.

conditiondatum flags
builtflags 1 and 2 of the object are clear
placed in the worldthe root has 0x20: it is linked into spatial cells
not hiddenthe object lacks 0x800, and no datum up the chain has 0x400, 0x80 or 4

Rows are filtered before the test.

caseeffect
handle, generation or placement have changedleft out and counted as excluded
flag 4 (destroyed) on the objectleft out and counted as excluded
the parent chain cannot be readthe 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.

fieldsource
identity, class, flags, placed-not-hidden bitthe object’s datum at answer time
position, rotation, scalethe object’s datum at answer time
parent handlethe object’s datum
root handle, root flags, chain flagsthe parent chain at answer time
attached objectsrows of the last scan whose parent is this object, at most 64
drawn modelsdraws of this owner in the last animation capture, at most 32
raw datumthe 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.

fieldvalue
mappingLocal\SunriseStudio.Cui.<pid>
magic0x55435353, ASCII SSCU
version1
size460,736 bytes
created byStudio, on first use
opened bythe 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

requesttargetanswer
watchhierarchy tag, ordinalthe widget tree of that screen, while the watch holds
widgethierarchy tag, ordinal, node idone 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.

  1. It checks the header and the heartbeat. A heartbeat older than 3 seconds drops the link.
  2. It writes checkedTime.
  3. It walks the open screens. It rewrites the screen section only when its content changed.
  4. 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.
  5. 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.

statusmeaning
completethe rows or the record are valid
absentno open screen has that tag and ordinal, or no widget has that node id
unavailablethe screen manager is not ready, or a read faulted

Diagnostics

Studio drops all CUI data while the game’s version differs or is missing.

conditionmessage
the game wrote another versionCUI bridge version differs. Update Sunrise and restart the game.
the game wrote no version after 5 secondsCUI bridge unavailable. Update Sunrise and restart the game.
the game’s ready flag is clearThe game has not built its screen list yet.
a fast re-attach still holds the old mappingCUI 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.

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_load effects 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.

statusStudio shows
okhow many running missions reloaded
invalidthe game refused the request
disabledmission scripting is off in the game
not loadedthe mod did not load in this game session
compile errora script did not compile; the old program keeps running
failedthe 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.