procPack — a family of procedural ops as content
A .procPack is a FAMILY of procedural operations authored as content: a folder whose init.luau returns a pack table. When the asset registers, every def in its ops map joins the proc op registry…
This is the one path an op family reaches the registry through. Procgen's own
families are packs — procPacks/ in this package holds one per family, and
terrain's terrain.procPack sits in its own package the same way — so the
packs shipped here are worked examples of exactly what you are writing, not a
separate mechanism the built-ins are exempt from.
MyPack.procPack/
init.luau -- returns the pack table
README.md
.metadata
The pack table
return {
name = "myPack", -- the asset's own name when omitted
description = "What this family is for.",
ops = {
["myPack.twist"] = {
description = "Twist a Geometry about an axis.",
inputs = { geometry = { type = "Geometry" } },
params = { turns = { type = "Float", default = 1.0 } },
outputs = { geometry = { type = "Geometry", default = true } },
eval = function(ctx, inputs, params) … end,
},
["myPack.bend"] = { … },
},
}
Each def is the same table every op in the registry is described by — typed
input sockets, params, typed output sockets, and an eval(ctx, inputs, params)
returning the outputs. ctx carries seed / nodeId / compositePath / hash /
checkpoint. Validation is the same for every pack, so a malformed socket or an
undeclared param type is refused here exactly as it is for the ones shipped in
this package.
procPack or procNode
Both contribute ops that a graph evaluates, and both take the same def table. They differ in how an op is addressed:
.procNode | .procPack | |
|---|---|---|
| contributes | one op | a family |
| addressed by | the asset's guid, resolved through asset.resolve(id, "procNode") | the string id the pack names it with |
| a graph writes | g:node("n", ref) | g:node("n", "myPack.twist") |
Reach for a .procPack when the ops belong together and you want to read the
family's name at every call site; reach for a .procNode when one operation
stands on its own.
Registration lifecycle
init.luau is compiled from its own source on every registration rather than
required, so an edit lands immediately and anything it pulls in uses an
absolute require (@builtin::modules.proc). Every op's version is derived
from the pack's source, so editing the file re-keys the node caches of every
graph that reaches any op in the family — those graphs recook.
Editing the pack to REMOVE an op unregisters it. A graph still naming it errors
loudly at eval rather than cooking against a definition that no longer exists,
and deleting the .procPack folder unregisters the whole family the same way.