Add error handler
This commit is contained in:
parent
fa103bcdf6
commit
c44a6858e0
6 changed files with 104 additions and 39 deletions
|
|
@ -34,9 +34,7 @@
|
||||||
<canvas></canvas>
|
<canvas></canvas>
|
||||||
|
|
||||||
<section class="errors-container">
|
<section class="errors-container">
|
||||||
<pre class="errors">
|
<noscript>JavaScript is required for this website.</noscript>
|
||||||
<noscript>JavaScript is required for this website.</noscript>
|
|
||||||
</pre>
|
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ html {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
display: none;
|
margin: var(--normal-margin);
|
||||||
|
|
||||||
pre {
|
pre {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
|
|
|
||||||
53
src/index.ts
53
src/index.ts
|
|
@ -2,6 +2,7 @@ import '../assets/icons/info.svg';
|
||||||
import GameLoop from './game-loop/game-loop';
|
import GameLoop from './game-loop/game-loop';
|
||||||
import './index.scss';
|
import './index.scss';
|
||||||
import { applyArrayPlugins } from './utils/array';
|
import { applyArrayPlugins } from './utils/array';
|
||||||
|
import { ErrorHandler, Severity } from './utils/error-handler';
|
||||||
import { FullScreenHandler } from './utils/full-screen-handler';
|
import { FullScreenHandler } from './utils/full-screen-handler';
|
||||||
import { initializeGPU } from './utils/graphics/initialize-gpu';
|
import { initializeGPU } from './utils/graphics/initialize-gpu';
|
||||||
|
|
||||||
|
|
@ -34,43 +35,49 @@ const getElements = () => ({
|
||||||
restartButton: document.querySelector('button.restart') as HTMLButtonElement,
|
restartButton: document.querySelector('button.restart') as HTMLButtonElement,
|
||||||
canvas: document.querySelector('canvas') as HTMLCanvasElement,
|
canvas: document.querySelector('canvas') as HTMLCanvasElement,
|
||||||
canvasContainer: document.querySelector('main.canvas-container') as HTMLCanvasElement,
|
canvasContainer: document.querySelector('main.canvas-container') as HTMLCanvasElement,
|
||||||
errorContainer: document.querySelector('.errors') as HTMLDivElement,
|
errorContainer: document.querySelector('.errors-container') as HTMLDivElement,
|
||||||
});
|
});
|
||||||
|
|
||||||
const main = async () => {
|
const main = async () => {
|
||||||
applyArrayPlugins();
|
|
||||||
const elements = getElements();
|
const elements = getElements();
|
||||||
|
|
||||||
const defaultTimeToLive = 3500;
|
ErrorHandler.addOnErrorListener((error, metadata) => {
|
||||||
const interval = 50;
|
elements.errorContainer.innerHTML += `
|
||||||
let timeToLive = defaultTimeToLive;
|
<pre class="${error.severity}">${error.message}</div>
|
||||||
setInterval(() => {
|
<p>${JSON.stringify(metadata, null, 2)}</p>
|
||||||
timeToLive = Math.max(0, timeToLive - interval);
|
`;
|
||||||
elements.aside.style.opacity =
|
});
|
||||||
timeToLive == 0 && FullScreenHandler.isInFullScreenMode() ? '0' : '1';
|
|
||||||
}, interval);
|
|
||||||
|
|
||||||
elements.aside.addEventListener('mouseover', () => (timeToLive = defaultTimeToLive));
|
try {
|
||||||
|
applyArrayPlugins();
|
||||||
|
|
||||||
new FullScreenHandler(
|
const defaultTimeToLive = 3500;
|
||||||
elements.minimizeFullScreenButton,
|
const interval = 50;
|
||||||
elements.maximizeFullScreenButton,
|
let timeToLive = defaultTimeToLive;
|
||||||
document.body
|
setInterval(() => {
|
||||||
);
|
timeToLive = Math.max(0, timeToLive - interval);
|
||||||
|
elements.aside.style.opacity =
|
||||||
|
timeToLive == 0 && FullScreenHandler.isInFullScreenMode() ? '0' : '1';
|
||||||
|
}, interval);
|
||||||
|
elements.aside.addEventListener('mouseover', () => (timeToLive = defaultTimeToLive));
|
||||||
|
|
||||||
const gpu = await initializeGPU();
|
new FullScreenHandler(
|
||||||
|
elements.minimizeFullScreenButton,
|
||||||
|
elements.maximizeFullScreenButton,
|
||||||
|
document.body
|
||||||
|
);
|
||||||
|
|
||||||
let game: GameLoop | null = null;
|
const gpu = await initializeGPU();
|
||||||
|
let game: GameLoop | null = null;
|
||||||
|
|
||||||
elements.restartButton.addEventListener('click', () => game?.destroy());
|
elements.restartButton.addEventListener('click', () => game?.destroy());
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
try {
|
|
||||||
game = new GameLoop(elements.canvas, gpu);
|
game = new GameLoop(elements.canvas, gpu);
|
||||||
await game.start();
|
await game.start();
|
||||||
} catch (e) {
|
|
||||||
elements.errorContainer.innerHTML = e.message;
|
|
||||||
}
|
}
|
||||||
|
} catch (e) {
|
||||||
|
ErrorHandler.addError(Severity.ERROR, e.message);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
45
src/utils/error-handler.ts
Normal file
45
src/utils/error-handler.ts
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
export enum Severity {
|
||||||
|
INFO = 'info',
|
||||||
|
WARNING = 'warning',
|
||||||
|
ERROR = 'error',
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ErrorHandlerError {
|
||||||
|
severity: Severity;
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ErrorMetadata = { [key: string]: any };
|
||||||
|
|
||||||
|
export class ErrorHandler {
|
||||||
|
private static readonly errors: Array<ErrorHandlerError> = [];
|
||||||
|
private static metadata: ErrorMetadata = {};
|
||||||
|
private static onErrorListeners: Array<
|
||||||
|
(error: ErrorHandlerError, metadata: ErrorMetadata) => void
|
||||||
|
> = [];
|
||||||
|
|
||||||
|
public static addException(exception: Error) {
|
||||||
|
ErrorHandler.addError(Severity.ERROR, exception.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static addError(severity: Severity, message: string) {
|
||||||
|
ErrorHandler.errors.push({ severity, message });
|
||||||
|
ErrorHandler.onErrorListeners.forEach((listener) =>
|
||||||
|
listener({ severity, message }, ErrorHandler.metadata)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static addMetadata(key: string, value: any) {
|
||||||
|
const serialized = {};
|
||||||
|
for (const k in value) {
|
||||||
|
serialized[k] = value[k];
|
||||||
|
}
|
||||||
|
ErrorHandler.metadata[key] = serialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static addOnErrorListener(
|
||||||
|
listener: (error: ErrorHandlerError, metadata: ErrorMetadata) => void
|
||||||
|
) {
|
||||||
|
ErrorHandler.onErrorListeners.push(listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
|
import { ErrorHandler, Severity } from '../error-handler';
|
||||||
|
|
||||||
export const initializeGPU = async (): Promise<GPUDevice> => {
|
export const initializeGPU = async (): Promise<GPUDevice> => {
|
||||||
const gpu = navigator.gpu;
|
const gpu = navigator.gpu;
|
||||||
if (!gpu) {
|
if (!gpu) {
|
||||||
throw new Error('WebGPU is not supported');
|
throw new Error('WebGPU is not supported in your browser');
|
||||||
}
|
}
|
||||||
|
|
||||||
const adapter = await gpu.requestAdapter({
|
const adapter = await gpu.requestAdapter({
|
||||||
|
|
@ -12,5 +14,15 @@ export const initializeGPU = async (): Promise<GPUDevice> => {
|
||||||
throw new Error('Could not request adatper');
|
throw new Error('Could not request adatper');
|
||||||
}
|
}
|
||||||
|
|
||||||
return await adapter.requestDevice(); // could request more resources
|
ErrorHandler.addMetadata('adapter', await adapter.requestAdapterInfo());
|
||||||
|
ErrorHandler.addMetadata('features', adapter.features);
|
||||||
|
ErrorHandler.addMetadata('limits', adapter.limits);
|
||||||
|
|
||||||
|
const gpuDevice = await adapter.requestDevice(); // could request more resources
|
||||||
|
|
||||||
|
gpuDevice.addEventListener('uncapturederror', (event: GPUUncapturedErrorEvent) =>
|
||||||
|
ErrorHandler.addError(Severity.ERROR, event.error.message)
|
||||||
|
);
|
||||||
|
|
||||||
|
return gpuDevice;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { ErrorHandler, Severity } from '../error-handler';
|
||||||
|
|
||||||
export const smartCompile = (device: GPUDevice, ...code: Array<string>) => {
|
export const smartCompile = (device: GPUDevice, ...code: Array<string>) => {
|
||||||
const concatenated = code.join('\n\n');
|
const concatenated = code.join('\n\n');
|
||||||
|
|
||||||
|
|
@ -5,17 +7,18 @@ export const smartCompile = (device: GPUDevice, ...code: Array<string>) => {
|
||||||
code: concatenated,
|
code: concatenated,
|
||||||
});
|
});
|
||||||
|
|
||||||
module
|
module.getCompilationInfo().then((info) =>
|
||||||
.getCompilationInfo()
|
info.messages.forEach((message) =>
|
||||||
.then((info) =>
|
ErrorHandler.addError(
|
||||||
info.messages.forEach((message) =>
|
{
|
||||||
console.warn(
|
info: Severity.INFO,
|
||||||
message.type,
|
warning: Severity.WARNING,
|
||||||
message.message,
|
error: Severity.ERROR,
|
||||||
concatenated.split('\n')[message.lineNum - 1]
|
}[message.type],
|
||||||
)
|
`${message.message}\n${concatenated.split('\n')[message.lineNum - 1]}`
|
||||||
)
|
)
|
||||||
);
|
)
|
||||||
|
);
|
||||||
|
|
||||||
return module;
|
return module;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue