# Match handlers and resumable ownership

> Documentation for Redweb 0.13.3. Install that exact version when following these examples.

Give the match its own socket route, then dispatch join, move and resume by type. These canonical socket-starter handlers create and recover server-owned player sessions; they are not a room-broadcast or account-authentication example.

Use the [complete socket recipe](/docs/reference/0.13.3/recipes/socket.md) for setup, files, and tests.

```ts
import { randomUUID } from 'node:crypto';
import type { RedWebSocket } from 'redweb';
import { match } from './contract';

class Player {
    readonly session = randomUUID();
    x = 0;
    y = 0;
    constructor(readonly name: string) {}
}

function requireUnjoined(socket: RedWebSocket) {
    if (socket.context?.session) throw new Error('Already joined.');
}

function currentPlayer(socket: RedWebSocket) {
    const session = socket.context?.session as { data?: unknown } | null | undefined;
    if (!(session?.data instanceof Player)) throw new Error('Join or resume first.');
    return session.data;
}

export const Join = match.handler('join', (socket, { name }, message) => {
    requireUnjoined(socket);
    const player = new Player(name);
    if (!socket.createSession?.(player.session, player)) throw new Error('Session capacity reached.');
    return match.send(socket, 'state', player, { requestId: message.requestId });
});

export const Move = match.handler('move', (socket, { x, y }, message) => {
    const player = currentPlayer(socket);
    player.x = x;
    player.y = y;
    return match.send(socket, 'state', player, { requestId: message.requestId });
});

export const Resume = match.handler('resume', (socket, { session }, message) => {
    requireUnjoined(socket);
    if (!(socket.resumeSession?.(session) instanceof Player)) throw new Error('Session expired or unknown.');
    return match.send(socket, 'state', currentPlayer(socket), { requestId: message.requestId });
});
```

## Notes and boundaries

- Initialize the complete socket recipe: src/contract.ts defines validated payloads and src/app.tsx configures /match, session capacity and transport limits. This file is not a standalone server.
- Join issues a random bearer session token. Move requires an existing player; resume restores that player on a new connection and replaces the previous owner.
- Keep the state response and session token private. Add account authentication and application movement rules before production; sessions remain in memory and expire 30 seconds after disconnect.
- For authenticated group delivery, use the separate One identity for a page and a protected room example. The shared socket contracts guide links its complete source and the room-authorization guide.
