mirror of
https://github.com/4ian/GDevelop.git
synced 2025-10-15 10:19:04 +00:00
WIP: robustify scene change and initial scene for in game edition
This commit is contained in:
@@ -169,6 +169,14 @@ struct PreviewExportOptions {
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set if the export is made for being edited in the editor.
|
||||
*/
|
||||
PreviewExportOptions &SetIsInGameEdition(bool enable) {
|
||||
isInGameEdition = enable;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief If set to a non zero value, the exported script URLs will have an
|
||||
* extra search parameter added (with the given value) to ensure browser cache
|
||||
@@ -291,6 +299,7 @@ struct PreviewExportOptions {
|
||||
bool projectDataOnlyExport;
|
||||
bool fullLoadingScreen;
|
||||
bool isDevelopmentEnvironment;
|
||||
bool isInGameEdition;
|
||||
unsigned int nonRuntimeScriptsCacheBurst;
|
||||
gd::String electronRemoteRequirePath;
|
||||
gd::String gdevelopResourceToken;
|
||||
|
@@ -263,15 +263,24 @@ namespace gdjs {
|
||||
} else if (data.command === 'hotReload') {
|
||||
that._hotReloader.hotReload().then((logs) => {
|
||||
that.sendHotReloaderLogs(logs);
|
||||
// TODO: if fatal error, should probably reload. The editor should handle this
|
||||
// as it knows the current scene to show.
|
||||
});
|
||||
} else if (data.command === 'requestSceneChange') {
|
||||
} else if (data.command === 'requestSceneReplace') {
|
||||
const sceneName = data.sceneName || null;
|
||||
if (!sceneName) {
|
||||
logger.warn('No scene name specified, requestSceneChange aborted');
|
||||
logger.warn('No scene name specified, requestSceneReplace aborted');
|
||||
return;
|
||||
}
|
||||
|
||||
const currentScene = runtimeGame.getSceneStack().getCurrentScene();
|
||||
if (currentScene && currentScene.getName() === sceneName) {
|
||||
return;
|
||||
}
|
||||
|
||||
runtimeGame.getSceneStack().replace(sceneName, true);
|
||||
// TODO: if fatal error, should probably reload. The editor should handle this
|
||||
// as it knows the current scene to show.
|
||||
} else if (data.command === 'updateInstances') {
|
||||
// TODO: do an update/partial hot reload of the instances
|
||||
} else {
|
||||
|
@@ -45,8 +45,12 @@ namespace gdjs {
|
||||
export type RuntimeGameOptions = {
|
||||
/** if true, force fullscreen. */
|
||||
forceFullscreen?: boolean;
|
||||
|
||||
/** if true, game is run as a preview launched from an editor. */
|
||||
isPreview?: boolean;
|
||||
/** if true, game is run for being edited from the editor. */
|
||||
isInGameEdition?: boolean;
|
||||
|
||||
/** The name of the external layout to create in the scene at position 0;0. */
|
||||
injectExternalLayout?: string;
|
||||
/** Script files, used for hot-reloading. */
|
||||
|
@@ -3843,6 +3843,7 @@ interface PreviewExportOptions {
|
||||
[Ref] PreviewExportOptions SetNativeMobileApp(boolean enable);
|
||||
[Ref] PreviewExportOptions SetFullLoadingScreen(boolean enable);
|
||||
[Ref] PreviewExportOptions SetIsDevelopmentEnvironment(boolean enable);
|
||||
[Ref] PreviewExportOptions SetIsInGameEdition(boolean enable);
|
||||
[Ref] PreviewExportOptions SetNonRuntimeScriptsCacheBurst(unsigned long value);
|
||||
[Ref] PreviewExportOptions SetElectronRemoteRequirePath([Const] DOMString electronRemoteRequirePath);
|
||||
[Ref] PreviewExportOptions SetGDevelopResourceToken([Const] DOMString gdevelopResourceToken);
|
||||
|
1
GDevelop.js/types.d.ts
vendored
1
GDevelop.js/types.d.ts
vendored
@@ -2850,6 +2850,7 @@ export class PreviewExportOptions extends EmscriptenObject {
|
||||
setNativeMobileApp(enable: boolean): PreviewExportOptions;
|
||||
setFullLoadingScreen(enable: boolean): PreviewExportOptions;
|
||||
setIsDevelopmentEnvironment(enable: boolean): PreviewExportOptions;
|
||||
setIsInGameEdition(enable: boolean): PreviewExportOptions;
|
||||
setNonRuntimeScriptsCacheBurst(value: number): PreviewExportOptions;
|
||||
setElectronRemoteRequirePath(electronRemoteRequirePath: string): PreviewExportOptions;
|
||||
setGDevelopResourceToken(gdevelopResourceToken: string): PreviewExportOptions;
|
||||
|
@@ -13,6 +13,7 @@ declare class gdPreviewExportOptions {
|
||||
setNativeMobileApp(enable: boolean): gdPreviewExportOptions;
|
||||
setFullLoadingScreen(enable: boolean): gdPreviewExportOptions;
|
||||
setIsDevelopmentEnvironment(enable: boolean): gdPreviewExportOptions;
|
||||
setIsInGameEdition(enable: boolean): gdPreviewExportOptions;
|
||||
setNonRuntimeScriptsCacheBurst(value: number): gdPreviewExportOptions;
|
||||
setElectronRemoteRequirePath(electronRemoteRequirePath: string): gdPreviewExportOptions;
|
||||
setGDevelopResourceToken(gdevelopResourceToken: string): gdPreviewExportOptions;
|
||||
|
@@ -2,7 +2,7 @@
|
||||
import * as React from 'react';
|
||||
import { type PreviewDebuggerServer } from '../ExportAndShare/PreviewLauncher.flow';
|
||||
|
||||
type SwitchToPreviewOptions = {|
|
||||
type AttachToPreviewOptions = {|
|
||||
previewIndexHtmlLocation: string,
|
||||
|};
|
||||
|
||||
@@ -10,14 +10,14 @@ type SwitchToSceneEditionOptions = {|
|
||||
sceneName: string,
|
||||
|};
|
||||
|
||||
let onSwitchToPreview: null | (SwitchToPreviewOptions => void) = null;
|
||||
let onAttachToPreview: null | (AttachToPreviewOptions => void) = null;
|
||||
let onSwitchToSceneEdition: null | (SwitchToSceneEditionOptions => void) = null;
|
||||
|
||||
export const switchToPreview = ({
|
||||
export const attachToPreview = ({
|
||||
previewIndexHtmlLocation,
|
||||
}: SwitchToPreviewOptions) => {
|
||||
if (!onSwitchToPreview) throw new Error('No EmbeddedGameFrame registered.');
|
||||
onSwitchToPreview({ previewIndexHtmlLocation });
|
||||
}: AttachToPreviewOptions) => {
|
||||
if (!onAttachToPreview) throw new Error('No EmbeddedGameFrame registered.');
|
||||
onAttachToPreview({ previewIndexHtmlLocation });
|
||||
};
|
||||
|
||||
export const switchToSceneEdition = ({
|
||||
@@ -30,36 +30,53 @@ export const switchToSceneEdition = ({
|
||||
|
||||
type Props = {|
|
||||
previewDebuggerServer: PreviewDebuggerServer | null,
|
||||
onLaunchPreviewForInGameEdition: ({| sceneName: string |}) => void,
|
||||
|};
|
||||
|
||||
export const EmbeddedGameFrame = ({ previewDebuggerServer }: Props) => {
|
||||
export const EmbeddedGameFrame = ({
|
||||
previewDebuggerServer,
|
||||
onLaunchPreviewForInGameEdition,
|
||||
}: Props) => {
|
||||
const [
|
||||
previewIndexHtmlLocation,
|
||||
setPreviewIndexHtmlLocation,
|
||||
] = React.useState<string>('');
|
||||
const iframeRef = React.useRef<HTMLIFrameElement | null>(null);
|
||||
// TODO: display a loader when the preview is being loaded.
|
||||
|
||||
React.useEffect(() => {
|
||||
// TODO: use a real context for this?
|
||||
onSwitchToPreview = (options: SwitchToPreviewOptions) => {
|
||||
setPreviewIndexHtmlLocation(options.previewIndexHtmlLocation);
|
||||
if (iframeRef.current) {
|
||||
iframeRef.current.contentWindow.focus();
|
||||
}
|
||||
};
|
||||
onSwitchToSceneEdition = (options: SwitchToSceneEditionOptions) => {
|
||||
if (!previewDebuggerServer) return;
|
||||
React.useEffect(
|
||||
() => {
|
||||
// TODO: use a real context for this?
|
||||
onAttachToPreview = (options: AttachToPreviewOptions) => {
|
||||
setPreviewIndexHtmlLocation(options.previewIndexHtmlLocation);
|
||||
if (iframeRef.current) {
|
||||
iframeRef.current.contentWindow.focus();
|
||||
}
|
||||
};
|
||||
onSwitchToSceneEdition = (options: SwitchToSceneEditionOptions) => {
|
||||
if (!previewDebuggerServer) return;
|
||||
|
||||
console.log('TODO: switch to scene edition', options);
|
||||
previewDebuggerServer.getExistingDebuggerIds().forEach(debuggerId => {
|
||||
previewDebuggerServer.sendMessage(debuggerId, {
|
||||
command: 'requestSceneChange',
|
||||
sceneName: options.sceneName,
|
||||
});
|
||||
});
|
||||
};
|
||||
}, [previewDebuggerServer]);
|
||||
const { sceneName } = options;
|
||||
|
||||
if (!previewIndexHtmlLocation) {
|
||||
console.info('Launching preview for embedded game.');
|
||||
onLaunchPreviewForInGameEdition({ sceneName });
|
||||
} else {
|
||||
console.info(`Switching previews to scene "${sceneName}".`);
|
||||
previewDebuggerServer.getExistingDebuggerIds().forEach(debuggerId => {
|
||||
previewDebuggerServer.sendMessage(debuggerId, {
|
||||
command: 'requestSceneReplace',
|
||||
sceneName,
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
},
|
||||
[
|
||||
previewDebuggerServer,
|
||||
previewIndexHtmlLocation,
|
||||
onLaunchPreviewForInGameEdition,
|
||||
]
|
||||
);
|
||||
|
||||
return (
|
||||
<div style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0 }}>
|
||||
|
@@ -20,7 +20,7 @@ import { displayBlackLoadingScreenOrThrow } from '../../../Utils/BrowserExternal
|
||||
import { getGDevelopResourceJwtToken } from '../../../Utils/GDevelopServices/Project';
|
||||
import { isNativeMobileApp } from '../../../Utils/Platform';
|
||||
import { getIDEVersionWithHash } from '../../../Version';
|
||||
import { switchToPreview } from '../../../EmbeddedGame/EmbeddedGameFrame';
|
||||
import { attachToPreview } from '../../../EmbeddedGame/EmbeddedGameFrame';
|
||||
const gd: libGDevelop = global.gd;
|
||||
|
||||
type State = {|
|
||||
@@ -159,6 +159,7 @@ export default class BrowserS3PreviewLauncher extends React.Component<
|
||||
);
|
||||
previewExportOptions.setLayoutName(layout.getName());
|
||||
previewExportOptions.setIsDevelopmentEnvironment(Window.isDev());
|
||||
previewExportOptions.setIsInGameEdition(previewOptions.isForInGameEdition);
|
||||
if (externalLayout) {
|
||||
previewExportOptions.setExternalLayoutName(externalLayout.getName());
|
||||
}
|
||||
@@ -221,9 +222,11 @@ export default class BrowserS3PreviewLauncher extends React.Component<
|
||||
// Upload any file that must be exported for the preview.
|
||||
await browserS3FileSystem.uploadPendingObjects();
|
||||
|
||||
switchToPreview({
|
||||
previewIndexHtmlLocation: outputDir + '/index.html',
|
||||
});
|
||||
if (previewOptions.isForInGameEdition) {
|
||||
attachToPreview({
|
||||
previewIndexHtmlLocation: outputDir + '/index.html',
|
||||
});
|
||||
}
|
||||
|
||||
// Change the HTML file displayed by the preview window so that it starts loading
|
||||
// the game.
|
||||
|
@@ -22,7 +22,7 @@ import {
|
||||
} from './LocalPreviewDebuggerServer';
|
||||
import Window from '../../../Utils/Window';
|
||||
import { getIDEVersionWithHash } from '../../../Version';
|
||||
import { switchToPreview } from '../../../EmbeddedGame/EmbeddedGameFrame';
|
||||
import { attachToPreview } from '../../../EmbeddedGame/EmbeddedGameFrame';
|
||||
const electron = optionalRequire('electron');
|
||||
const path = optionalRequire('path');
|
||||
const ipcRenderer = electron ? electron.ipcRenderer : null;
|
||||
@@ -48,6 +48,28 @@ type State = {|
|
||||
captureOptions: ?CaptureOptions,
|
||||
|};
|
||||
|
||||
const prepareExporter = async (): Promise<{|
|
||||
outputDir: string,
|
||||
exporter: gdjsExporter,
|
||||
gdjsRoot: string,
|
||||
|}> => {
|
||||
const { gdjsRoot } = await findGDJS();
|
||||
console.info('GDJS found in ', gdjsRoot);
|
||||
|
||||
const localFileSystem = new LocalFileSystem({
|
||||
downloadUrlsToLocalFiles: false,
|
||||
});
|
||||
const fileSystem = assignIn(new gd.AbstractFileSystemJS(), localFileSystem);
|
||||
const outputDir = path.join(fileSystem.getTempDir(), 'preview');
|
||||
const exporter = new gd.Exporter(fileSystem, gdjsRoot);
|
||||
|
||||
return {
|
||||
outputDir,
|
||||
exporter,
|
||||
gdjsRoot,
|
||||
};
|
||||
};
|
||||
|
||||
export default class LocalPreviewLauncher extends React.Component<
|
||||
PreviewLauncherProps,
|
||||
State
|
||||
@@ -172,171 +194,138 @@ export default class LocalPreviewLauncher extends React.Component<
|
||||
);
|
||||
};
|
||||
|
||||
_prepareExporter = (): Promise<{|
|
||||
outputDir: string,
|
||||
exporter: gdjsExporter,
|
||||
gdjsRoot: string,
|
||||
|}> => {
|
||||
return findGDJS().then(({ gdjsRoot }) => {
|
||||
console.info('GDJS found in ', gdjsRoot);
|
||||
|
||||
const localFileSystem = new LocalFileSystem({
|
||||
downloadUrlsToLocalFiles: false,
|
||||
});
|
||||
const fileSystem = assignIn(
|
||||
new gd.AbstractFileSystemJS(),
|
||||
localFileSystem
|
||||
);
|
||||
const outputDir = path.join(fileSystem.getTempDir(), 'preview');
|
||||
const exporter = new gd.Exporter(fileSystem, gdjsRoot);
|
||||
|
||||
return {
|
||||
outputDir,
|
||||
exporter,
|
||||
gdjsRoot,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
launchPreview = (previewOptions: PreviewOptions): Promise<any> => {
|
||||
launchPreview = async (previewOptions: PreviewOptions): Promise<any> => {
|
||||
const { project, layout, externalLayout } = previewOptions;
|
||||
|
||||
// Start the debugger server for previews. Even if not used,
|
||||
// useful if the user opens the Debugger editor later, or want to
|
||||
// hot reload.
|
||||
return this.getPreviewDebuggerServer()
|
||||
.startServer()
|
||||
.catch(err => {
|
||||
// Ignore any error when running the debugger server - the preview
|
||||
// can still work without it.
|
||||
console.error(
|
||||
'Unable to start the Debugger Server for the preview:',
|
||||
err
|
||||
);
|
||||
})
|
||||
.then(() => this._prepareExporter())
|
||||
.then(({ outputDir, exporter, gdjsRoot }) => {
|
||||
timeFunction(
|
||||
() => {
|
||||
const previewExportOptions = new gd.PreviewExportOptions(
|
||||
project,
|
||||
outputDir
|
||||
);
|
||||
previewExportOptions.setIsDevelopmentEnvironment(Window.isDev());
|
||||
previewExportOptions.setLayoutName(layout.getName());
|
||||
if (externalLayout) {
|
||||
previewExportOptions.setExternalLayoutName(
|
||||
externalLayout.getName()
|
||||
);
|
||||
}
|
||||
try {
|
||||
await this.getPreviewDebuggerServer().startServer();
|
||||
} catch (err) {
|
||||
console.error(
|
||||
'Unable to start the Debugger Server for the preview:',
|
||||
err
|
||||
);
|
||||
}
|
||||
|
||||
const previewDebuggerServerAddress = getDebuggerServerAddress();
|
||||
if (previewDebuggerServerAddress) {
|
||||
previewExportOptions.useWebsocketDebuggerClientWithServerAddress(
|
||||
previewDebuggerServerAddress.address,
|
||||
'' + previewDebuggerServerAddress.port
|
||||
);
|
||||
}
|
||||
const { outputDir, exporter, gdjsRoot } = await prepareExporter();
|
||||
|
||||
const includeFileHashs = this.props.getIncludeFileHashs();
|
||||
for (const includeFile in includeFileHashs) {
|
||||
const hash = includeFileHashs[includeFile];
|
||||
previewExportOptions.setIncludeFileHash(includeFile, hash);
|
||||
}
|
||||
var previewStartTime = performance.now();
|
||||
|
||||
// Give the preview the path to the "@electron/remote" module of the editor,
|
||||
// as this is required by some features and we've not removed dependency
|
||||
// on "@electron/remote" yet.
|
||||
previewExportOptions.setElectronRemoteRequirePath(
|
||||
path.join(
|
||||
gdjsRoot,
|
||||
'../preview_node_modules',
|
||||
'@electron/remote',
|
||||
'renderer/index.js'
|
||||
)
|
||||
);
|
||||
const previewExportOptions = new gd.PreviewExportOptions(
|
||||
project,
|
||||
outputDir
|
||||
);
|
||||
previewExportOptions.setIsDevelopmentEnvironment(Window.isDev());
|
||||
previewExportOptions.setLayoutName(layout.getName());
|
||||
previewExportOptions.setIsInGameEdition(previewOptions.isForInGameEdition);
|
||||
if (externalLayout) {
|
||||
previewExportOptions.setExternalLayoutName(externalLayout.getName());
|
||||
}
|
||||
|
||||
const debuggerIds = this.getPreviewDebuggerServer().getExistingDebuggerIds();
|
||||
const shouldHotReload =
|
||||
previewOptions.hotReload && !!debuggerIds.length;
|
||||
const previewDebuggerServerAddress = getDebuggerServerAddress();
|
||||
if (previewDebuggerServerAddress) {
|
||||
previewExportOptions.useWebsocketDebuggerClientWithServerAddress(
|
||||
previewDebuggerServerAddress.address,
|
||||
'' + previewDebuggerServerAddress.port
|
||||
);
|
||||
}
|
||||
|
||||
previewExportOptions.setProjectDataOnlyExport(
|
||||
// Only export project data if asked and if a hot-reloading is being done.
|
||||
shouldHotReload && previewOptions.projectDataOnlyExport
|
||||
);
|
||||
const includeFileHashs = this.props.getIncludeFileHashs();
|
||||
for (const includeFile in includeFileHashs) {
|
||||
const hash = includeFileHashs[includeFile];
|
||||
previewExportOptions.setIncludeFileHash(includeFile, hash);
|
||||
}
|
||||
|
||||
previewExportOptions.setFullLoadingScreen(
|
||||
previewOptions.fullLoadingScreen
|
||||
);
|
||||
previewExportOptions.setGDevelopVersionWithHash(
|
||||
getIDEVersionWithHash()
|
||||
);
|
||||
previewExportOptions.setCrashReportUploadLevel(
|
||||
this.props.crashReportUploadLevel
|
||||
);
|
||||
previewExportOptions.setPreviewContext(this.props.previewContext);
|
||||
previewExportOptions.setProjectTemplateSlug(
|
||||
project.getTemplateSlug()
|
||||
);
|
||||
previewExportOptions.setSourceGameId(this.props.sourceGameId);
|
||||
// Give the preview the path to the "@electron/remote" module of the editor,
|
||||
// as this is required by some features and we've not removed dependency
|
||||
// on "@electron/remote" yet.
|
||||
previewExportOptions.setElectronRemoteRequirePath(
|
||||
path.join(
|
||||
gdjsRoot,
|
||||
'../preview_node_modules',
|
||||
'@electron/remote',
|
||||
'renderer/index.js'
|
||||
)
|
||||
);
|
||||
|
||||
if (previewOptions.fallbackAuthor) {
|
||||
previewExportOptions.setFallbackAuthor(
|
||||
previewOptions.fallbackAuthor.id,
|
||||
previewOptions.fallbackAuthor.username
|
||||
);
|
||||
}
|
||||
if (previewOptions.authenticatedPlayer) {
|
||||
previewExportOptions.setAuthenticatedPlayer(
|
||||
previewOptions.authenticatedPlayer.playerId,
|
||||
previewOptions.authenticatedPlayer.playerUsername,
|
||||
previewOptions.authenticatedPlayer.playerToken
|
||||
);
|
||||
}
|
||||
if (previewOptions.captureOptions) {
|
||||
if (previewOptions.captureOptions.screenshots) {
|
||||
previewOptions.captureOptions.screenshots.forEach(
|
||||
screenshot => {
|
||||
previewExportOptions.addScreenshotCapture(
|
||||
screenshot.delayTimeInSeconds,
|
||||
screenshot.signedUrl,
|
||||
screenshot.publicUrl
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
const debuggerIds = this.getPreviewDebuggerServer().getExistingDebuggerIds();
|
||||
const shouldHotReload = previewOptions.hotReload && !!debuggerIds.length;
|
||||
|
||||
exporter.exportProjectForPixiPreview(previewExportOptions);
|
||||
previewExportOptions.delete();
|
||||
exporter.delete();
|
||||
previewExportOptions.setProjectDataOnlyExport(
|
||||
// Only export project data if asked and if a hot-reloading is being done.
|
||||
shouldHotReload && previewOptions.projectDataOnlyExport
|
||||
);
|
||||
|
||||
if (shouldHotReload) {
|
||||
debuggerIds.forEach(debuggerId => {
|
||||
this.getPreviewDebuggerServer().sendMessage(debuggerId, {
|
||||
command: 'hotReload',
|
||||
});
|
||||
});
|
||||
previewExportOptions.setFullLoadingScreen(previewOptions.fullLoadingScreen);
|
||||
previewExportOptions.setGDevelopVersionWithHash(getIDEVersionWithHash());
|
||||
previewExportOptions.setCrashReportUploadLevel(
|
||||
this.props.crashReportUploadLevel
|
||||
);
|
||||
previewExportOptions.setPreviewContext(this.props.previewContext);
|
||||
previewExportOptions.setProjectTemplateSlug(project.getTemplateSlug());
|
||||
previewExportOptions.setSourceGameId(this.props.sourceGameId);
|
||||
|
||||
if (
|
||||
this.state.hotReloadsCount % 16 === 0 &&
|
||||
this._hotReloadSubscriptionChecker
|
||||
) {
|
||||
this._hotReloadSubscriptionChecker.checkUserHasSubscription();
|
||||
}
|
||||
this.setState(state => ({
|
||||
hotReloadsCount: state.hotReloadsCount + 1,
|
||||
}));
|
||||
} else {
|
||||
switchToPreview({
|
||||
previewIndexHtmlLocation: `file://${outputDir}/index.html`,
|
||||
});
|
||||
this._openPreviewWindow(project, outputDir, previewOptions);
|
||||
}
|
||||
},
|
||||
time => console.info(`Preview took ${time}ms`)
|
||||
);
|
||||
if (previewOptions.fallbackAuthor) {
|
||||
previewExportOptions.setFallbackAuthor(
|
||||
previewOptions.fallbackAuthor.id,
|
||||
previewOptions.fallbackAuthor.username
|
||||
);
|
||||
}
|
||||
if (previewOptions.authenticatedPlayer) {
|
||||
previewExportOptions.setAuthenticatedPlayer(
|
||||
previewOptions.authenticatedPlayer.playerId,
|
||||
previewOptions.authenticatedPlayer.playerUsername,
|
||||
previewOptions.authenticatedPlayer.playerToken
|
||||
);
|
||||
}
|
||||
if (previewOptions.captureOptions) {
|
||||
if (previewOptions.captureOptions.screenshots) {
|
||||
previewOptions.captureOptions.screenshots.forEach(screenshot => {
|
||||
previewExportOptions.addScreenshotCapture(
|
||||
screenshot.delayTimeInSeconds,
|
||||
screenshot.signedUrl,
|
||||
screenshot.publicUrl
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
exporter.exportProjectForPixiPreview(previewExportOptions);
|
||||
previewExportOptions.delete();
|
||||
exporter.delete();
|
||||
|
||||
if (shouldHotReload) {
|
||||
debuggerIds.forEach(debuggerId => {
|
||||
this.getPreviewDebuggerServer().sendMessage(debuggerId, {
|
||||
command: 'hotReload',
|
||||
});
|
||||
});
|
||||
|
||||
if (
|
||||
this.state.hotReloadsCount % 16 === 0 &&
|
||||
this._hotReloadSubscriptionChecker
|
||||
) {
|
||||
this._hotReloadSubscriptionChecker.checkUserHasSubscription();
|
||||
}
|
||||
this.setState(state => ({
|
||||
hotReloadsCount: state.hotReloadsCount + 1,
|
||||
}));
|
||||
} else {
|
||||
if (previewOptions.isForInGameEdition) {
|
||||
attachToPreview({
|
||||
previewIndexHtmlLocation: `file://${outputDir}/index.html`,
|
||||
});
|
||||
}
|
||||
|
||||
if (previewOptions.numberOfWindows >= 1) {
|
||||
this._openPreviewWindow(project, outputDir, previewOptions);
|
||||
}
|
||||
}
|
||||
|
||||
const previewStopTime = performance.now();
|
||||
console.info(`Preview took ${previewStopTime - previewStartTime}ms`);
|
||||
};
|
||||
|
||||
getPreviewDebuggerServer() {
|
||||
|
@@ -15,6 +15,7 @@ export type LaunchPreviewOptions = {
|
||||
fullLoadingScreen?: boolean,
|
||||
forceDiagnosticReport?: boolean,
|
||||
numberOfWindows?: number,
|
||||
isForInGameEdition?: {forcedSceneName: string},
|
||||
launchCaptureOptions?: LaunchCaptureOptions,
|
||||
};
|
||||
export type CaptureOptions = {|
|
||||
@@ -40,6 +41,7 @@ export type PreviewOptions = {|
|
||||
playerToken: string,
|
||||
},
|
||||
numberOfWindows: number,
|
||||
isForInGameEdition: boolean,
|
||||
getIsMenuBarHiddenInPreview: () => boolean,
|
||||
getIsAlwaysOnTopInPreview: () => boolean,
|
||||
captureOptions: CaptureOptions,
|
||||
|
@@ -87,21 +87,21 @@ const PreviewAndShareButtons = React.memo<PreviewAndShareButtonsProps>(
|
||||
click: async () => {
|
||||
await onPreviewWithoutHotReload({ numberOfWindows: 2 });
|
||||
},
|
||||
enabled: isPreviewEnabled && !hasPreviewsRunning,
|
||||
enabled: isPreviewEnabled,
|
||||
},
|
||||
{
|
||||
label: i18n._(t`3 previews in 3 windows`),
|
||||
click: async () => {
|
||||
onPreviewWithoutHotReload({ numberOfWindows: 3 });
|
||||
},
|
||||
enabled: isPreviewEnabled && !hasPreviewsRunning,
|
||||
enabled: isPreviewEnabled,
|
||||
},
|
||||
{
|
||||
label: i18n._(t`4 previews in 4 windows`),
|
||||
click: async () => {
|
||||
onPreviewWithoutHotReload({ numberOfWindows: 4 });
|
||||
},
|
||||
enabled: isPreviewEnabled && !hasPreviewsRunning,
|
||||
enabled: isPreviewEnabled,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@@ -621,7 +621,6 @@ const MainFrame = (props: Props) => {
|
||||
<ExtensionIcon />
|
||||
) : null;
|
||||
|
||||
|
||||
// Scene editors can have an embedded game, so they redefine manually
|
||||
// which components can have clicks/touches.
|
||||
const removePointerEvents = kind === 'layout';
|
||||
@@ -1614,6 +1613,7 @@ const MainFrame = (props: Props) => {
|
||||
fullLoadingScreen,
|
||||
forceDiagnosticReport,
|
||||
launchCaptureOptions,
|
||||
isForInGameEdition,
|
||||
}: LaunchPreviewOptions) => {
|
||||
if (!currentProject) return;
|
||||
if (currentProject.getLayoutsCount() === 0) return;
|
||||
@@ -1624,10 +1624,14 @@ const MainFrame = (props: Props) => {
|
||||
setPreviewLoading(true);
|
||||
notifyPreviewOrExportWillStart(state.editorTabs);
|
||||
|
||||
const layoutName = previewState.isPreviewOverriden
|
||||
const layoutName = isForInGameEdition
|
||||
? isForInGameEdition.forcedSceneName
|
||||
: previewState.isPreviewOverriden
|
||||
? previewState.overridenPreviewLayoutName
|
||||
: previewState.previewLayoutName;
|
||||
const externalLayoutName = previewState.isPreviewOverriden
|
||||
const externalLayoutName = isForInGameEdition
|
||||
? null
|
||||
: previewState.isPreviewOverriden
|
||||
? previewState.overridenPreviewExternalLayoutName
|
||||
: previewState.previewExternalLayoutName;
|
||||
|
||||
@@ -1679,23 +1683,25 @@ const MainFrame = (props: Props) => {
|
||||
authenticatedPlayer,
|
||||
getIsMenuBarHiddenInPreview: preferences.getIsMenuBarHiddenInPreview,
|
||||
getIsAlwaysOnTopInPreview: preferences.getIsAlwaysOnTopInPreview,
|
||||
numberOfWindows: numberOfWindows || 1,
|
||||
numberOfWindows: numberOfWindows === undefined ? 1 : numberOfWindows,
|
||||
isForInGameEdition: !!isForInGameEdition,
|
||||
captureOptions,
|
||||
onCaptureFinished,
|
||||
});
|
||||
setPreviewLoading(false);
|
||||
|
||||
sendPreviewStarted({
|
||||
quickCustomizationGameId:
|
||||
quickCustomizationDialogOpenedFromGameId || null,
|
||||
networkPreview: !!networkPreview,
|
||||
hotReload: !!hotReload,
|
||||
projectDataOnlyExport: !!projectDataOnlyExport,
|
||||
fullLoadingScreen: !!fullLoadingScreen,
|
||||
numberOfWindows: numberOfWindows || 1,
|
||||
forceDiagnosticReport: !!forceDiagnosticReport,
|
||||
previewLaunchDuration: Date.now() - startTime,
|
||||
});
|
||||
if (!isForInGameEdition)
|
||||
sendPreviewStarted({
|
||||
quickCustomizationGameId:
|
||||
quickCustomizationDialogOpenedFromGameId || null,
|
||||
networkPreview: !!networkPreview,
|
||||
hotReload: !!hotReload,
|
||||
projectDataOnlyExport: !!projectDataOnlyExport,
|
||||
fullLoadingScreen: !!fullLoadingScreen,
|
||||
numberOfWindows: numberOfWindows || 1,
|
||||
forceDiagnosticReport: !!forceDiagnosticReport,
|
||||
previewLaunchDuration: Date.now() - startTime,
|
||||
});
|
||||
|
||||
if (inAppTutorialOrchestratorRef.current) {
|
||||
inAppTutorialOrchestratorRef.current.onPreviewLaunch();
|
||||
@@ -1779,6 +1785,21 @@ const MainFrame = (props: Props) => {
|
||||
[launchPreview]
|
||||
);
|
||||
|
||||
const onLaunchPreviewForInGameEdition = React.useCallback(
|
||||
({ sceneName }: {| sceneName: string |}) => {
|
||||
launchPreview({
|
||||
networkPreview: false,
|
||||
hotReload: false,
|
||||
forceDiagnosticReport: false,
|
||||
isForInGameEdition: {
|
||||
forcedSceneName: sceneName,
|
||||
},
|
||||
numberOfWindows: 0,
|
||||
});
|
||||
},
|
||||
[launchPreview]
|
||||
);
|
||||
|
||||
const launchQuickCustomizationPreview = React.useCallback(
|
||||
() =>
|
||||
launchPreview({
|
||||
@@ -3577,7 +3598,30 @@ const MainFrame = (props: Props) => {
|
||||
'main-frame' /* The root styling, done in CSS to read some CSS variables. */
|
||||
}
|
||||
>
|
||||
<EmbeddedGameFrame previewDebuggerServer={previewDebuggerServer} />
|
||||
{!!renderPreviewLauncher &&
|
||||
renderPreviewLauncher(
|
||||
{
|
||||
crashReportUploadLevel:
|
||||
preferences.values.previewCrashReportUploadLevel ||
|
||||
'exclude-javascript-code-events',
|
||||
previewContext: quickCustomizationDialogOpenedFromGameId
|
||||
? 'preview-quick-customization'
|
||||
: 'preview',
|
||||
sourceGameId: quickCustomizationDialogOpenedFromGameId || '',
|
||||
getIncludeFileHashs:
|
||||
eventsFunctionsExtensionsContext.getIncludeFileHashs,
|
||||
onExport: () => openShareDialog('publish'),
|
||||
onCaptureFinished,
|
||||
},
|
||||
(previewLauncher: ?PreviewLauncherInterface) => {
|
||||
_previewLauncher.current = previewLauncher;
|
||||
}
|
||||
)}
|
||||
<EmbeddedGameFrame
|
||||
key={currentProject ? currentProject.ptr : 0}
|
||||
previewDebuggerServer={previewDebuggerServer || null}
|
||||
onLaunchPreviewForInGameEdition={onLaunchPreviewForInGameEdition}
|
||||
/>
|
||||
{!!renderMainMenu &&
|
||||
renderMainMenu(
|
||||
{ ...buildMainMenuProps, isApplicationTopLevelMenu: true },
|
||||
@@ -3849,7 +3893,11 @@ const MainFrame = (props: Props) => {
|
||||
<LoaderModal
|
||||
show={showLoader}
|
||||
progress={fileMetadataOpeningProgress}
|
||||
message={loaderModalOpeningMessage || fileMetadataOpeningMessage}
|
||||
message={
|
||||
loaderModalOpeningMessage ||
|
||||
fileMetadataOpeningMessage ||
|
||||
(previewLoading ? t`Loading preview...` : null)
|
||||
}
|
||||
/>
|
||||
<Snackbar
|
||||
open={state.snackMessageOpen}
|
||||
@@ -3872,25 +3920,6 @@ const MainFrame = (props: Props) => {
|
||||
initialTab: shareDialogInitialTab,
|
||||
gamesList,
|
||||
})}
|
||||
{!!renderPreviewLauncher &&
|
||||
renderPreviewLauncher(
|
||||
{
|
||||
crashReportUploadLevel:
|
||||
preferences.values.previewCrashReportUploadLevel ||
|
||||
'exclude-javascript-code-events',
|
||||
previewContext: quickCustomizationDialogOpenedFromGameId
|
||||
? 'preview-quick-customization'
|
||||
: 'preview',
|
||||
sourceGameId: quickCustomizationDialogOpenedFromGameId || '',
|
||||
getIncludeFileHashs:
|
||||
eventsFunctionsExtensionsContext.getIncludeFileHashs,
|
||||
onExport: () => openShareDialog('publish'),
|
||||
onCaptureFinished,
|
||||
},
|
||||
(previewLauncher: ?PreviewLauncherInterface) => {
|
||||
_previewLauncher.current = previewLauncher;
|
||||
}
|
||||
)}
|
||||
{chooseResourceOptions && onResourceChosen && !!currentProject && (
|
||||
<NewResourceDialog
|
||||
project={currentProject}
|
||||
|
@@ -249,6 +249,11 @@ export default class SceneEditor extends React.Component<Props, State> {
|
||||
this.resourceExternallyChangedCallbackId = registerOnResourceExternallyChangedCallback(
|
||||
this.onResourceExternallyChanged.bind(this)
|
||||
);
|
||||
if (this.props.isActive) {
|
||||
if (this.props.layout && this.state.gameEditorMode === 'embedded-game') {
|
||||
switchToSceneEdition({ sceneName: this.props.layout.getName() });
|
||||
}
|
||||
}
|
||||
}
|
||||
componentWillUnmount() {
|
||||
unregisterOnResourceExternallyChangedCallback(
|
||||
|
@@ -28,14 +28,15 @@ type Props = {|
|
||||
progress?: ?number,
|
||||
|};
|
||||
|
||||
const transitionDuration = { enter: 0, exit: 150 };
|
||||
const transitionDuration = { enter: 400 };
|
||||
|
||||
const LoaderModal = ({ progress, message, show }: Props) => {
|
||||
const isInfinite = progress === null || progress === undefined;
|
||||
if (!show) return null;
|
||||
return (
|
||||
<I18n>
|
||||
{({ i18n }) => (
|
||||
<Dialog open={show} transitionDuration={transitionDuration}>
|
||||
<Dialog open transitionDuration={transitionDuration}>
|
||||
<DialogContent style={styles.dialogContent}>
|
||||
<div
|
||||
style={{
|
||||
|
Reference in New Issue
Block a user