📌 Game Pinning Architecture
A comprehensive cheat-sheet on how Funday’s persistent “Game Tabs” and global mounting architecture handles both iFrames and Native Svelte components to provide a glitch-free, persistent, zero-duplication experience.
✨ Core Principles
🎮 Unified Viewport Pool
All games—whether nested completely in an iFrame or imported dynamically as a Native Svelte chunk—are mounted globally in the root layout’s ViewportPool. This ensures zero unmounting upon navigation.
🚀 Ephemeral GameViewport
The <GameViewport> component is not responsible for loading or mounting any games anymore! It functions strictly as a message router and a skeletal CSS boundary. The actual game overlaps it from the overarching ViewportPool.
🏛️ The Stack & Flow
1. openGamesStore.svelte.ts (The Source of Truth)
Holds the canonical state for all active and pinned games.
poolGames(derived): Returns all active games that the global pool needs to keep alive in the background (native & iframe combined).- Pinning: Flips the
isPinnedflag safely. No more arbitrary exclusions againstsvelte-component.
2. ViewportPool.svelte (The Global Host)
Lives at the root layout level, rendering elements statically in absolute/pointer-events-none mode so they hover globally over the app.
- IFrames:
<iframe src="...">bounds are preserved natively. - Native Svelte: Spans the
<NativeGameHost>internally via an{#if}block matching theintegrationType.
3. NativeGameHost.svelte (Native Session Wrapper)
Provides the lifecycle container for Native games like Battleships.
- Imports the chunk from
game.entryPoint. - Ingests the user session and handles deep prop-drilling into the dynamically loaded chunk.
- Directly subscribes to
nativeMessageBusand publishes tohostMessageBus.
4. nativeBridge.ts (The Decoupler)
Eliminated local variables bindings!
nativeMessageBus: Platform -> Native GamehostMessageBus: Native Game -> Platform Allows ephemeralGameViewports to drop in/out of the DOM but instantly reconnect their inputs/outputs to the globally pinned native components.
🛠️ Communication Bridges
IFrames:
Utilize the exact same postMessage Bridge (createHostBridge). The listener connects dynamically when the ephemeral GameViewport appears inside the page route by targeting the pre-existing iframe in the pool.
Native Mounts:
Utilize nativeMessageBus to push actions from the Platform (like chat messages or vote executions) to the persistently mounted NativeGameHost, which then trickles the update into the isolated game component.
🔧 Extending the Architecture
If you want to add a new integration type or heavily rely on a local engine (e.g. Phaser purely embedded natively):
- Never manually mount it in
GameViewport. - Add a conditional block for the new integration inside
ViewportPool. - Scaffold a wrapper similar to
NativeGameHostto control the specific engine’s memory cleanup on full termination. - Bridge the input/outputs to the global native bus logic.