diff --git a/src/libs/compiler/DashService.ts b/src/libs/compiler/DashService.ts index 7113c5dc8..9980e858f 100644 --- a/src/libs/compiler/DashService.ts +++ b/src/libs/compiler/DashService.ts @@ -35,10 +35,32 @@ export class DashService implements AsyncDisposable { constructor(public project: BedrockProject, fileSystem?: BaseFileSystem) { this.worker.onmessage = this.onWorkerMessage.bind(this) + this.worker.onerror = this.onWorkerError.bind(this) this.outputFileSystem = new WorkerFileSystemEntryPoint(this.worker, fileSystem ?? project.outputFileSystem, 'outputFileSystem') } + /** + * Backstop for a hard worker crash. The pending request cannot be resolved here (the error event carries no + * request id), but we at least clear the stuck progress notification and reset the building flag so the UI + * does not stay in a permanent loading state. + */ + private onWorkerError(event: ErrorEvent) { + console.error('Dash worker error:', event.message) + + this.clearProgressNotification() + + this.building = false + } + + private clearProgressNotification() { + if (this.progressNotification === null) return + + NotificationSystem.clearNotification(this.progressNotification) + + this.progressNotification = null + } + private async onWorkerMessage(event: MessageEvent) { if (!event.data) return @@ -68,7 +90,7 @@ export class DashService implements AsyncDisposable { public async setup(mode: 'development' | 'production', compilerConfigPath?: string) { this.mode = mode - await sendAndWait( + const response = await sendAndWait( { action: 'setup', config: this.project.config, @@ -79,6 +101,8 @@ export class DashService implements AsyncDisposable { this.worker ) + if (response?.error) throw new Error(response.error) + this.isSetup = true } @@ -125,36 +149,44 @@ export class DashService implements AsyncDisposable { this.progressNotification = NotificationSystem.addProgressNotification('manufacturing', 0, 1, undefined, undefined) - await sendAndWait( - { - action: 'build', - }, - this.worker - ) + try { + const response = await sendAndWait( + { + action: 'build', + }, + this.worker + ) - NotificationSystem.clearNotification(this.progressNotification) + if (response?.error) throw new Error(response.error) + } finally { + this.clearProgressNotification() - this.building = false + this.building = false - if (this.inFlightBuildRequest) { - this.inFlightBuildRequest = false + if (this.inFlightBuildRequest) { + this.inFlightBuildRequest = false - this.build() + this.build() + } } } public async compileFiles(paths: string[]) { this.progressNotification = NotificationSystem.addProgressNotification('manufacturing', 0, 1, undefined, undefined) - await sendAndWait( - { - action: 'compileFiles', - paths, - }, - this.worker - ) + try { + const response = await sendAndWait( + { + action: 'compileFiles', + paths, + }, + this.worker + ) - NotificationSystem.clearNotification(this.progressNotification) + if (response?.error) throw new Error(response.error) + } finally { + this.clearProgressNotification() + } } private async setupCompileActions() { diff --git a/src/libs/compiler/DashWorker.ts b/src/libs/compiler/DashWorker.ts index ec84dc52e..ca3696cf2 100644 --- a/src/libs/compiler/DashWorker.ts +++ b/src/libs/compiler/DashWorker.ts @@ -77,45 +77,81 @@ async function setup(config: any, mode: 'development' | 'production', configPath }) }) - await dash.setup({ - fileTypes: await getJsonData('packages/minecraftBedrock/fileDefinitions.json'), - packTypes: await getJsonData('packages/minecraftBedrock/packDefinitions.json'), - }) + try { + await dash.setup({ + fileTypes: await getJsonData('packages/minecraftBedrock/fileDefinitions.json'), + packTypes: await getJsonData('packages/minecraftBedrock/packDefinitions.json'), + }) - postMessage({ - action: 'setupComplete', - id: actionId, - }) + postMessage({ + action: 'setupComplete', + id: actionId, + }) + } catch (error: any) { + postMessage({ + action: 'setupComplete', + id: actionId, + error: error?.message ?? String(error), + }) + } } async function build(actionId: string) { if (!dash) { console.warn('Tried building but Dash is not setup yet!') + postMessage({ + action: 'buildComplete', + id: actionId, + error: 'Tried building but Dash is not setup yet!', + }) + return } - await dash.build() + try { + await dash.build() - postMessage({ - action: 'buildComplete', - id: actionId, - }) + postMessage({ + action: 'buildComplete', + id: actionId, + }) + } catch (error: any) { + postMessage({ + action: 'buildComplete', + id: actionId, + error: error?.message ?? String(error), + }) + } } async function compileFiles(actionId: string, paths: string[]) { if (!dash) { console.warn('Tried compiling files but Dash is not setup yet!') + postMessage({ + action: 'compileFilesComplete', + id: actionId, + error: 'Tried compiling files but Dash is not setup yet!', + }) + return } - await dash.updateFiles(paths) + try { + await dash.updateFiles(paths) - postMessage({ - action: 'compileFilesComplete', - id: actionId, - }) + postMessage({ + action: 'compileFilesComplete', + id: actionId, + }) + } catch (error: any) { + postMessage({ + action: 'compileFilesComplete', + id: actionId, + error: error?.message ?? String(error), + }) + } } onmessage = (event: any) => { diff --git a/src/libs/export/exporters/McAddon.ts b/src/libs/export/exporters/McAddon.ts index 1f89eec5a..9c27ea4f8 100644 --- a/src/libs/export/exporters/McAddon.ts +++ b/src/libs/export/exporters/McAddon.ts @@ -6,6 +6,7 @@ import { addGeneratedWith, incrementManifestVersions, saveOrDownload } from '../ import { DashService } from '@/libs/compiler/DashService' import { BedrockProject } from '@/libs/project/BedrockProject' import { Settings } from '@/libs/settings/Settings' +import { ReportErrorWindow } from '@/components/Windows/ReportError/ReportErrorWindow' export async function exportAsMcAddon() { if (!ProjectManager.currentProject) return @@ -15,16 +16,20 @@ export async function exportAsMcAddon() { if (Settings.get('addGeneratedWith')) await addGeneratedWith() const dash = new DashService(ProjectManager.currentProject, fileSystem) - await dash.setup('production') - await dash.build() - await dash.dispose() - - const zipFile = await zipDirectory(fileSystem, join(ProjectManager.currentProject.path, 'builds/dist')) - const savePath = join(ProjectManager.currentProject.path, 'builds/', ProjectManager.currentProject.name) + '.mcaddon' try { + await dash.setup('production') + await dash.build() + + const zipFile = await zipDirectory(fileSystem, join(ProjectManager.currentProject.path, 'builds/dist')) + const savePath = join(ProjectManager.currentProject.path, 'builds/', ProjectManager.currentProject.name) + '.mcaddon' + await saveOrDownload(savePath, zipFile, fileSystem) } catch (err) { console.error(err) + + ReportErrorWindow.openErrorWindow(err instanceof Error ? err : new Error(String(err))) + } finally { + await dash.dispose() } } diff --git a/src/libs/export/exporters/McTemplate.ts b/src/libs/export/exporters/McTemplate.ts index 7cb51f758..403006928 100644 --- a/src/libs/export/exporters/McTemplate.ts +++ b/src/libs/export/exporters/McTemplate.ts @@ -10,6 +10,7 @@ import { Data } from '@/libs/data/Data' import { Windows } from '@/components/Windows/Windows' import { DropdownWindow } from '@/components/Windows/Dropdown/DropdownWindow' import { Settings } from '@/libs/settings/Settings' +import { ReportErrorWindow } from '@/components/Windows/ReportError/ReportErrorWindow' export async function exportAsTemplate(asMcworld = false) { if (!ProjectManager.currentProject) return @@ -21,9 +22,19 @@ export async function exportAsTemplate(asMcworld = false) { const projectPath = ProjectManager.currentProject.path const dash = new DashService(ProjectManager.currentProject, fileSystem) - await dash.setup('production') - await dash.build() - await dash.dispose() + + try { + await dash.setup('production') + await dash.build() + } catch (err) { + console.error(err) + + ReportErrorWindow.openErrorWindow(err instanceof Error ? err : new Error(String(err))) + + return + } finally { + await dash.dispose() + } let baseWorlds: string[] = []