Two fixed nodes, plus one node per race and per class - all of them defaulting to open, so the nodes only matter once you want to restrict something.
Any handler NeoForge can name
Permissions go through NeoForge's own PermissionAPI, so any handler
built on it works.
SableCraft Standards ships one,
and LuckPerms is the other common choice. The active handler is an explicit server setting,
not a contest:
# neoforge-server.toml
permissionHandler = "standards:permissions"
With no handler chosen, nobody can be granted
anything. NeoForge's default answers every question with the node's own default,
so legendquest.party.spy becomes ungrantable and restricted races
stay restricted for everyone. /lq admin still works, because it
falls back to the vanilla op level.
Fixed nodes
| Node | Default | What it allows |
|---|---|---|
| legendquest.admin | deny (op level 2 also passes) | The whole /lq admin tree: setrace, setclass, addxp, setkarma, level. |
| legendquest.party.spy | deny, including for operators | Read other players' party chat. Holding it is not enough - the listener must also switch themselves on with /lq party spy on. |
legendquest.party.spy is deliberately not granted by op level.
Listening in on private conversations is something a server owner grants on purpose, not
something that should arrive attached to an op level. It is also rechecked on every
message, so revoking it silences a listener immediately rather than at their next login.
Race and class nodes
Controls whether a player may select it:
legendquest.race.<race>
legendquest.class.<class>
Content from any namespace other than legendquest (which is every genre
pack) folds the namespace in:
legendquest.race.<namespace>.<race>
legendquest.class.<namespace>.<class>
These default to allow. A race or class is
selectable by everyone unless its data file declares a perm field, which flips it to deny-until-granted. Nothing in the shipped content declares
one, so out of the box every entry below is open and the nodes only matter if you want
to restrict something.
The nodes gate player self-selection only -
/lq race choose, /lq class choose, and the
character GUI, which greys out what the player may not take.
/lq admin setrace and /lq admin setclass do
not consult them, so an admin can always assign a restricted race.
Namespace legendquest - ships in the jar. 10 races, 9 classes.
legendquest.race.dwarf
legendquest.race.elf
legendquest.race.gnome
legendquest.race.half_elf
legendquest.race.half_orc
legendquest.race.hobbit
legendquest.race.human
legendquest.race.orc
legendquest.race.tiefling
legendquest.race.undecided
legendquest.class.barbarian
legendquest.class.bard
legendquest.class.citizen
legendquest.class.cleric
legendquest.class.fighter
legendquest.class.mage
legendquest.class.ranger
legendquest.class.rogue
legendquest.class.warlord
Namespace lq_apoc - legendquest-apocalypse.zip. 8 races, 20 classes.
legendquest.race.lq_apoc.athlete
legendquest.race.lq_apoc.ex_military
legendquest.race.lq_apoc.immune
legendquest.race.lq_apoc.mechanic
legendquest.race.lq_apoc.nobody
legendquest.race.lq_apoc.paramedic
legendquest.race.lq_apoc.prepper
legendquest.race.lq_apoc.street_kid
legendquest.class.lq_apoc.builder
legendquest.class.lq_apoc.chemist
legendquest.class.lq_apoc.combat_medic
legendquest.class.lq_apoc.doc
legendquest.class.lq_apoc.drifter
legendquest.class.lq_apoc.enforcer
legendquest.class.lq_apoc.infiltrator
legendquest.class.lq_apoc.labourer
legendquest.class.lq_apoc.mercenary
legendquest.class.lq_apoc.miner
legendquest.class.lq_apoc.pathfinder
legendquest.class.lq_apoc.plague_caller
legendquest.class.lq_apoc.pyro
legendquest.class.lq_apoc.quartermaster
legendquest.class.lq_apoc.rancher
legendquest.class.lq_apoc.scavenger
legendquest.class.lq_apoc.scout
legendquest.class.lq_apoc.sharpshooter
legendquest.class.lq_apoc.trader
legendquest.class.lq_apoc.veteran
Namespace lq_scifi - legendquest-scifi.zip. 8 races, 16 classes.
legendquest.race.lq_scifi.belter
legendquest.race.lq_scifi.clone
legendquest.race.lq_scifi.mutant
legendquest.race.lq_scifi.synth
legendquest.race.lq_scifi.terran
legendquest.race.lq_scifi.unregistered
legendquest.race.lq_scifi.uplift
legendquest.race.lq_scifi.vex
legendquest.class.lq_scifi.captain
legendquest.class.lq_scifi.commando
legendquest.class.lq_scifi.crewman
legendquest.class.lq_scifi.engineer
legendquest.class.lq_scifi.juggernaut
legendquest.class.lq_scifi.marine
legendquest.class.lq_scifi.medtech
legendquest.class.lq_scifi.pathjumper
legendquest.class.lq_scifi.psion
legendquest.class.lq_scifi.railgunner
legendquest.class.lq_scifi.recon
legendquest.class.lq_scifi.roboticist
legendquest.class.lq_scifi.scientist
legendquest.class.lq_scifi.surgeon
legendquest.class.lq_scifi.technician
legendquest.class.lq_scifi.xenobiologist
The perm field in race/class data
A race or class JSON may carry an optional perm field:
{ "name": "Tiefling", "perm": "anything" }
The value is ignored - it is a flag meaning this entry is restricted, and the node
checked is always the generated legendquest.race.<id>. It does
not name a custom node, which is what the field did
in the original Bukkit plugin and is the easy assumption to make.
Worked examples
A grant is a node plus an optional state, and the state defaults to true:
/rank user Sable set legendquest.party.spy
/rank group moderator set legendquest.admin
/rank group donor set legendquest.class.lq_apoc.plague_caller
/rank group storyteller set legendquest.race.*
/rank group storyteller set legendquest.race.tiefling false
/rank user takes a plain name rather than a selector, so granting to
somebody who is offline works - which is most of what this gets used for.
Tier beats specificity. The order is the player's own grants, then the groups they are in, then parent groups, then the default group - and the first tier with any opinion wins outright. A wildcard grant on a group the player is in beats an exact deny sitting in the default group. Specificity never gets consulted, because the nearer tier already answered.
Within one tier, the most specific pattern wins -
exact beats legendquest.race.* beats
legendquest.* beats *. That is why the two
storyteller lines above work as a pair: to carve one race out of a wildcard, the exact
deny has to sit in the same place as the wildcard. If two patterns in one tier match
equally well and disagree, the deny wins.
prefix.* covers the prefix node
itself, not just its children, so legendquest.race.* also grants bare legendquest.race.
When nothing anywhere has an opinion, the node's own default applies - the column in the tables above. Switching Standards on therefore changes nothing on its own: a server that has granted nothing behaves exactly as it did before.
Gotchas
Nodes are gathered once per server start. A
race or class added by /reload has no node until the next
restart, and an entry with no node is treated as open. Restart after adding restricted
content.
Wildcards are the handler's feature, not ours. The mod only ever asks whether one specific node is held; the wildcard expansion is done by whichever handler you run. Both Standards and LuckPerms do it.
A genre pack does not remove the built-in nodes. Both sets are registered if both are loaded, so grants are namespaced and never collide.
Command reference
| Command | Requirement |
|---|---|
| /lq admin ... | legendquest.admin or op level 2 |
| /lq party spy on | legendquest.party.spy |
| /lq race choose <race> | legendquest.race.<race> |
| /lq class choose <class> | legendquest.class.<class> |
| /lq class sub <class> | legendquest.class.<class> |