Skip to content

Commit 419e74f

Browse files
committed
Merge branch 'feature-pr230' into develop
2 parents 762ade3 + 84967f4 commit 419e74f

19 files changed

+680
-97
lines changed

.github/workflows/deploy.yml

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
name: Deploy playground
2+
3+
on:
4+
workflow_dispatch:
5+
push:
6+
branches: [develop]
7+
8+
concurrency:
9+
group: "deploy"
10+
cancel-in-progress: true
11+
12+
jobs:
13+
build:
14+
runs-on: ubuntu-latest
15+
steps:
16+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
17+
with:
18+
persist-credentials: false
19+
- name: Install Node.js
20+
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af
21+
with:
22+
node-version: 20
23+
cache: npm
24+
- name: Install dependencies
25+
run: npm ci
26+
- name: Build playground
27+
run: npm run build
28+
- name: Build docs
29+
run: npm run docs
30+
# We expect to see syntax errors from the old jsdoc cli not understanding some of our syntax
31+
# It will still generate what it can, so it's safe to ignore the error
32+
continue-on-error: true
33+
- name: Upload artifact
34+
uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa
35+
with:
36+
path: ./playground/
37+
38+
deploy:
39+
environment:
40+
name: github-pages
41+
url: ${{ steps.deployment.outputs.page_url }}
42+
permissions:
43+
pages: write
44+
id-token: write
45+
runs-on: ubuntu-latest
46+
needs: build
47+
steps:
48+
- name: Deploy to GitHub Pages
49+
id: deployment
50+
uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e

.github/workflows/node.js.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@ jobs:
88
build:
99
runs-on: ubuntu-latest
1010
steps:
11-
- uses: actions/checkout@v4
11+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
12+
with:
13+
persist-credentials: false
1214
- name: Install Node.js
13-
uses: actions/setup-node@v4
15+
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af
1416
with:
1517
node-version: 20
1618
cache: npm

