Devtools wiki
wiki() lists every part of the bound world’s game as an entry, grouped by kind, and returns { kinds, entries }. Each kind carries its name, which heads its entries, a description and a wiki page. Each entry carries its kind and name, an optional description, page and source file, the parent it belongs to, a system’s phase, its properties, the links it refers to, and usedBy, the entities that carry it by the key the dump, the entity tree and describe use. An entry is made of the entries whose parent it is, and its links are its other references, never its parent or one of the entries made part of it: a behaviour’s trait is under it and not linked, and KeyE, under climbUp, is linked from interact. Nobody writes an entry by hand: each kind reads a registry the engine already keeps.
An entry’s properties hold its own data as its registration holds it, in plain JSON, with a function as the string "function". A kind with nothing to say leaves them out, and they never hold a timing or a place in the step. A link carries a relation where a word says how it connects, such as changes from an item to the trait its use changes; a link with none only refers to its entry. The engine’s kinds are the following:
behaviour: each declared behaviour, with its description, page and file, and the entities that carry its trait. Its properties areprops, its trait’s fields and their defaults, andranges, the slider bounds it declares. It links to each event itshandlesnames, with the relationhandles.trait: each trait the dump names, under the behaviour whose trait it is, or the event it is, and the entities that carry it. Its properties are its fields and their defaults; a trait whose whole value one function makes, such astransform’s vector, has none.stat: each stat that sets a trait’s field, under the first such trait and linked to the rest, and the entities whose stats hold it. Its properties hold itsbase: the default of the field it sets on that first trait.system: each system in the world’s step, under the plugin it belongs to, with thephaseof the step it runs in, which the Wiki tab shows under its description. Each system carries the line on what it does that its plugin gave it. Each plugin is a system too, with the plugin’s description and the systems the game’s list omits, and the core and the engine’s plugins are marked as the engine’s; a system that replaces another names it. A system links to each trait its queries name, with the relationreads:core.movereadstransformandvelocity. On a world in development, attaching the devtools has each step note the traits a system’s queries name, whether it queries throughworld.queryor through the engine’s passes, so a system links to nothing until the world has stepped, and a trait it reads withgetorhasalone is not among them. A system links to no trait it writes: in koota a system writes a class trait, such astransform’s vector, in place, which no call or subscription reports, and a change seen in a step shows only the writes that step happened to make.machine: each machinestates()made, such as the engine’sround, with its states as properties, and the entities that hold it. A machine on the world adds its state and the seconds its wait has left as the propertyworld. It links to its trait, with the relationstored in.event: each eventdefineEventmade: the engine’s, such asdamaged,gaveUp,itemAccepted,taken,triggerEnteredandtriggerExited, and each of a game’s. Its properties are its record’s fields and their defaults. It links to each behaviour whosehandlesnames it, with the relationhandled by. It links to nothing that sends it: an event is raised by anemitEventcall anywhere, such as the one indealDamagefrom the Weapon’s click, the projectile system or the room’s judgement of a shot, and a game’s behaviour names no system.input: each action the player takes on the keyboard, the engine’s such asjumpand the game’s inputs, presses and page actions such assprint.sprint, with one line on what it does, and a link to each of its keys that another action binds first.key: each key the page binds, by itsKeyboardEvent.code, such asSpace, under the first action that binds it and linked to the rest:KeyEclimbs the free camera and interacts.scene: each registered scene, by name. The running scene links to the map its World lays out, with the relationlays out, since only the running scene has a World mounted.model: each registered model, with itsurl, and thesizeandboundsits manifest measured, as properties, and the entities drawn with it.avatar: each registered avatar, with its VRM body’s data as properties.map: each registered map, with the whole map as properties, linked to each registered model of a scatter kind that stands on it, with the relationscatters. A model matches a kind by its file: the example’stree-tallandtreeboth nametree-tall.glb, which the meadow’streeTallkind draws.item: each registered item, with its definition as properties, and the entities whose bag holds it, who wear it or whose loot gives it. It links to each trait its use effects change, and to each stat amodifyeffect names that the wiki lists, with the relationchanges: Potion’shealchangeshealth. It also links to the behaviours that hold it, with the relationheld by:inventory, andequipmentwhere it has anequipslot.
registerWikiKind(kind) adds a game’s own kind, such as a quest or a card, with a list(world) that finds its entries. A kind registered again under the same name replaces the first, since a hot reload runs the module again, and the return forgets it and puts back the engine’s kind of that name. Each kind:name names one entry: a repeat keeps the first and warns in the console, and a kind whose list throws lists nothing and warns, so the other kinds still show. The wiki explains structure and holds no timings of the step or the frame: the Frame tab shows those.
spawnite play wiki [query] [--kind <kind>] prints the running playtest’s wiki as JSON, and the MCP’s read_game_wiki replies with it. A query keeps the entries whose name, kind or description holds it, ignoring case, and a kind keeps that kind alone. Both add the paths of the pages that exist, each from the game’s folder: pagePath on each entry, and overviewPath beside the kinds. A game’s entry gets its own page, and an engine entry gets the engine’s page that the Wiki tab shows for it, such as node_modules/@spawnite/engine/dist/wiki/engine/behaviours/stats.md.
The overlay’s Wiki tab shows wiki() in one pane: a map at the top, which stays in view, and the page under it. The map centres on the page that shows. At its default of 2 steps, its left side holds the 2 steps of the chain up to the game nearest the centre, such as Health Behaviour then Health Trait for Max Health, and its right side holds what the centre is made of, refers to and is used by; the home’s map is made of each kind with an entry. A link with a relation goes in a group of its own, titled by the word as the data writes it with a capital first letter, one group for each word, after Refers to, and a link with none stays under Refers to: Potion’s map shows Health under Changes and Inventory under Held by. A relation’s group draws as Refers to does and follows the same tiers and folds. The links that point at the centre follow, as Obsidian shows a note’s backlinks, in a group for each word they read back as, which reverseWikiRelation in @spawnite/schema gives: Inventory’s map shows Potion under Holds, and Health’s shows it under Changed by. A link with no relation reads back as Referred to by, and a link the centre already makes back, or one from a part of it, shows once, where the centre says it. Each column of entries lists them in three tiers, by name within each: the game’s entries, then the engine’s entries the game uses, then the rest of the engine’s. The home’s kinds come in the same tiers: the kinds that hold a game entry, then the kinds that hold an engine entry in use, then the rest. Used by and Holds keep their own order. The game’s entries, and each kind that holds one, draw bright with an accent outline, one column further out too, and the engine’s draw muted. A column folds past 8 into “N more”, and each node’s parts one column further out fold past 3 into “+N”, but a fold hides only the engine’s entries and the entity rows, the last tier first: every one of the game’s is drawn. The running scene’s map holds the rows under its row in the tree, and a scene that is not running has none to show. The debugger page holds the rest of the tab’s design.
The map’s box takes at most 40% of the pane and scrolls like any other box, with the wheel, a two-finger scroll or one finger on a phone; the page under it scrolls on its own. The drawing is as wide as the box and never zooms. You move around the map by pressing a node, and it draws again only after that or an unfolded group:
- A press on a node moves into it: the node becomes the centre, the page under the map changes to its page, and the box scrolls the new centre to its middle, as far as it scrolls.
- The map reaches two steps out from the centre: the centre and its neighbours, with 1 step of the chain, then each neighbour’s own parts one column further out, 3 at most. Every ring keeps the tiers and never folds the game’s entries. There is no control to reach further: a press on a node centres it instead. An unfolded group keeps the box’s scroll.
The page under the map is built of folding sections, and a press on a section’s header folds or opens it. A section’s items sit in under its header, and a section inside another sits one step further in. The home holds Your game, a section with a section inside it for each kind, in the map’s tiers, then the overview. Each kind lists the game’s entries by name, then, muted, the entries that carry engine: true and that the game uses, by name. The game uses an engine entry when an entity carries it, or when a game entry names it in links or as its parent. The engine’s entries that nothing uses fold into one “N more from the engine” section at the end of their kind. A kind’s header shows its count and an arrow that opens the kind’s page. Every section starts open except the overview, an entry’s Page and each “N more from the engine” section, and the tab keeps which are folded while another tab shows.
A kind’s page and an entry’s page open with the same header: the name in words and what it is, with icons at the right, each named in a tooltip. A copy icon’s tooltip shows the code name, such as maxHealth, and a press copies it. A book opens the page on the wiki site, and a file opens the entry’s source in the developer’s editor, where it has one. The description sits under the header, and a scene that is not running says there that it holds nothing until it runs. A kind’s entries follow, listed as the home lists them. An entry with properties shows them next, in a Properties section, and its Page section follows. An entry with none goes straight to its Page section.
The Properties section folds like any other and takes no edit: it draws the inspector’s read-only card, which says whether the game’s code or the engine’s holds the values. Each key reads as its data writes it, such as cooldownSeconds, and each value keeps its field: a number in its box, a string in its box, true or false as a switch, and a function as the word function, muted. A nested object sits indented under its key, and each object of an array, such as each of Potion’s use.effects, sits under the array’s key as a group of its own.
A behaviour or a trait that entities carry shows a Used by section after Properties, with the count of its usedBy in the header. It lists each entity by its row in the tree, such as Player #1, or by its dump key where it has no row, with the value the dump holds under the entry’s name, in the same read-only card: Player’s Health shows current and max. The section reads one dump() as it opens and nothing while it is folded, as the inspector reads a selection once; folding it and opening it again reads the values afresh.
Godot builds its class reference from the running ClassDB, and a script’s doc comments put a game’s own classes beside the engine’s; the wiki matches it, with a game’s kinds beside the engine’s. Unity’s Inspector shows a ScriptableObject’s serialized fields, Unreal’s Details panel a Data Asset’s properties, Godot’s Inspector a Resource’s, and Roblox’s Properties panel an instance’s; an entry’s properties hold the same data, read-only, since it lives in the game’s code. Unity’s Systems window lists the systems by group, as the system kind lists them by place, and each system’s queries, the components it reads and writes, which a system declares there; the system kind links the traits a system reads from the queries it runs, since a koota system declares none. Godot’s Node dock lists a node’s signals and the methods connected to them, as the event kind links each event to the behaviours that handle it. Godot’s Input Map and Unity’s Input System list each action with the keys bound to it, as the input kind does. Cost per frame: none for the entries, which are found when read. While the devtools are attached to a world in development, each query a system runs adds its traits to that system’s set.
A game’s own pages
Section titled “A game’s own pages”A game explains how it works in markdown pages in its own folder, one for each entry at wiki/<kind>/<name>.md and an overview at wiki/index.md. The entry’s kind and name are the path, so no field in code names a page: wikiPagePath(ref) in @spawnite/schema gives it, such as wiki/item/sword.md, and wikiOverviewPath gives the overview’s. A page is markdown as the wiki site reads it, tables included, and a mermaid fence draws as a diagram in the theme of the Wiki tab’s map. The Wiki tab leaves out a page’s frontmatter, the --- block a site page opens with. The game’s agent writes each page with the MCP’s write_wiki_page tool, by the entry’s kind and name, and the overview with neither.
The engine’s Vite plugin wikiPages() serves the pages on the dev server alone, so a published build carries none of them. It answers /__spawnite/ and a page’s path with the file’s markdown, read on each request, or 204, no content, where the game has no such page. When a file under wiki/ is added, changed or removed, the page fires spawnite:wiki on its window with the file’s path. Every game’s config loads the plugin, and so does the config spawnite create writes.
The Wiki tab reads a page the first time its entry opens, and reads nothing for an entry that never opens:
- An entry with a page shows the page’s opening paragraph under its description. Read more shows the whole page in the entry’s Page section, with its file’s path under the title, and Show less at the section’s top returns to the opening. A press on the path opens the file in the developer’s editor.
- An entry without a page shows, muted, the path its page would take. A press on the path opens Send to agent with a line that asks the agent to write the page.
- A relative link from one page to another, such as
[Sword](sword.md)inwiki/item/potion.md, opens that entry in the panel. A link towiki/index.mdopens the tab’s home, a link to any other file in the game’s folder opens the file in the developer’s editor, and a path from the game’s folder written as code, such assrc/models.ts, opens as a link to that file does. On an entry’s page, a site route opens in the panel too where it names an entry’s page or a kind’s, whole or without its anchor:/engine/behaviours/inventory/#itemsopens the item kind, and/engine/behaviours/stats/#the-formulathe Stats behaviour. Any other link opens in a new tab, and any other site route opens on the wiki site. - The tab’s home shows the overview below the kinds, folded until a press, and hides it during a search.
- A saved page shows on the open entry without a reload.
- Until a page, the overview, a diagram on a page or the map arrives, its place holds a loading ring at a set height, so nothing under it moves. The ring’s frame is a
progressbarwitharia-busy, so a script driving the page can wait until none is left. A page or a diagram read before shows at once, the map shows its last drawing at once when the tab opens again, and it keeps that drawing while the next one draws. Where nothing arrives, such as a page the game lacks, the ring gives way to what that case shows.
An engine entry, one the engine registered, carries engine: true. It shows the engine’s page rather than a game page, and never a game path. A game’s registration under one of the engine’s names is the game’s. Godot builds a help page in the editor from the ## doc comments on a class and updates it on save; a game’s pages are files instead, because an overview, an item, a system and an input action have no declaration to carry a comment, and a page holds diagrams. Unity and Unreal show tooltips from a user’s attributes and comments. Cost per frame: none; a page is read when it shows.
The engine’s pages
Section titled “The engine’s pages”The engine ships the wiki site’s pages under /engine/ in its package, at dist/wiki/, so the Wiki tab shows an engine entry’s page offline and matched to the engine version the game runs. An engine entry’s page is the one at its route: its own, such as a behaviour’s wiki, or its kind’s where it has none, as a system has none. The route /engine/behaviours/stats/ names engine/behaviours/stats.md, .mdx, or the index in that folder. wikiPages() answers /__spawnite/ and the route, such as /__spawnite/engine/behaviours/stats/, with that page’s markdown, or 204 where the engine has none. In this repository, where the games run the engine’s source, it reads the wiki’s own source instead of a build.
The Wiki tab shows an engine page as it shows a game’s, with two differences:
- Where the route names a section, such as
#systems, the entry shows that section, its heading and everything under it, in place of the opening paragraph. Where no heading has the anchor, the entry shows the opening paragraph. - Read more shows the route under the title, and a press on it opens the page on the wiki site. The book in the header opens the same page there.
An engine entry that neither it nor its kind gives a route, such as a key, shows no page. A route with no page shows nothing either, and asks no agent to write one. Unity installs an offline copy of its manual matched to the editor’s version, and Godot and Unreal ship theirs with the editor; the engine ships its pages with the package for the same reason.