// ── 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, coins, shortId } from '../../lib/format.js' import api from '../../api.js' import Backpack from '../../components/Backpack.jsx' /** 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 (
) } /** 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 ({error}
)}Nothing yet. Ranks and rewards this site hands out show up here, and reach you in game on the servers they cover.
) } const waiting = [...groups, ...grants].some((entry) => entry.reach.some((server) => !server.live)) return ( <>None of this reaches the game yet — link a Steam account above and the site pushes it across on its next sync.
)} {accounts > 0 && waiting && (A server marked waiting has not confirmed it yet. One that is offline catches up when it comes back.
)} > ) } /** * RunicNPC (runicnpc stage 4, D252): your kills of each NPC profile, this wipe, * by server, across every Steam account you have linked. Nothing without a link. */ function NpcKills() { const { data, loading, error } = useAsync(() => api.playerNpcKills.list(), []) if (loading) returnNone this wipe.
} const byServer = new Map() for (const r of rows) byServer.set(r.server || r.serverId, [...(byServer.get(r.server || r.serverId) || []), r]) return (No servers yet.
return (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.
/link in chat.If this site offers Steam sign-in, linking Steam on your account page links that Steam account here too. Unlinking it there only stops you signing in with it; remove a Rust link here.
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.
No Steam account is linked to this profile yet.
)} {/* Phase 8. Rendered whether or not anything is linked: an entitlement is authored against the website account, so it exists before a Steam id does — and hiding it until one appears is the mistake the admin user page shipped in phase 7 (PLAN.md §20.5). */}Link a Steam account to see them.
}Link a Steam account to see your balance.
}Link a Steam account to see your backpack.
}