MultiplayerBridge

<MultiplayerBridge> bridges the multiplayer context from the parent React tree into the R3F Canvas. This is required when <MultiplayerProvider> is placed outside the <Game> component.

tsx
import { MultiplayerBridge } from "@carverjs/multiplayer";

Why It's Needed #

CarverJS <Game> renders into an R3F <Canvas>, which uses a separate React reconciler. React contexts from the parent tree — including the MultiplayerContext created by <MultiplayerProvider> — are not automatically available inside the Canvas.

Without <MultiplayerBridge>, hooks like useNetworkEvents, useMultiplayer, and useNetworkState will throw an error when called inside the Canvas because they cannot find the MultiplayerContext.


When to Use #

Use <MultiplayerBridge> when your <MultiplayerProvider> is outside the <Game> component. This is the recommended pattern for games that have lobby/room screens (HTML UI) that need access to multiplayer hooks like useRoom, usePlayers, and useHost before the Canvas is rendered.

You do NOT need <MultiplayerBridge> if your <MultiplayerProvider> is placed inside <Game> (directly in the R3F tree). In that case, the context is already available.


Usage #

tsx
import { Game, World, Actor } from "@carverjs/core/components";
import { useGameLoop, useInput } from "@carverjs/core/hooks";
import {
  MultiplayerProvider,
  MultiplayerBridge,
  useRoom,
  usePlayers,
  useNetworkEvents,
} from "@carverjs/multiplayer";

function App() {
  return (
    // MultiplayerProvider is OUTSIDE Game — lobby hooks work here
    <MultiplayerProvider appId="my-game">
      <GameApp />
    </MultiplayerProvider>
  );
}

function GameApp() {
  // These hooks work because we're inside MultiplayerProvider (parent React tree)
  const room = useRoom("my-room");
  const { players } = usePlayers();

  if (room.connectionState !== "connected") {
    return <div>Connecting...</div>;
  }

  return (
    <Game mode="2d">
      {/* MultiplayerBridge re-provides the context inside the R3F Canvas */}
      <MultiplayerBridge>
        <World>
          <GameScene players={players} />
        </World>
      </MultiplayerBridge>
    </Game>
  );
}

function GameScene({ players }) {
  // These hooks work because MultiplayerBridge bridges the context into the Canvas
  const { broadcast, onEvent } = useNetworkEvents();

  useGameLoop((delta) => {
    // Game logic using multiplayer events
  });

  return (
    <>
      {players.map((p) => (
        <Actor key={p.peerId} type="primitive" shape="circle" color="red" />
      ))}
    </>
  );
}

Props #

PropTypeDescription
childrenReactNodeScene content to render inside the bridged context

How It Works #

<MultiplayerBridge> reads the MultiplayerContext value from the parent React tree using useContext, then re-provides that same value via a new <MultiplayerContext.Provider> inside the R3F tree. This is a lightweight operation — no data is copied or transformed.

text
Parent React Tree          R3F Canvas Tree
─────────────────          ────────────────────────
MultiplayerProvider        MultiplayerBridge
  └─ context value ──────►   └─ re-provides same value
       │                          │
    useRoom (works)            useNetworkEvents (works)
    usePlayers (works)         useMultiplayer (works)

Common Patterns #

tsx
<MultiplayerProvider appId="my-game">
  {phase === "lobby" && <LobbyScreen />}     {/* HTML - useRoom, usePlayers */}
  {phase === "game" && (
    <Game mode="2d">
      <MultiplayerBridge>                     {/* Bridges context into Canvas */}
        <World>
          <GameScene />                       {/* useNetworkEvents, useMultiplayer */}
        </World>
      </MultiplayerBridge>
    </Game>
  )}
</MultiplayerProvider>

Pattern 2: Everything Inside Canvas (No Bridge Needed) #

tsx
<Game mode="2d">
  <MultiplayerProvider appId="my-game">          {/* Already inside Canvas */}
    <World>
      <GameScene />                           {/* Hooks work directly */}
    </World>
  </MultiplayerProvider>
</Game>