// ── The player's own Rust identity ──────────────────────────────────────── // // `/player/rust` — where a signed-in player links the Steam account they play // on. It is the one page in this module a player is asked to *do* something on, // and the thing they are doing matters more than it looks: from phase 7 the link // is what in-game permissions are granted against, and from phase 13 it is what // rewards are handed to. // // **A player route renders no layout of its own.** Core wraps `/player/*` in its // own portal chrome, so this page starts at a heading — unlike the public pages // in this module, which render `PublicLayout` themselves. // // The three-step instruction at the top is not decoration. Nothing else on the // site tells a player that the code comes from the game, and a code field with no // explanation is a code field nobody can use. import { useCallback, useState } from 'react' import { ErrorState, Loading, useAsync } from '../../core.js' import { ago, shortId } from '../../lib/format.js' import api from '../../api.js' /** The code field, and the four answers it can produce. */ function LinkForm({ onLinked }) { const [code, setCode] = useState('') const [busy, setBusy] = useState(false) const [message, setMessage] = useState('') const [error, setError] = useState('') async function submit(event) { event.preventDefault() if (!code.trim() || busy) return setBusy(true) setMessage('') setError('') try { const result = await api.playerLinks.confirm(code.trim()) setMessage( result.already ? 'That account was already linked to you.' : `Linked ${result.link.name || shortId(result.link.steamId)}.`, ) setCode('') await onLinked() } catch (err) { // Every refusal the server sends is already a sentence aimed at a player — // "run /link again", "run /unlink in game", "try again in a minute" — so // this renders it rather than replacing it with one of its own. The three // are not interchangeable, and a page that flattened them into "could not // link that code" would send a player back to the server that is down. setError(err.message || 'Could not link that code.') } finally { setBusy(false) } } return (
{message && (

{message}

)} {error && (

{error}

)}
) } /** One linked account, and the control that releases it. */ function LinkRow({ link, onRemoved }) { const [busy, setBusy] = useState(false) const [error, setError] = useState('') async function remove() { setBusy(true) setError('') try { await api.playerLinks.remove(link.steamId) await onRemoved() } catch (err) { setError(err.message || 'Could not unlink that account.') setBusy(false) } } return (
  • {link.name || shortId(link.steamId)}
    {link.steamId} · linked {ago(link.linkedAt)} {link.serverId ? ` on ${link.serverId}` : ''}
    {error && (

    {error}

    )}
  • ) } export default function Account() { // `useAsync` rather than this module's `usePolled`: nothing here changes unless // the person looking at it changes it, and a page that re-asked every twenty // seconds would be asking a question nobody is waiting on. // // **Core's `useAsync` has no `refresh`** — it re-runs when its deps change and // that is the whole of its interface — so a counter in the deps is how a page // re-reads after its own write. It blanks while it re-reads, which is right // here and is exactly what made it wrong for a poll (see `hooks/usePolled.js`). const [reloads, setReloads] = useState(0) const { data, loading, error } = useAsync(() => api.playerLinks.list(), [reloads]) const links = data ? data.links : [] const reload = useCallback(() => setReloads((n) => n + 1), []) return (
    Steam accounts

    Linking tells this site which Steam account is yours, so your play on our servers appears under your name here — and so rewards and permissions the site hands out can reach you in game.

    1. Join any of our Rust servers and type /link in chat.
    2. The server replies with a six-character code, only you can see it, and it lasts five minutes.
    3. Type it below. It works once.
    {loading && } {error && } {data && links.length > 0 && (
      {links.map((link) => ( ))}
    )} {data && links.length > 0 && (

    A link covers every server this community runs — a Steam account is one person wherever they play, while stats are kept per server and per wipe. You can also type {' '}/unlink in game to release one.

    )} {data && links.length === 0 && (

    No Steam account is linked to this profile yet.

    )}
    ) }