Skip to content

Commit a6e4fb5

Browse files
committed
Added setting and language button to Game Editor Select Screen.
Added additional check to only update preview on "real" code changes. Added additional check to defer preview updates on text inputs. Added endless loop protected for "Restart Game" and "Game Start" block. Added error messages for missing audio keys. Added code editor is disabled for un-save changes. Fixed bgm sound block.
1 parent efdaf3b commit a6e4fb5

File tree

11 files changed

+346
-248
lines changed

11 files changed

+346
-248
lines changed

package-lock.json

Lines changed: 233 additions & 212 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "coding-with-chrome",
3-
"version": "10.6.0",
3+
"version": "10.7.0",
44
"description": "Educational Coding Development Environment",
55
"repository": {
66
"type": "git",

src/components/BlockEditor/BlockEditor.js

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -485,7 +485,25 @@ export class BlockEditor extends React.PureComponent {
485485
*/
486486
// eslint-disable-next-line @typescript-eslint/no-unused-vars, no-unused-vars, require-jsdoc
487487
handleWorkspaceChange(workspace) {
488-
// Not used.
488+
// Not implemented yet.
489+
}
490+
491+
/**
492+
* @return {boolean}
493+
*/
494+
isSelectedBlockATextInput() {
495+
return (
496+
typeof Blockly !== 'undefined' &&
497+
Blockly.getSelected() &&
498+
Blockly.getSelected().inputList &&
499+
Blockly.getSelected().inputList[0] &&
500+
Blockly.getSelected().inputList[0].fieldRow &&
501+
Blockly.getSelected().inputList[0].fieldRow.some(
502+
(input) =>
503+
input instanceof Blockly.FieldTextInput ||
504+
input instanceof Blockly.FieldNumber,
505+
)
506+
);
489507
}
490508

491509
/**
@@ -540,9 +558,12 @@ export class BlockEditor extends React.PureComponent {
540558
isFirstXMLUpdate: false,
541559
});
542560
} else {
543-
this.timer.handleXMLChange = setTimeout(() => {
544-
this.updateCodeAfterXMLChange();
545-
}, 200);
561+
this.timer.handleXMLChange = setTimeout(
562+
() => {
563+
this.updateCodeAfterXMLChange();
564+
},
565+
this.isSelectedBlockATextInput() ? 5000 : 200,
566+
);
546567
}
547568
}
548569

src/components/BlockEditor/BlockEditorToolbar.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,7 @@ export class BlockEditorToolbar extends React.PureComponent {
332332
{this.props.blockEditor.codeEditor && (
333333
<ToolbarIconButton
334334
aria-label="code"
335+
disabled={this.props.hasChanged}
335336
onClick={() => {
336337
this.props.blockEditor.showCodeEditor();
337338
}}

src/components/GameEditor/GameEditor.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,10 @@ export class GameEditor extends React.PureComponent {
8989
handleXMLChange: null,
9090
};
9191

92+
this.changeCache = {
93+
code: '',
94+
};
95+
9296
// Set trusted origin for postMessage and other communication.
9397
this.trustedOrigin = `${window.location.origin}`;
9498

@@ -415,6 +419,12 @@ export class GameEditor extends React.PureComponent {
415419
this.timer.handleXMLChange = null;
416420
}
417421

422+
// Skip update if the code is the same as before.
423+
if (this.changeCache.code == code) {
424+
return;
425+
}
426+
this.changeCache.code = code;
427+
418428
// Allow updates for the preview directly on the first run / load and use
419429
// auto-fresh for further refresh / updates.
420430
if (this.state.isFirstRunPreview) {

src/components/GameEditor/GameEditorSelectScreen.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ import { Project } from '../Project/Project';
4545
import { ProjectType } from '../Project/ProjectType';
4646

4747
import LanguageSetting from '../Settings/LanguageSetting';
48+
import SettingScreen from '../Settings/SettingScreen';
49+
4850
const NewGameProject = lazy(() => import('./dialog/NewGameProject'));
4951
const OpenGameProject = lazy(() => import('./dialog/OpenGameProject'));
5052

@@ -100,6 +102,11 @@ export class GameEditorSelectScreen extends React.PureComponent {
100102
Project.hasProjects(ProjectType.GAME_EDITOR).then((hasProjects) => {
101103
this.setState({ hasProjects });
102104
});
105+
106+
// Update language
107+
i18next.on('languageChanged', () => {
108+
this.forceUpdate();
109+
});
103110
}
104111

105112
/**
@@ -153,7 +160,8 @@ export class GameEditorSelectScreen extends React.PureComponent {
153160
>
154161
{i18next.t('GAME_EDITOR')}
155162
</Typography>
156-
<LanguageSetting color="inherit" />
163+
<SettingScreen color="inherit" showIcon={true} />
164+
<LanguageSetting color="inherit" showIcon={true} />
157165
</Toolbar>
158166
</AppBar>
159167

src/components/GameEditor/blocks/AudioBlocks.js

Lines changed: 21 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -67,33 +67,24 @@ Blocks['phaser_audio_add_bgm'] = {
6767
* @return {string}
6868
*/
6969
javascriptGenerator.forBlock['phaser_audio_add_bgm'] = function (block) {
70-
const text_audio = block.getFieldValue('audio');
71-
const number_volume = block.getFieldValue('volume');
72-
const dropdown_loop = block.getFieldValue('loop');
70+
const textAudio = block.getFieldValue('audio');
71+
const numberVolume = block.getFieldValue('volume');
72+
const dropdownLoop = block.getFieldValue('loop');
7373
const variable = javascriptGenerator.valueToCode(
7474
block,
7575
'variable',
7676
javascriptGenerator.ORDER_ATOMIC,
7777
);
78-
return (
79-
'if (typeof ' +
80-
variable +
81-
" === 'undefined') {\n" +
82-
' ' +
83-
variable +
84-
" = game.add.audio('" +
85-
text_audio +
86-
"', " +
87-
number_volume / 100 +
88-
', ' +
89-
dropdown_loop +
90-
');\n' +
91-
'} else {\n ' +
92-
variable +
93-
'.stop();\n}\n' +
94-
variable +
95-
'.play();\n'
96-
);
78+
return `
79+
try {
80+
${variable} = this.sound.add('${textAudio}', {
81+
volume: ${numberVolume / 100}, loop: ${dropdownLoop}
82+
});
83+
${variable}.play();
84+
} catch (e) {
85+
window.alert(e);
86+
}
87+
`;
9788
};
9889

9990
/**
@@ -144,9 +135,14 @@ javascriptGenerator.forBlock['phaser_audio_add'] = function (block) {
144135
javascriptGenerator.ORDER_ATOMIC,
145136
);
146137
return `
147-
${variable} = this.sound.add('${textAudio}', {
148-
volume: ${numberVolume / 100}, loop: ${dropdownLoop}
149-
});`;
138+
try {
139+
${variable} = this.sound.add('${textAudio}', {
140+
volume: ${numberVolume / 100}, loop: ${dropdownLoop}
141+
});
142+
} catch (e) {
143+
window.alert(e);
144+
}
145+
`;
150146
};
151147

152148
/**

src/components/GameEditor/blocks/CreateBlocks.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ javascriptGenerator.forBlock['phaser_create'] = function (block) {
6262
// Default definition for easier access.
6363
this.default_group = this.add.group(undefined, 'default_group');
6464
65+
// Reset reload protection.
66+
this.helper_.resetReloadProtection('phaser_game_start');
67+
this.helper_.resetReloadProtection('phaser_game_restart');
68+
6569
${javascriptGenerator.statementToCode(block, 'CODE')}
6670
6771
// Separate event block for better code structure.

src/components/GameEditor/blocks/GameBlocks.js

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,11 @@ javascriptGenerator.forBlock['phaser_game_start'] = function (block) {
161161
'variable',
162162
javascriptGenerator.ORDER_ATOMIC,
163163
);
164-
return `this.scene.start('${variable}');\n`;
164+
return `
165+
if (this.helper_.checkReloadProtection('phaser_game_start')) {
166+
this.scene.start('${variable}');
167+
}
168+
`;
165169
};
166170

167171
/**
@@ -186,8 +190,10 @@ Blocks['phaser_game_restart'] = {
186190
*/
187191
javascriptGenerator.forBlock['phaser_game_restart'] = function () {
188192
return `
189-
this.events.off();
190-
this.registry.destroy();
191-
this.scene.restart();
193+
if (this.helper_.checkReloadProtection('phaser_game_restart')) {
194+
this.events.off();
195+
this.registry.destroy();
196+
this.scene.restart();
197+
}
192198
`;
193199
};

src/frameworks/phaser/phaser_helper.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ class PhaserHelper {
3333
this.sprite = new Set();
3434
this.tileSprite = new Set();
3535
this.arcadeSprite = new Set();
36+
window.phaserReloadProtection = window.phaserReloadProtection || {};
3637
}
3738

3839
/**
@@ -56,6 +57,36 @@ class PhaserHelper {
5657
this.arcadeSprite.add(arcadeSprite);
5758
}
5859

60+
/**
61+
* @param {string} id
62+
* @return {boolean}
63+
*/
64+
checkReloadProtection(id) {
65+
if (window.phaserReloadProtection[id] < 5) {
66+
window.phaserReloadProtection[id] = window.phaserReloadProtection[id] + 1;
67+
return true;
68+
} else {
69+
console.error('⚠️ Reload protection triggered for', id);
70+
console.warn('💡 Please check your code for endless loops!');
71+
}
72+
return false;
73+
}
74+
75+
/**
76+
* @param {string} id
77+
*/
78+
resetReloadProtection(id) {
79+
// Reset reload protection.
80+
if (
81+
window.phaserReloadProtection[id] &&
82+
window.phaserReloadProtection[id] > 0
83+
) {
84+
window.setTimeout(() => {
85+
window.phaserReloadProtection[id] = 0;
86+
}, 100);
87+
}
88+
}
89+
5990
/**
6091
* Handles input specific functions, if needed.
6192
*/

0 commit comments

Comments
 (0)