Select a Part or Model in Studio, answer "what is this object?", fill a form —
it becomes a gatherable node, a mob or a quest giver. Closes the gap between
authoring a def and setting up a world object, which until now meant knowing to
tag a part and hand-typing PascalCase attributes in the property panel.
Engine — components can declare an attribute SCHEMA:
- src/components/Schema.luau (new): AttributeSpec/Display/ComponentSchema types,
normalize/defaults/get/list, and the schemas for Gatherable, Mob, QuestGiver.
Dependency-free ON PURPOSE: the plugin requires it live at edit time, and the
component modules themselves can't be required there (Harvesting asserts
IsServer; Remotes creates instances in ReplicatedStorage).
- Components.define now accepts EITHER the legacy `attr = default` map or a
schema array, normalizing both to one ordered spec list; bind() reads the
derived default map, so binding is byte-identical. Legacy maps are sorted, as
`pairs` order is arbitrary and would make a UI jitter. New getSchema/
listSchemas. The three shipped components pull name/tag/display/attributes
from the schema; their onSetup bodies are untouched (defaults verified
identical, all 23 attributes).
Plugin — the Build page:
- Field.luau (new): coerce/format/equalsDefault, lifted from ConfigAdmin (which
now delegates), shared by every schema-driven editor.
- FieldRow.luau (new): the shared [○/●] label … control + help row, including a
⌄ picker that cycles authored ids for fields declaring `ref`.
- BuildAdmin.luau (new): live schema read with three distinct empty states,
selection/eligibility/identify, deltas-only attribute writes, applyType
(tag + clear any other component) and clear.
- BuildAdminUi.luau (new): chooser cards, grouped form, multi-select apply,
stale-bind-marker warning, SelectionChanged-driven refresh.
- init.server.luau: record()-wrapped buildActions + the page.
Docs: docs/admin-plugin.md Build section + a 60-second walkthrough,
docs/extending.md schema guide, a CONTRIBUTING rule that new creator components
declare one, CHANGELOG.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Engine: quest overrides with objective*/reward* fields on nested code quests
now WARN at boot (they merge nothing — QuestData prefers nested tables);
EngineConfig + ConfigAdmin reject non-finite numbers (inf/nan).
Plugin: Config group panels auto-size (fixed-height math clipped the last row
of big groups — Theme group lost its Bold font row); Stats panels get the gap
math right; the Engine Config explainer page now REBUILDS the window when the
engine appears (the old hint was impossible — pages were assembled once at
plugin load); create() refuses ids that already have an override (mirror
guard); failed Create/Override reasons render under the create row; rejected
config edits report in the footer; explicit navigation cancels a pending
debounced search jump; the mount-time page restore no longer clobbers the
saved last-page setting during an engine-less session; undo/redo refresh
skips while typing in one of the plugin's own text boxes; search results past
the 50-cap no longer render empty category headers.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>