Workloads
Multiplayer
An actor holds a room's shared state and broadcasts events to every connected client.
Prefer to read code? Clone the example repository. View on GitHub
Example generated code
import { Hono } from "hono";
import { actor, event, setup } from "rivetkit";
type Position = { x: number; y: number };
// One actor per room. State is shared by every connected client.
const room = actor({
state: { players: {} as Record<string, Position> },
events: { changed: event() },
actions: {
join(c, player: string) {
c.state.players[player] ??= { x: 0, y: 0 };
c.broadcast("changed", c.state.players);
return c.state.players;
},
move(c, player: string, x: number, y: number) {
c.state.players[player] = { x, y };
c.broadcast("changed", c.state.players);
return c.state.players;
},
},
});
export const registry = setup({ use: { room } });
const app = new Hono();
app.all("/api/rivet/*", (c) => registry.handler(c.req.raw));
app.get("/", (c) =>
c.json({ message: "Use the RivetKit client to join a room." }),
);
export default app;
Deploy and connect
Deploy the app, then connect to its actors from your own system:
const client = createClient<typeof registry>({
endpoint: deployment.endpoint,
namespace: deployment.namespace,
poolName: deployment.pool,
token: deployment.token,
});
// Open a realtime connection and receive every broadcast from the room.
const room = client.room.getOrCreate(["lobby"]).connect();
room.on("changed", (players) => console.log("players", players));
await room.join("alice");
await room.move("alice", 4, 8);
await room.dispose();
Dynamic Apps does not wrap RivetKit’s action, event, or connection APIs. See Events and Connections in Rivet Actors.