fix(game): resolve race condition in render dirty flag using bitwise OR

The render dirty flag was being reset instead of accumulated, causing
the game to become stuck and unplayable in web builds. Changed from
assignment to bitwise OR to preserve all dirty state updates.

Also adds game layout component and updates Justfile to build frontend.
This commit is contained in:
Ryan Walters
2025-11-22 21:14:24 -06:00
parent 9bf8d0428c
commit c306e992c4
4 changed files with 14 additions and 9 deletions
+2 -1
View File
@@ -36,7 +36,8 @@ samply:
# Build the project for Emscripten # Build the project for Emscripten
web *args: web *args:
bun run pacman/web.build.ts {{args}}; bun run pacman/web.build.ts {{args}}
bun run --cwd web build
caddy file-server --root web/dist/client caddy file-server --root web/dist/client
# Fix linting errors & formatting # Fix linting errors & formatting
+1 -1
View File
@@ -521,7 +521,7 @@ impl Game {
stage_system.in_set(GameplaySet::Respond), stage_system.in_set(GameplaySet::Respond),
( (
(|mut dirty: ResMut<RenderDirty>, score: Res<ScoreResource>, stage: Res<GameStage>| { (|mut dirty: ResMut<RenderDirty>, score: Res<ScoreResource>, stage: Res<GameStage>| {
dirty.0 = score.is_changed() || stage.is_changed(); dirty.0 |= score.is_changed() || stage.is_changed();
}), }),
dirty_render_system.run_if(|dirty: Res<RenderDirty>| dirty.0.not()), dirty_render_system.run_if(|dirty: Res<RenderDirty>| dirty.0.not()),
combined_render_system, combined_render_system,
+9
View File
@@ -0,0 +1,9 @@
import "../../layouts/tailwind.css";
export default function GameLayout({ children }: { children: React.ReactNode }) {
return (
<div className="bg-black text-yellow-400 min-h-screen flex flex-col">
<main className="flex-1">{children}</main>
</div>
);
}
+2 -7
View File
@@ -2,32 +2,27 @@ import { useEffect } from "react";
export default function Page() { export default function Page() {
useEffect(() => { useEffect(() => {
// Only setup Module if not already configured (prevents double-initialization on hot reload)
if (!(window as any).Module) { if (!(window as any).Module) {
const canvas = document.getElementById("canvas"); const canvas = document.getElementById("canvas");
// Simple Module configuration matching the original working approach
(window as any).Module = { (window as any).Module = {
canvas: canvas, canvas: canvas,
locateFile: (path: string) => { locateFile: (path: string) => {
// Return absolute paths for all resources
return path.startsWith("/") ? path : `/${path}`; return path.startsWith("/") ? path : `/${path}`;
}, },
preRun: [], preRun: [],
}; };
// Load the Emscripten script
const script = document.createElement("script"); const script = document.createElement("script");
script.src = "/pacman.js"; script.src = "/pacman.js";
script.async = false; // Load synchronously to ensure Module is configured first script.async = false;
document.body.appendChild(script); document.body.appendChild(script);
// Cleanup function (runs when component unmounts)
return () => { return () => {
script.remove(); script.remove();
}; };
} }
}, []); // Empty dependency array = run once on mount }, []);
return ( return (
<div className="mt-4 flex justify-center h-[calc(100vh-120px)]"> <div className="mt-4 flex justify-center h-[calc(100vh-120px)]">