// ── Admin · Rust · Zone presets (PLAN_REDESIGNS §3.1, D195, D210) ──────────
//
// Where a zone's ZoneManager flags and settings are chosen. Core's step editor
// has single-value fields only, so the org lead put the checkboxes here (D210):
// an admin saves a named set for one server, several, or every server, and a
// zone step's Options field offers the presets. Picking one COPIES its line into
// the step, so editing a preset here changes no published event.
//
// The flags are whatever each server's ZoneManager declared in its last hello
// (D211), grouped by `server/model/zones/zoneOptions.js`. A flag that file does
// not know is under Other, never hidden. A flag not on every server a preset
// covers is refused on save, with the server named.
import { useMemo, useState } from 'react'
import { Link } from 'react-router-dom'
import { ErrorState, Loading, useAsync } from '../../core.js'
import api from '../../api.js'
const inputStyle = {
background: 'var(--panel-flat, transparent)',
color: 'var(--text)',
border: '1px solid var(--line)',
borderRadius: 6,
padding: '6px 8px',
font: 'inherit',
}
function blankForm() {
return { id: null, name: '', flags: [], settings: {}, allServers: false, servers: [] }
}
function formFrom(p) {
const settings = {}
for (const [k, v] of Object.entries(p.settings || {})) settings[k] = String(v)
return { id: p.id, name: p.name, flags: [...p.flags], settings, allServers: p.allServers, servers: [...p.servers] }
}
/** What the page sends: blank settings left out, the rest as typed (the server checks them). */
function bodyFrom(form) {
const settings = {}
for (const [k, v] of Object.entries(form.settings)) {
if (v !== '' && v !== undefined && v !== null) settings[k] = v
}
return { name: form.name.trim(), flags: form.flags, settings, allServers: form.allServers, servers: form.allServers ? [] : form.servers }
}
/** One line on what a server said about its zone mods. */
export function zoneModsLine(s) {
const zm = s.zoneManager
const zd = s.zoneDomes
const parts = [zm ? `ZoneManager ${zm.version || ''} · ${zm.flagCount} flags`.replace(' ', ' ') : 'no ZoneManager reported']
if (!zd || !zd.loaded) parts.push('no ZoneDomes, so no domes')
else if (!zd.helper || zd.helper.state === 'missing') parts.push('ZoneDomes without RunicGatewayDomes.cs, so no domes')
else if (zd.helper.state !== 'patched') parts.push(`the domes helper could not patch ZoneDomes${zd.helper.reason ? ` (${zd.helper.reason})` : ''}`)
else parts.push('domes available')
return parts.join(' · ')
}
export default function ZonePresets() {
const [reloads, setReloads] = useState(0)
const { data, error: loadError } = useAsync(() => api.adminZones.read(), [reloads])
const [form, setForm] = useState(null)
const [busy, setBusy] = useState(false)
const [error, setError] = useState('')
if (loadError) return
Named sets of ZoneManager flags and settings. An event’s Open a zone step offers these in its Options
field; picking one copies it into the step, so changing a preset later does not change an event already
published. Who is exempt from a flag is a zonemanager.ignoreflag.* permission, set on the{' '}
permissions page.
No servers are configured yet.
} {servers.map((s) => (None yet. None ship with the module: what an arena is differs by server.
)} {(data.presets || []).map((p) => ({p.line || 'no flags or settings'}