diff --git a/package-lock.json b/package-lock.json index 710122d..05adfc6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "diabloweb", - "version": "1.0.34", + "version": "1.0.35", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index a630b0a..d03877a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "diabloweb", - "version": "1.0.34", + "version": "1.0.35", "private": true, "dependencies": { "@babel/core": "7.4.3", diff --git a/src/App.js b/src/App.js index 1e44be5..59f6dde 100644 --- a/src/App.js +++ b/src/App.js @@ -155,13 +155,19 @@ class App extends React.Component { } onError(message, stack) { - if (stack) { - mapStackTrace(stack, stack => { - this.setState(({error}) => !error && {error: {message, stack: stack.join("\n")}}); - }); - } else { - this.setState(({error}) => !error && {error: {message}}); - } + (async () => { + const errorObject = {message}; + if (this.saveName) { + errorObject.save = await (await this.fs).fileUrl(this.saveName); + } + if (stack) { + mapStackTrace(stack, stack => { + this.setState(({error}) => !error && {error: {...errorObject, stack: stack.join("\n")}}); + }); + } else { + this.setState(({error}) => !error && {error: errorObject}); + } + })(); } openKeyboard(rect) { @@ -641,7 +647,7 @@ class App extends React.Component {
The following error has occurred:
{error.message}
Click to create an issue on GitHub
- {this.saveName != null &&Download save file
} + {error.save != null && Download save file} )} {!!loading && !started && !error && ( diff --git a/src/api/game.worker.js b/src/api/game.worker.js index a1ccda0..684b9d2 100644 --- a/src/api/game.worker.js +++ b/src/api/game.worker.js @@ -20,6 +20,14 @@ let drawBelt = null; let is_spawn = false; let websocket = null; +function onError(err, action="error") { + if (err instanceof Error) { + worker.postMessage({action, error: err.toString(), stack: err.stack}); + } else { + worker.postMessage({action, error: err.toString()}); + } +} + const ChunkSize = 1 << 20; class RemoteFile { constructor(url) { @@ -27,7 +35,7 @@ class RemoteFile { request.open('HEAD', url, false); request.send(); if (request.status < 200 || request.status >= 300) { - worker.postMessage({action: "error", error: `Failed to load remote file`}); + throw Error('Failed to load remote file'); } this.byteLength = parseInt(request.getResponseHeader('Content-Length')); @@ -54,7 +62,7 @@ class RemoteFile { request.responseType = 'arraybuffer'; request.send(); if (request.status < 200 || request.status >= 300) { - worker.postMessage({action: "error", error: `Failed to load remote file`}); + throw Error('Failed to load remote file'); } else { const header = request.getResponseHeader('Content-Range'); let m, start = 0; @@ -75,7 +83,7 @@ class RemoteFile { const DApi = { exit_error(error) { - worker.postMessage({action: "error", error}); + throw Error(error); }, exit_game() { @@ -131,10 +139,10 @@ const DApi = { } }, code => { if (typeof code !== "number") { - worker.postMessage({action: "error", error: code.toString(), stack: code.stack}); - code = 3; + throw code; + } else { + call_api("SNet_WebsocketStatus", code); } - call_api("SNet_WebsocketStatus", code); }); } else { call_api("SNet_WebsocketStatus", 0); @@ -150,7 +158,7 @@ const DApi = { return websocket ? websocket.readyState !== 1 : false; }, }; - +/* let frameTime = 0, lastTime = 0; function getFPS() { const time = performance.now(); @@ -161,7 +169,7 @@ function getFPS() { lastTime = time; return frameTime ? 1000.0 / frameTime : 0.0; } - +*/ const DApi_renderLegacy = { draw_begin() { renderBatch = { @@ -281,7 +289,7 @@ function try_api(func) { try { func(); } catch (e) { - worker.postMessage({action: "error", error: e.toString(), stack: e.stack}); + onError(e); } } @@ -409,7 +417,7 @@ worker.addEventListener("message", ({data}) => { files = data.files; init_game(data.mpq, data.spawn, data.offscreen).then( () => worker.postMessage({action: "loaded"}), - e => worker.postMessage({action: "failed", error: e.toString(), stack: e.stack})); + e => onError(e, "failed")); break; case "event": call_api(data.func, ...data.params); diff --git a/src/api/loader.js b/src/api/loader.js index cc92b34..6280735 100644 --- a/src/api/loader.js +++ b/src/api/loader.js @@ -102,7 +102,7 @@ async function do_load_game(api, audio, mpq, spawn) { api.onError(data.error, data.stack); break; case "failed": - reject(Error(data.stack || data.error)); + reject({message: data.error, stack: data.stack}); break; case "progress": api.onProgress({text: data.text, loaded: data.loaded, total: data.total}); diff --git a/src/fs.js b/src/fs.js index 724893e..c2cce64 100644 --- a/src/fs.js +++ b/src/fs.js @@ -93,6 +93,13 @@ export default async function create_fs(load) { clear: () => store.clear(), download: name => downloadFile(store, name), upload: file => uploadFile(store, files, file), + fileUrl: async name => { + const file = await store.get(name.toLowerCase()); + if (file) { + const blob = new Blob([file], {type: 'binary/octet-stream'}); + return URL.createObjectURL(blob); + } + }, }; } catch (e) { window.DownloadFile = () => console.error('IndexedDB is not supported'); @@ -104,6 +111,7 @@ export default async function create_fs(load) { clear: () => Promise.resolve(), download: () => Promise.resolve(), upload: () => Promise.resolve(), + fileUrl: () => Promise.resolve(), }; } }