src/blocks/scratch3_sensing.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,7 @@ class Scratch3SensingBlocks {
244244

245245
current (args) {
246246
const menuOption = Cast.toString(args.CURRENTMENU).toLowerCase();
247+
if (menuOption === 'refreshtime') return (this.runtime.screenRefreshTime / 1000);
247248
const date = new Date();
248249
switch (menuOption) {
249250
case 'year': return date.getFullYear();

src/compiler/irgen.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -603,6 +603,10 @@ class ScriptTreeGenerator {
603603
return {
604604
kind: 'sensing.second'
605605
};
606+
case 'refreshtime':
607+
return {
608+
kind: 'sensing.refrehTime'
609+
};
606610
}
607611
return {
608612
kind: 'constant',

src/compiler/jsgen.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -761,6 +761,8 @@ class JSGenerator {
761761
}
762762
case 'sensing.second':
763763
return new TypedInput(`(new Date().getSeconds())`, TYPE_NUMBER);
764+
case 'sensing.refreshTime':
765+
return new TypedInput('(runtime.screenRefreshTime / 1000)', TYPE_NUMBER);
764766
case 'sensing.touching':
765767
return new TypedInput(`target.isTouchingObject(${this.descendInput(node.object).asUnknown()})`, TYPE_BOOLEAN);
766768
case 'sensing.touchingColor':

src/engine/runtime.js

Lines changed: 10 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -193,12 +193,6 @@ let stepProfilerId = -1;
193193
*/
194194
let stepThreadsProfilerId = -1;
195195

196-
/**
197-
* Numeric ID for RenderWebGL.draw in Profiler instances.
198-
* @type {number}
199-
*/
200-
let rendererDrawProfilerId = -1;
201-
202196
/**
203197
* Manages targets, scripts, and the sequencer.
204198
* @constructor
@@ -445,6 +439,14 @@ class Runtime extends EventEmitter {
445439
*/
446440
this.platform = Object.assign({}, platform);
447441

442+
/**
443+
* Screen refresh time speculated from screen refresh rate, in milliseconds.
444+
* Indicates time passed between two screen refreshments.
445+
* Based on site isolation status, the resolution could be ~0.1ms or lower.
446+
* @type {!number}
447+
*/
448+
this.screenRefreshTime = 0;
449+
448450
this._initScratchLink();
449451

450452
this.resetRunId();
@@ -471,7 +473,6 @@ class Runtime extends EventEmitter {
471473

472474
this.debug = false;
473475

474-
this._lastStepTime = Date.now();
475476
this.interpolationEnabled = false;
476477

477478
this._defaultStoredSettings = this._generateAllProjectOptions();
@@ -2495,8 +2496,8 @@ class Runtime extends EventEmitter {
24952496
}
24962497

24972498
_renderInterpolatedPositions () {
2498-
const frameStarted = this._lastStepTime;
2499-
const now = Date.now();
2499+
const frameStarted = this.frameLoop._lastStepTime;
2500+
const now = this.frameLoop.now();
25002501
const timeSinceStart = now - frameStarted;
25012502
const progressInFrame = Math.min(1, Math.max(0, timeSinceStart / this.currentStepTime));
25022503

@@ -2567,24 +2568,6 @@ class Runtime extends EventEmitter {
25672568
// Store threads that completed this iteration for testing and other
25682569
// internal purposes.
25692570
this._lastStepDoneThreads = doneThreads;
2570-
if (this.renderer) {
2571-
// @todo: Only render when this.redrawRequested or clones rendered.
2572-
if (this.profiler !== null) {
2573-
if (rendererDrawProfilerId === -1) {
2574-
rendererDrawProfilerId = this.profiler.idByName('RenderWebGL.draw');
2575-
}
2576-
this.profiler.start(rendererDrawProfilerId);
2577-
}
2578-
// tw: do not draw if document is hidden or a rAF loop is running
2579-
// Checking for the animation frame loop is more reliable than using
2580-
// interpolationEnabled in some edge cases
2581-
if (!document.hidden && !this.frameLoop._interpolationAnimation) {
2582-
this.renderer.draw();
2583-
}
2584-
if (this.profiler !== null) {
2585-
this.profiler.stop();
2586-
}
2587-
}
25882571

25892572
if (this._refreshTargets) {
25902573
this.emit(Runtime.TARGETS_UPDATE, false /* Don't emit project changed */);
@@ -2600,10 +2583,6 @@ class Runtime extends EventEmitter {
26002583
this.profiler.stop();
26012584
this.profiler.reportFrames();
26022585
}
2603-
2604-
if (this.interpolationEnabled) {
2605-
this._lastStepTime = Date.now();
2606-
}
26072586
}
26082587

26092588
/**
@@ -2662,9 +2641,6 @@ class Runtime extends EventEmitter {
26622641
* @param {number} framerate Target frames per second
26632642
*/
26642643
setFramerate (framerate) {
2665-
// Setting framerate to anything greater than this is unnecessary and can break the sequencer
2666-
// Additionally, the JS spec says intervals can't run more than once every 4ms (250/s) anyways
2667-
if (framerate > 250) framerate = 250;
26682644
// Convert negative framerates to 1FPS
26692645
// Note that 0 is a special value which means "matching device screen refresh rate"
26702646
if (framerate < 0) framerate = 1;

src/engine/sequencer.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,13 +82,15 @@ class Sequencer {
8282
// Whether `stepThreads` has run through a full single tick.
8383
let ranFirstTick = false;
8484
const doneThreads = [];
85+
86+
// tw: If this happens, the runtime is in initialization, do not execute any thread.
87+
if (this.runtime.currentStepTime === 0) return [];
8588
// Conditions for continuing to stepping threads:
8689
// 1. We must have threads in the list, and some must be active.
8790
// 2. Time elapsed must be less than WORK_TIME.
8891
// 3. Either turbo mode, or no redraw has been requested by a primitive.
8992
while (this.runtime.threads.length > 0 &&
9093
numActiveThreads > 0 &&
91-
this.timer.timeElapsed() < WORK_TIME &&
9294
(this.runtime.turboMode || !this.runtime.redrawRequested)) {
9395
if (this.runtime.profiler !== null) {
9496
if (stepThreadsInnerProfilerId === -1) {
@@ -164,6 +166,10 @@ class Sequencer {
164166
}
165167
this.runtime.threads.length = nextActiveThread;
166168
}
169+
170+
// tw: Detect timer here so the sequencer won't break when FPS is greater than 1000
171+
// and performance.now() is not available.
172+
if (this.timer.timeElapsed() >= WORK_TIME) break;
167173
}
168174

169175
this.activeThread = null;

src/engine/thread.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,8 @@ class Thread {
451451
let callCount = 5; // Max number of enclosing procedure calls to examine.
452452
const sp = this.stackFrames.length - 1;
453453
for (let i = sp - 1; i >= 0; i--) {
454-
const block = this.target.blocks.getBlock(this.stackFrames[i].op.id);
454+
const block = this.target.blocks.getBlock(this.stackFrames[i].op.id) ||
455+
this.target.runtime.flyoutBlocks.getBlock(this.stackFrames[i].op.id);
455456
if (block.opcode === 'procedures_call' &&
456457
block.mutation.proccode === procedureCode) {
457458
return true;

0 commit comments

Comments
 (0)