Compare commits
2
Commits
d7ea38d8ee
...
44504c718d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
44504c718d | ||
|
|
53c8ea03ac |
@@ -64,11 +64,8 @@ export default function CoinDoorHud({ instanceRef, tableInfo, loadTimeMs }: Coin
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleEject = () => {
|
const handleEject = () => {
|
||||||
// dispose() (called by usePinballInstance's unmount cleanup, triggered by
|
// usePinballInstance's unmount cleanup (triggered by this navigation)
|
||||||
// this navigation) tears the instance down immediately. Calling stop()
|
// handles stop()+dispose() itself, sequenced to avoid hanging the tab.
|
||||||
// here too raced it — stop() defers teardown to the engine's next
|
|
||||||
// internal step, and dispose() freeing WASM memory before that step runs
|
|
||||||
// hung the tab.
|
|
||||||
router.push("/");
|
router.push("/");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -74,6 +74,13 @@ export default function PinballCanvasImpl({ tableData, rom, tableInfo }: Pinball
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{status === "starting" && (
|
||||||
|
<div className="absolute inset-0 flex flex-col items-center justify-center gap-4 bg-ink/90 text-paper">
|
||||||
|
<div className="h-12 w-12 animate-spin rounded-full border-4 border-chrome/20 border-t-marquee" />
|
||||||
|
<div className="font-display text-2xl tracking-wide text-marquee">STARTING TABLE</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { hasWebGL2 } from "./webglSupport";
|
|||||||
// outside webpack's module graph (see scripts/copy-engine-assets.mjs).
|
// outside webpack's module graph (see scripts/copy-engine-assets.mjs).
|
||||||
const ENGINE_BASE_URL = "/vendor/vpinball-wasm";
|
const ENGINE_BASE_URL = "/vendor/vpinball-wasm";
|
||||||
|
|
||||||
export type PinballStatus = "idle" | "loading" | "ready" | "running" | "error";
|
export type PinballStatus = "idle" | "loading" | "ready" | "starting" | "running" | "error";
|
||||||
|
|
||||||
interface EngineModule {
|
interface EngineModule {
|
||||||
loadPinball(options: LoadPinballOptions): Promise<PinballInstance>;
|
loadPinball(options: LoadPinballOptions): Promise<PinballInstance>;
|
||||||
@@ -104,8 +104,20 @@ export function usePinballInstance(
|
|||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
disposedRef.current = true;
|
disposedRef.current = true;
|
||||||
instanceRef.current?.dispose();
|
const instance = instanceRef.current;
|
||||||
instanceRef.current = null;
|
instanceRef.current = null;
|
||||||
|
if (instance) {
|
||||||
|
// stop() halts the engine's render loop, but only takes effect on
|
||||||
|
// its next internal step rather than immediately. Calling dispose()
|
||||||
|
// synchronously right after — freeing WASM memory before that step
|
||||||
|
// runs — is what hangs the tab. Give it a couple of frames first.
|
||||||
|
instance.stop();
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
instance.dispose();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
// Intentionally run once: PlayView mounts a fresh PinballCanvasImpl
|
// Intentionally run once: PlayView mounts a fresh PinballCanvasImpl
|
||||||
// (via `key`) per table rather than swapping tableData in place.
|
// (via `key`) per table rather than swapping tableData in place.
|
||||||
@@ -114,8 +126,18 @@ export function usePinballInstance(
|
|||||||
|
|
||||||
const start = useCallback(() => {
|
const start = useCallback(() => {
|
||||||
if (!instanceRef.current) return;
|
if (!instanceRef.current) return;
|
||||||
instanceRef.current.start();
|
setStatus("starting");
|
||||||
setStatus("running");
|
// instance.start() runs synchronously and blocks the main thread for a
|
||||||
|
// noticeable moment (heavy WASM init) — a nested rAF lets the browser
|
||||||
|
// paint the "starting" spinner from the state update above before that
|
||||||
|
// block hits, instead of the UI freezing on the old screen mid-click.
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
if (disposedRef.current || !instanceRef.current) return;
|
||||||
|
instanceRef.current.start();
|
||||||
|
setStatus("running");
|
||||||
|
});
|
||||||
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return { status, progress, error, loadTimeMs, instanceRef, start };
|
return { status, progress, error, loadTimeMs, instanceRef, start };
|
||||||
|
|||||||
+1
-1
@@ -12,7 +12,7 @@
|
|||||||
"lint": "eslint"
|
"lint": "eslint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@valknar/vpinball-wasm": "0.3.2",
|
"@valknar/vpinball-wasm": "0.3.3",
|
||||||
"next": "16.3.2",
|
"next": "16.3.2",
|
||||||
"react": "19.2.8",
|
"react": "19.2.8",
|
||||||
"react-dom": "19.2.8"
|
"react-dom": "19.2.8"
|
||||||
|
|||||||
Generated
+5
-5
@@ -9,8 +9,8 @@ importers:
|
|||||||
.:
|
.:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@valknar/vpinball-wasm':
|
'@valknar/vpinball-wasm':
|
||||||
specifier: 0.3.2
|
specifier: 0.3.3
|
||||||
version: 0.3.2
|
version: 0.3.3
|
||||||
next:
|
next:
|
||||||
specifier: 16.3.2
|
specifier: 16.3.2
|
||||||
version: 16.3.2(@babel/core@7.29.7(supports-color@7.2.0))(@types/node@20.19.43)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
|
version: 16.3.2(@babel/core@7.29.7(supports-color@7.2.0))(@types/node@20.19.43)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
|
||||||
@@ -754,8 +754,8 @@ packages:
|
|||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
|
|
||||||
'@valknar/vpinball-wasm@0.3.2':
|
'@valknar/vpinball-wasm@0.3.3':
|
||||||
resolution: {integrity: sha512-52vkjVzev92khiO8a50N2afYIviX1hHCMzTilN24Pe/7pu5m9LlSQVS3o11rRdItSF5h1RVmTLpoh1ENYrLQJw==, tarball: https://dev.pivoine.art/api/packages/valknar/npm/%40valknar%2Fvpinball-wasm/-/0.3.2/vpinball-wasm-0.3.2.tgz}
|
resolution: {integrity: sha512-It2VO8FrmBa7u+aom4m+JCWaVv6ZOhRn0x8BqqrFbGo2ptTb64G8SgqRXuGU8GldhIvgl3/ug+PsSg3EhETZpg==, tarball: https://dev.pivoine.art/api/packages/valknar/npm/%40valknar%2Fvpinball-wasm/-/0.3.3/vpinball-wasm-0.3.3.tgz}
|
||||||
|
|
||||||
acorn-jsx@5.3.2:
|
acorn-jsx@5.3.2:
|
||||||
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
|
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
|
||||||
@@ -2645,7 +2645,7 @@ snapshots:
|
|||||||
'@unrs/resolver-binding-win32-x64-msvc@1.12.2':
|
'@unrs/resolver-binding-win32-x64-msvc@1.12.2':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@valknar/vpinball-wasm@0.3.2': {}
|
'@valknar/vpinball-wasm@0.3.3': {}
|
||||||
|
|
||||||
acorn-jsx@5.3.2(acorn@8.18.0):
|
acorn-jsx@5.3.2(acorn@8.18.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|||||||
+1
-1
@@ -2,5 +2,5 @@ allowBuilds:
|
|||||||
sharp: false
|
sharp: false
|
||||||
unrs-resolver: false
|
unrs-resolver: false
|
||||||
minimumReleaseAgeExclude:
|
minimumReleaseAgeExclude:
|
||||||
- '@valknar/vpinball-wasm@0.2.0 || 0.3.1 || 0.3.2'
|
- '@valknar/vpinball-wasm@0.2.0 || 0.3.1 || 0.3.2 || 0.3.3'
|
||||||
updateNotifier: false
|
updateNotifier: false
|
||||||
|
|||||||
Reference in New Issue
Block a user