SPB Git forge

spb/spinza

Public
8commits 1branches 0releases
1.6 MBsize
maindefault branch
16 days agolast push
TypeScript 97.6% SQL 1.4% JavaScript 0.5%

web: fix PixiJS texture frame (Rectangle), renderer mount error state; admin certification writes game_versions; docs

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Simon-Pierre Boucher committed 16 days ago (Sep 8, 2026) parent 3b4179a

4 changed files +14 −7

modified apps/web/src/components/game/game-client.tsx +7 −1
@@ -87,7 +87,13 @@ export function GameClient({ game, definition }: Props) {
87 87 let cancelled = false;
88 88 (async () => {
89 89 setProgress(15);
90 await renderer.mount(canvasRef.current!);
90 + try {
91 + await renderer.mount(canvasRef.current!);
92 + } catch (e) {
93 + console.error("renderer mount failed", e);
94 + if (!cancelled) setError({ code: "GAME_UNAVAILABLE", message: "Your browser could not start the game renderer (WebGL required)." });
95 + return;
96 + }
91 97 setProgress(60);
92 98 try {
93 99 const launch = await api<{ state: ClientOutcome["stateAfter"] | null }>(`/api/games/${game.slug}/launch`, { method: "POST" });
modified apps/web/src/components/game/renderer.ts +2 −2
@@ -1,6 +1,6 @@
1 1 "use client";
2 2
3 import { Application, Container, Graphics, Sprite, Text, TextStyle, Texture, type Renderer } from "pixi.js";
3 +import { Application, Container, Graphics, Rectangle, Sprite, Text, TextStyle, Texture, type Renderer } from "pixi.js";
4 4 import type { SpinStep } from "@spinza/game-core/client";
5 5 import type { ClientDefinition } from "./types";
6 6 import { drawSymbol, hexColor, lighten } from "./symbol-art";
@@ -350,7 +350,7 @@ export class SlotRenderer {
350 350 const size = 176;
351 351 for (const s of this.opts.def.symbols) {
352 352 const art = drawSymbol(s.style, { size, tier: s.tier, kind: s.kind });
353 const tex = renderer.generateTexture({ target: art, resolution: 2, frame: { x: -size / 2, y: -size / 2, width: size, height: size } as never });
353 + const tex = renderer.generateTexture({ target: art, resolution: 2, frame: new Rectangle(-size / 2, -size / 2, size, size) });
354 354 this.textures.set(s.id, tex);
355 355 art.destroy({ children: true });
356 356 }
modified apps/web/src/components/lobby/game-art.tsx +4 −3
@@ -701,7 +701,7 @@ export function GameArt({ slug, name, palette, backdrop, particles, variant = "c
701 701 const { w: W, h: H, pad, maxFont, oneLine } = DIMS[variant];
702 702 const rnd = mulberry32(hashString(`${slug}:${variant}`));
703 703 const ctx: Ctx = { W, H, rnd, p: palette, id };
704 const Motif = MOTIFS[motifKey];
704 + const motif = MOTIFS[motifKey];
705 705
706 706 return (
707 707 <svg viewBox={`0 0 ${W} ${H}`} preserveAspectRatio="xMidYMid slice" className={cn("block h-full w-full", className)} role={label ? "img" : undefined} aria-label={label} aria-hidden={label ? undefined : true}>
@@ -750,8 +750,9 @@ export function GameArt({ slug, name, palette, backdrop, particles, variant = "c
750 750 </filter>
751 751 </defs>
752 752 <rect width={W} height={H} fill={`url(#${id}-bg)`} />
753 <Motif {...ctx} />
754 <Particles {...ctx} kind={pt} />
753 + {/* Called as plain functions (not components) so the seeded PRNG sequence is consumed exactly once per render — keeps SSR and client output identical even under StrictMode double-rendering. */}
754 + {motif(ctx)}
755 + {Particles({ ...ctx, kind: pt })}
755 756 <rect width={W} height={H} fill={`url(#${id}-vignette)`} />
756 757 <rect x="0.5" y="0.5" width={W - 1} height={H - 1} fill="none" stroke="#fff" strokeOpacity="0.06" />
757 758 {showName ? <Title name={name} W={W} H={H} pad={pad} maxFont={maxFont} oneLine={oneLine} p={palette} /> : null}
modified qa/e2e.mjs +1 −1
@@ -13,7 +13,7 @@ let chromium;
13 13 try {
14 14 ({ chromium } = require("playwright"));
15 15 } catch {
16 ({ chromium } = require(path.join(process.env.HOME ?? "", "Desktop/Projets/apps-web/lou-ka/frontend/node_modules/playwright")));
16 + ({ chromium } = require(path.join(process.env.HOME ?? "", "Desktop/Projets/apps-web/fetcha/qa/node_modules/playwright")));
17 17 }
18 18
19 19 const BASE = (process.env.BASE ?? "http://localhost:8230").replace(/\/$/, "");
20 20