improve error handling

This commit is contained in:
d07riv
2019-08-18 03:33:36 +03:00
parent d922765878
commit 7cbd7b0ea9
6 changed files with 43 additions and 21 deletions

2
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{ {
"name": "diabloweb", "name": "diabloweb",
"version": "1.0.34", "version": "1.0.35",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {

View File

@@ -1,6 +1,6 @@
{ {
"name": "diabloweb", "name": "diabloweb",
"version": "1.0.34", "version": "1.0.35",
"private": true, "private": true,
"dependencies": { "dependencies": {
"@babel/core": "7.4.3", "@babel/core": "7.4.3",

View File

@@ -155,13 +155,19 @@ class App extends React.Component {
} }
onError(message, stack) { onError(message, stack) {
if (stack) { (async () => {
mapStackTrace(stack, stack => { const errorObject = {message};
this.setState(({error}) => !error && {error: {message, stack: stack.join("\n")}}); if (this.saveName) {
}); errorObject.save = await (await this.fs).fileUrl(this.saveName);
} else { }
this.setState(({error}) => !error && {error: {message}}); if (stack) {
} mapStackTrace(stack, stack => {
this.setState(({error}) => !error && {error: {...errorObject, stack: stack.join("\n")}});
});
} else {
this.setState(({error}) => !error && {error: errorObject});
}
})();
} }
openKeyboard(rect) { openKeyboard(rect) {
@@ -641,7 +647,7 @@ class App extends React.Component {
<p className="header">The following error has occurred:</p> <p className="header">The following error has occurred:</p>
<p className="body">{error.message}</p> <p className="body">{error.message}</p>
<p className="footer">Click to create an issue on GitHub</p> <p className="footer">Click to create an issue on GitHub</p>
{this.saveName != null && <p className="link" onClick={this.downloadSave}>Download save file</p>} {error.save != null && <a href={error.save} download={this.saveName}>Download save file</a>}
</Link> </Link>
)} )}
{!!loading && !started && !error && ( {!!loading && !started && !error && (

View File

@@ -20,6 +20,14 @@ let drawBelt = null;
let is_spawn = false; let is_spawn = false;
let websocket = null; 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; const ChunkSize = 1 << 20;
class RemoteFile { class RemoteFile {
constructor(url) { constructor(url) {
@@ -27,7 +35,7 @@ class RemoteFile {
request.open('HEAD', url, false); request.open('HEAD', url, false);
request.send(); request.send();
if (request.status < 200 || request.status >= 300) { 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')); this.byteLength = parseInt(request.getResponseHeader('Content-Length'));
@@ -54,7 +62,7 @@ class RemoteFile {
request.responseType = 'arraybuffer'; request.responseType = 'arraybuffer';
request.send(); request.send();
if (request.status < 200 || request.status >= 300) { 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 { } else {
const header = request.getResponseHeader('Content-Range'); const header = request.getResponseHeader('Content-Range');
let m, start = 0; let m, start = 0;
@@ -75,7 +83,7 @@ class RemoteFile {
const DApi = { const DApi = {
exit_error(error) { exit_error(error) {
worker.postMessage({action: "error", error}); throw Error(error);
}, },
exit_game() { exit_game() {
@@ -131,10 +139,10 @@ const DApi = {
} }
}, code => { }, code => {
if (typeof code !== "number") { if (typeof code !== "number") {
worker.postMessage({action: "error", error: code.toString(), stack: code.stack}); throw code;
code = 3; } else {
call_api("SNet_WebsocketStatus", code);
} }
call_api("SNet_WebsocketStatus", code);
}); });
} else { } else {
call_api("SNet_WebsocketStatus", 0); call_api("SNet_WebsocketStatus", 0);
@@ -150,7 +158,7 @@ const DApi = {
return websocket ? websocket.readyState !== 1 : false; return websocket ? websocket.readyState !== 1 : false;
}, },
}; };
/*
let frameTime = 0, lastTime = 0; let frameTime = 0, lastTime = 0;
function getFPS() { function getFPS() {
const time = performance.now(); const time = performance.now();
@@ -161,7 +169,7 @@ function getFPS() {
lastTime = time; lastTime = time;
return frameTime ? 1000.0 / frameTime : 0.0; return frameTime ? 1000.0 / frameTime : 0.0;
} }
*/
const DApi_renderLegacy = { const DApi_renderLegacy = {
draw_begin() { draw_begin() {
renderBatch = { renderBatch = {
@@ -281,7 +289,7 @@ function try_api(func) {
try { try {
func(); func();
} catch (e) { } 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; files = data.files;
init_game(data.mpq, data.spawn, data.offscreen).then( init_game(data.mpq, data.spawn, data.offscreen).then(
() => worker.postMessage({action: "loaded"}), () => worker.postMessage({action: "loaded"}),
e => worker.postMessage({action: "failed", error: e.toString(), stack: e.stack})); e => onError(e, "failed"));
break; break;
case "event": case "event":
call_api(data.func, ...data.params); call_api(data.func, ...data.params);

View File

@@ -102,7 +102,7 @@ async function do_load_game(api, audio, mpq, spawn) {
api.onError(data.error, data.stack); api.onError(data.error, data.stack);
break; break;
case "failed": case "failed":
reject(Error(data.stack || data.error)); reject({message: data.error, stack: data.stack});
break; break;
case "progress": case "progress":
api.onProgress({text: data.text, loaded: data.loaded, total: data.total}); api.onProgress({text: data.text, loaded: data.loaded, total: data.total});

View File

@@ -93,6 +93,13 @@ export default async function create_fs(load) {
clear: () => store.clear(), clear: () => store.clear(),
download: name => downloadFile(store, name), download: name => downloadFile(store, name),
upload: file => uploadFile(store, files, file), 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) { } catch (e) {
window.DownloadFile = () => console.error('IndexedDB is not supported'); window.DownloadFile = () => console.error('IndexedDB is not supported');
@@ -104,6 +111,7 @@ export default async function create_fs(load) {
clear: () => Promise.resolve(), clear: () => Promise.resolve(),
download: () => Promise.resolve(), download: () => Promise.resolve(),
upload: () => Promise.resolve(), upload: () => Promise.resolve(),
fileUrl: () => Promise.resolve(),
}; };
} }
} }