/** * While playing (#265): how closely the panels follow a running game. The preset is picked in the * Project settings with the mouse; Live brings the panels up to date every frame, Balanced four * times a second, and neither holds the game back. On Balanced the scene view stops drawing behind * the game tab and catches up when it comes back; on Minimal the hierarchy waits for a pause. A * crate spawned at the top level is listed in a group named "Crate". */ export async function whilePlaying(t) { const { check, json, evaluate, click, clickText, clickToolbar, press, play, stop, shot, showTab, undo, sleep, } = t; const playInfo = () => json('__gm.playInfo()'); /** Panel updates so far and the page's clock, so a rate is measured in the page, not over CDP. */ const sample = () => json('({ at: performance.now(), ...__gm.playInfo().panelUpdates })'); const valuesPerSecond = async (ms) => { const a = await sample(); await sleep(ms); const b = await sample(); return { rate: ((b.values - a.values) * 1000) / (b.at - a.at), a, b }; }; const setWhilePlaying = async (label) => { await clickToolbar('menu'); await clickText('Project settings…'); const before = (await json('__gm.projectSettings()')).whilePlaying; const at = await json('__gm.projectSettingsWhilePlayingAt()'); await click(at.x, at.y); await clickText(label); const chosen = (await json('__gm.projectSettings()')).whilePlaying; await clickText('Apply'); return { before, chosen }; }; const undoSteps = () => json('__gm.state.commands.undoStack.length'); /** Whether the hierarchy *shows* the row – `hierarchyOrder` reads the model, not the list on screen. */ const listed = (rowId) => json(`__gm.hierarchyRow(${JSON.stringify(rowId)}) !== null`); const crateGroups = () => json(`(__gm.state.activeScene.groups ?? []).filter((g) => g.name === 'Crate')`); await press('Escape'); await evaluate('__gm.state.select(null)'); await showTab('scene'); // 59a. The fixture was written without the setting, so it reads as Balanced; Live is picked with // the mouse and is one undo step. check( 'a project written before there was a choice plays Balanced', (await json('__gm.state.project.whilePlaying')) === 'balanced', ); const steps = await undoSteps(); const picked = await setWhilePlaying('Live'); check( 'the Project settings show Balanced and take Live from the menu', picked.before === 'balanced' && picked.chosen === 'live' && (await json('__gm.state.project.whilePlaying')) === 'live' && (await undoSteps()) === steps + 1, JSON.stringify({ picked, steps: await undoSteps(), before: steps }), ); // 59b. Live follows every frame; Balanced four times a second. await play(); const live = await valuesPerSecond(1500); await stop(); await undo(); check('undo sets it back to Balanced', (await json('__gm.state.project.whilePlaying')) === 'balanced'); await play(); const balanced = await valuesPerSecond(1500); check( 'Live brings the panels up to date every frame, Balanced at most four times a second', live.rate > 20 && balanced.rate > 2 && balanced.rate <= 4.5, JSON.stringify({ live: live.rate, balanced: balanced.rate }), ); // 59c. Behind the game tab the scene view does not draw on Balanced – three crates spawned, not // one redraw – and catches up when its tab comes back. The crates are in a group "Crate". const hidden = (await json('__gm.viewportInfo()')).redraws; const entitiesBefore = (await json('__gm.world()')).entities.length; for (let i = 0; i < 3; i++) await press('Space'); await sleep(600); const stillHidden = (await json('__gm.viewportInfo()')).redraws; const groups = await crateGroups(); const members = await json( `__gm.state.activeScene.objects.filter((o) => o.groupId === ${JSON.stringify(groups[0]?.id ?? '')}).length`, ); check( 'the game ran on – three crates spawned – and they are listed in one group named Crate', (await json('__gm.world()')).entities.length === entitiesBefore + 3 && groups.length === 1 && members === 3, JSON.stringify({ entitiesBefore, groups, members }), ); check('the hierarchy shows the group after the next update', await listed(`group:${groups[0]?.id}`)); check( 'behind the game tab the scene view did not draw once', stillHidden === hidden, JSON.stringify({ hidden, stillHidden }), ); await showTab('scene'); const shown = (await json('__gm.viewportInfo()')).redraws; check( 'and it draws again when its tab comes back', shown > stillHidden, JSON.stringify({ stillHidden, shown }), ); await shot('59a-balanced-group'); await stop(); // 59d. Minimal: the hierarchy waits while the game runs, and a pause brings it up to date at once. await showTab('scene'); await setWhilePlaying('Minimal'); await play(); await press('Space'); await sleep(600); const spawnedGroups = await crateGroups(); const groupRow = `group:${spawnedGroups[0]?.id}`; const listedRunning = await listed(groupRow); check( 'on Minimal a spawn reaches the scene, but the hierarchy waits', spawnedGroups.length === 1 && !listedRunning, JSON.stringify({ spawnedGroups, listedRunning }), ); await clickToolbar('pause'); check('and a pause lists it at once', (await playInfo()).paused && (await listed(groupRow))); await shot('59b-minimal-paused'); await stop(); await undo(); check( 'the fixture is left on Balanced with the undo stack as found', (await json('__gm.state.project.whilePlaying')) === 'balanced' && (await undoSteps()) === steps, JSON.stringify({ steps: await undoSteps(), before: steps }), ); }