Take a 3D model from Blender to the Paralives Build Mode catalogue, in two clicks.
ParaForge is a Blender add-on for Paralives.
It checks a mesh against every rule the game imposes, fixes what can be fixed,
rebuilds the textures into the maps the game actually reads, writes the FBX and
its PNGs straight into a .mod folder, and then declares the item in the Build
Mode catalogue.
The game never has to be running, and no file inside the installation is ever touched.
Interface in French by default, English on request, switchable from the panel header.
Blender 4.2 or newer. Developed and tested against Blender 5.2 LTS.
Tip
Complementary Modding Tool: Pair ParaForge with ParaReload, an in-game BepInEx plugin for Paralives that hot-reloads modified 3D models, textures, and settings live without restarting the game. Save in Blender with ParaForge, and see your asset update instantly in-game!
Tip
Exporting something detailed? ParaLOD makes high poly assets usable in Paralives. The game gives the physics collider the same mesh as the renderer, so a 393,214 triangle model also carries a 393,214 triangle collider. Measured at 6 frames per second, against 380 once the collider is simplified. ParaLOD does it automatically and leaves the rendered mesh untouched.
Note
Extracting from The Sims: sims-package2glb reads The Sims 2, 3 and 4 .package mods and writes the models and textures out as glTF.
ParaForge panel in Blender 5.2 showing automated checklist validation, asset rules, and export tools.
Exported custom 3D asset placed live in Paralives Build Mode alongside hot-reloading console commands.
- Screenshots
- Why this exists
- Install
- Use it
- What the checklist verifies
- Colour zones, three ways in
- Textures, and the naming that configures them
- A downloaded GLB, converted on its own
- Writing the item without the Control Panel
- What the wiki does not say, and the game does
- Clearing out a mod folder
- Undo, and safety
- Calibration
- Limits
- Build from source
- Tests
- Project layout
- Credits and licence
Paralives ships a real modding toolkit inside the game, and it works. What it
does not do is stop you from getting the mesh wrong. The official path is:
export from Blender, drop the files in a mod folder, restart the game, open the
Control Panel, create an Item, create a Prefab, add an ItemMeshReference, pick
the mesh, fit the bounding box, assign a surface, tag it, pick a swatch group,
then find out whether the orientation, the scale, the origin and the texture
roles were right.
Every mistake costs a full game restart, because the game imports its assets at launch and has no hot reload.
ParaForge moves the whole verification into Blender, where a mistake costs nothing, and then writes the catalogue entry itself so the Control Panel pass disappears.
Download paraforge-x.y.z.zip from the Releases page,
then in Blender:
Edit > Preferences > Get Extensions > Install from Disk, and pick the zip.
The panel appears in the 3D viewport sidebar, tab ParaForge. Press N to
open the sidebar.
- Pick the target mod, or create one with the
+next to the selector. Mods live in%USERPROFILE%\AppData\LocalLow\Paralives\Paralives\. AvoidLocal.mod: it is the game's own scratch folder, fine for a quick try, but it cannot be published to the Workshop. - Select the mesh. The checklist appears in the panel and as an overlay in the viewport.
- Fix everything safe sorts out unit scale, transforms, origin, the colour attribute and the texture roles.
- Check the orientation against the green arrow, then confirm. This is the one thing no tool can work out for you.
- 1. Export to Paralives writes the mesh and the textures.
- 2. Create the item in the catalogue declares the item.
Both steps are needed. Without the second, the files sit in the mod and nothing appears in Build Mode.
Then restart Paralives. There is no hot reload.
Each line is green, orange or red, with the reason, and a button that fixes it where a fix exists.
| Check | What it means |
|---|---|
| Scene unit scale | Paralives works in metres at scale 1.0 |
| Transforms applied | Rotation and scale baked, or the item arrives wrong |
| Origin placement | Per item type, see below |
| Bounding size | Sanity check against the tile size |
| Faces Y+ | Manual, compared with the arrow drawn in the viewport |
| Colour zones | Legal zone colours only, four maximum |
| UV map | Present, and UV2 reported when it is there |
| Texture coordinates | A Mapping node is baked into the exported UVs, since an FBX cannot carry one |
| Triangle count | Against your own budget, with a decimate button |
| N-gons | They triangulate on export and can shade badly |
| Texture naming | Every image classified into a role the game knows |
| Texture size | Nothing in the game is above 2K |
| Seat height | For items a Para sits on, against the heights the game's own furniture uses |
| Which way round | The backrest on the side the game's 43 chairs put theirs, with a half turn to fix it |
| Target mod folder | Exists, ends in .mod, and is not inside the game |
Origin rules, from the wiki:
| Item type | Rule |
|---|---|
| Floor item | Centred in X and Y, base at Z=0 |
| Wall item | Centred in X and Z, back at Y=0 |
| Window or door | Centred on all three axes |
Paralives reads up to four recolourable zones from vertex colours, plus yellow for decals. Painting them by hand is slow, and an asset that arrives from a marketplace or a generator has none at all.
- Select faces, click a zone. The obvious one, with coloured buttons.
- One material per zone. Most imported assets already split that way, so the slots map straight onto zones.
- Pick a colour off the model, then grow it by tolerance. This is the one that rescues an asset whose only texture is a single bake. The picker samples the texture through a raycast rather than reading screen pixels, so viewport lighting never shifts the result, and the tolerance slider is live in the redo panel.
The legal colours are exact, and nothing else is read by the game:
| Zone | Colour |
|---|---|
| 0 | white 1, 1, 1 |
| 1 | red 1, 0, 0 |
| 2 | green 0, 1, 0 |
| 3 | blue 0, 0, 1 |
| Decal | yellow 1, 1, 0, never recolourable |
The suffix on the file name is what makes the game assign the right import settings. Getting it right removes a manual configuration pass per texture, which is the single largest saving in the whole pipeline.
| Suffix | Content | Note |
|---|---|---|
GrayMask |
Recolourable base, 50 percent gray is the neutral tone | sRGB |
Detail |
Free colour, not recolourable | one per item |
NormalOcclusion |
Normal map in RGB, occlusion in the alpha | data |
Smoothness |
White is glossy, black is matte | data |
ColorZone |
Zone map, for meshes that cannot carry vertex colours | data |
Master |
Walls and floors: R GrayMask, G variant, B hue shift | sRGB |
ParaForge also writes the .meta sidecar next to each asset, carrying its GUID
and its import flags, which makes the import deterministic instead of depending
on the game parsing the file name.
A model taken off the web never arrives in the right shape. glTF gives you
roughness where the game wants smoothness, packs occlusion into the red
channel of an ORM texture, and gltfpack strips image names so Blender calls
them Image_0, Image_1, Image_2.
ParaForge identifies each image from three sources of evidence, most reliable first:
- the shader graph, meaning what the image is actually wired to. That is a fact rather than a guess, and it is the only clue left when the names are gone;
- the file name:
_Diffuse,-ORM,_Normal,_BaseColor; - the pixels: a normal map is recognisable, and a texture with no colour is never an albedo.
Then it rebuilds:
baseColor -> Detail (exact copy) or GrayMask (desaturated, recentred on 50%)
normal + occlusion -> NormalOcclusion (RGB + alpha)
roughness -> Smoothness (1 - roughness)
metallic -> no channel, folded into smoothness
emissive -> no channel, folded back into the colour
Nothing is invented. An image that nothing identifies stays marked unknown and is not written, rather than putting the wrong map into someone's mod.
Paralives gives one surface per mesh. An asset split across five materials therefore does not import as it is. Merge into one surface repacks the UVs of the whole selection into an atlas, bakes every channel, and replaces the materials with one. The look is preserved, the UVs are not.
A Paralives item is three pieces of plain text, all inside your own mod:
<Mod>/<Name>.prefab the object tree, its size, its mesh
<Mod>/Settings/Items.setting the catalogue entry and its tag
<Mod>/Settings/Translations.setting the label the player reads
A mod carries only what it adds and the game merges everything: the game's own
French translation mod contains nothing but a Translations.setting. That is
why no game file is ever modified.
Merging is done on the text rather than by rebuilding the file from a parsed
model. An Items.setting written by the game may hold fields this add-on has
never heard of, and re-serialising it would drop them in silence.
A placed item can carry either of the game's two resize widgets, both, or
neither. The game keeps them apart itself, in CancelResizeOrScaleItem, and
133 of its prefabs declare both. An item a Para sits on declares neither: see
A chair carries no resize handle.
Scaling multiplies the whole item at once. It needs IsScalable on the
root, and the bounds are factors:
ItemObjectRoot:
IsScalable:True
ScalableAxes:bool3(True, True, True)
HasMinScale:True
MinScale:0.1
HasMaxScale:True
MaxScale:10
Stretching pulls the item along chosen axes to real dimensions, so a shelf can be made wider without becoming taller. It takes two statements, because the mesh has to be told which of its own axes follow the item's cube:
ItemObjectRoot:
IsResizable:True
ResizableAxes:bool3(True, False, True)
MinSizes:(0.2000, 0.1000, 0.0500)
HasMaxSize:True
MaxSizes:(20.0000, 10.0000, 5.0000)
ItemMeshReference:
IsResizable:bool3(True, False, True)
The sizes are metres in the same order as Size, so ParaForge derives them
from the item's own measurements rather than a number out of the air.
HasMaxScale and HasMaxSize are what the clamp actually reads, and there is
no HasMinSize anywhere in the assembly: the floor always applies, the ceiling
only when declared. 139 shipped prefabs write MaxSizes while only 47 declare
HasMaxSize, so 92 of them carry a ceiling the game never applies.
The ranges ParaForge writes are wider than the game's own. Across the 185
prefabs that set them, MinScale runs 0.25 to 1.75 and MaxScale 0.5 to 10,
because each is authored for a purpose. A mod item is handed to someone who
wants it tiny on a shelf and huge in the garden, so the ceiling is the game's
own maximum and the floor goes below anything it ships.
ItemCanBeStackedOn is what lets a Para set something down on an item.
Counted over the game's items by family:
| Family | Items | Declaring it |
|---|---|---|
| Couches | 13 | 100% |
| Benches | 11 | 100% |
| Toilets | 6 | 100% |
| Tables | 26 | 100% |
| Counters | 79 | 100% |
| Chairs | 48 | 0% |
So it is not about having a flat top, since a chair has one. It is about whether that top is meant to be used. Without it the item looks right and refuses everything a Para tries to put down.
In the order the game writes bool3, width, height and depth, the middle flag
is height and it is false almost everywhere: all 13 couches and all 11 benches
are True,False,False, and 12 of 26 tables are True,False,True. Stretching
an item vertically moves everything anchored to it, which on anything a Para
uses means moving the place they stand or sit.
Joining the game's whole catalogue to its prefabs by GUID and grouping every item by the template its tag or its own override gives it:
| Template | Items | Resizable |
|---|---|---|
ChairSlotAndLocator |
18 | 1 |
ShorterChairSlotAndLocator |
9 | 0 |
LongChairSlotAndLocator |
8 | 0 |
ArmchairSlotAndLocator |
9 | 0 |
ShorterArmchairSlotAndLocators |
2 | 0 |
LowerArmchairSlotAndLocators |
2 | 0 |
ToiletSlotAndLocators and two variants |
6 | 0 |
| Chairs, armchairs and toilets | 54 | 1 |
CouchesSlotAndLocators and five variants |
13 | 13 |
BenchSlotsAndLocators |
10 | 10 |
CounterSlotsAndLocators and two variants |
79 | 78 |
TableSlots |
22 | 14 |
So it is not sitting that the handle breaks. A couch and a bench carry a row of seats and survive being stretched; a chair carries one, and moving it puts it where the Para cannot path to. The item appears in the catalogue, renders correctly, accepts the sit command, and the Para walks to a different chair. Nothing is logged, because as far as the game is concerned nothing failed.
Confirmed on two custom items whose catalogue entries are identical down to the
tag and the slot GUID, differing only in the prefab: the one declaring no
widget is sat on, the one declaring IsResizable is walked past.
ParaForge drops both handles from chairs, armchairs and toilets, whatever is ticked in the panel, and greys them out with the reason. Couches, benches, beds, tables and counters keep theirs.
Nothing in a mesh tells the game where to sit, and nothing fixes the height
either. Every slot template carries VaryBasedOnHeight:True on its seat
locator, with Min and Max children bounding the travel, so the game moves
the Para to suit the item rather than demanding a height of it. That is why a
stool and a dining chair both work through the same ChairSlotAndLocator.
What the game cannot do is invent a surface. A mesh whose only flat top is at 1.2 m still seats a Para, who then floats, and nothing is logged because nothing failed.
So the seat is measured: the largest single horizontal surface the item has, found by binning upward facing triangles by height and taking the heaviest bin by area. Not a fraction of the item, and not an average, which would mix a chair's seat with its armrests and its backrest top. Run over the game's own furniture:
| Family | Meshes | Seat height | Share of the item's height |
|---|---|---|---|
| Chairs | 28 | 0.448 m | 49% |
| Office chairs | 5 | 0.449 m | 43% |
| Benches | 12 | 0.450 m | 86% |
| Ottomans | 7 | 0.449 m | 100% |
| Stools | 9 | 0.649 m | 99% |
Two heights, then: 0.45 for anything you sit on with your feet down, 0.65 for a stool. The ratios are why a single band cannot exist, since a dining chair's seat is halfway up it and an ottoman's seat is its lid. Couches and beds are assembled from several meshes, cushions apart from frames, so one file measured alone means nothing and they are left out.
ParaForge measures your mesh the same way, names which of the two it matches, and draws both heights in the viewport. It only warns outside 0.20 to 0.75, where no shipped item sits, and never blocks.
Measured rather than reasoned, because reasoning it out of the template's locator coordinates got the answer backwards once. The game's own chairs, imported into Blender, asked where the geometry in the top third of each one sits against the middle of the item:
| Chairs and armchairs | |
|---|---|
| Backrest on the Y- side | 43 |
| Too symmetrical to say | 4 |
| Backrest on the Y+ side | 1 |
So the knees go at the arrow and the backrest behind it. The viewport draws a second arrow at seat height to say so, and the checklist measures your own mesh the same way, warning with a half turn on a button when the back is on the wrong side. Nothing in the game will tell you: the item lands in the catalogue, a Para walks over, sits down, and faces their own backrest.
The floor arrow is a separate thing and is unchanged. It is the item's front, and it is what the catalogue thumbnail and the placement rotation use.
Paralives ships its own content as a mod: Main.mod, in the installation
folder, is an ordinary folder of FBX, PNG and text. Every number below was
measured there, not assumed. This section is the part of the project most
likely to be useful to other tools.
The game multiplies the raw vertex coordinates of an FBX by 0.01. It ignores both the file's unit declaration and any scaling on the node, so a mesh authored in metres arrives a hundred times too small: present, correctly placed, with the right footprint, and far too small to see.
Read out of the .import files, which are what the game made of each FBX:
| Mesh | Size in its prefab | Coordinates in the import |
|---|---|---|
CityGravelPile |
4.4642 m | around 2.24 |
| Cereal box | around 0.3 m | around 0.15 |
| An early ParaForge export | 1.9086 m | around 0.0088 |
No Blender export option produces this, because Blender puts the factor on the node. ParaForge scales the geometry on a throwaway copy instead.
The game's own body meshes carry their world position, so assembling them answers it outright:
| Mesh | From | To |
|---|---|---|
HumanFoot0Left |
0.000 | 0.119 |
HumanBottom |
0.390 | 0.962 |
HumanTop |
0.918 | 1.454 |
HumanHead |
1.427 | 1.702 |
Feet on the floor, top of the skull at 1.702. Anything built against a 1.80 reference comes out a little large next to the furniture the game ships, which is why ParaForge can draw a Para sized outline beside the item, proportioned on those four numbers.
An asymmetric item exported without a half turn arrives with its front where its back should be. On a chair it shows twice over, and both symptoms are the same rotation: the catalogue thumbnail is shot from behind, and a Para sits down facing their own backrest. Symmetrical items never show it at all.
ParaForge applies a half turn around Z before the Y up rotation, so what you build facing the green viewport arrow is what the game calls the front.
This one was settled in the game rather than in the files. Three attempts to
read it out of the shipped meshes gave three different answers, because the
FBX importer's axis conversion is not the inverse of the export's and every
reading needed a sign that could not be checked. It agrees with the slot
template independently: ChairSlotAndLocator puts the front feet at Z +0.42,
and Blender +Y leaves on the game's +Z, so the knees land where the arrow
points.
Same cause. Blender writes its axis conversion as a rotation on the node, and the game ignores the node, so the mesh arrives Z up in a Y up world, lying on its back. Importing with the conversion switched off shows the files as they are:
CityGravelPile.fbx base sits on Y=0 node rotation 180 deg about Z
Barbecue.fbx base sits on Y=0 node rotation 180 deg about Z
an early export base sits on Z=0 node rotation 90 deg about X
ParaForge bakes the rotation into the geometry too, and then tells the exporter to convert nothing.
This one is worth knowing for anyone writing .setting files by hand.
A collection can be written three ways, and the marker decides what the game does with the base game's own entries:
| Marker | Meaning |
|---|---|
i<index> |
Positional. Authoring a collection from scratch. Used by a mod on a list the base game also fills, it drops the base collection and keeps only what the mod wrote |
@<GUID> |
Add a new member to a list the base game already fills. What a content mod wants |
g<GUID> |
Merge fields onto a member that already exists |
The symptom of getting this wrong is spectacular and misleading: a
Translations.setting with one entry wipes the entire translation table, and
every menu label in the game turns into a raw key such as
UIBuildModeCatalog_XCancel.
Verified in the game's own data: French.mod extends Translations.Items
using g<GUID> markers with no s<N> size line, and never uses
i<index>. Credit to paralives-modgen
for documenting the @<GUID> form first.
The game does not give each item its own surface. Surfaces are a shared
material library: 397 of the 2434 shipped prefabs point at GenericGrayMask,
and 370 of those lay their own texture over it through DetailMap. Of the 486
DetailMap textures, 344 are used by exactly one prefab and live next to their
mesh, so it really is the per item slot.
That is one of the two shapes ParaForge writes, copied from
CityGravelPile.prefab:
ItemMeshReference:
Surfaces:
Surface:
GUID:4303346223996877069 identity of this list entry
Value:6533686579680309849 GenericGrayMask
DetailMap:4868737352193020236 the item's own texture
The other shape gives the item a surface of its own, which is the only place a
normal map and a smoothness value can live. No prefab field anywhere mentions
smoothness, metallic or occlusion, checked across 300 prefabs, so an item
borrowing a shared surface has no relief at all. The entry is modelled on
TextileQuiltedSquares, one of the 75 shipped surfaces carrying a real normal
map:
#Setting.Surfaces
=AllSurfaces
@2693213273477870343
=DisplayName:Stool
=Texture:4272001606441780869 the game's neutral gray base
=NormalAndAmbientOcclusionMap:... the item's relief
=AmbientOcclusionStrength:1
=SmoothnessValue:0.42
=DefaultSwatchGroup:0
=DefaultSwatch:0
Note that the item's colour is not in there. A surface's Texture is the
base the shader tints, and across the game's 925 references it is a GrayMask
634 times, a Master 100 times, and a Detail 133 times, the last almost always
under a vegetation or special shader. The colour of an ordinary item arrives
through DetailMap on the prefab, over that base. Putting the colour in
Texture and dropping DetailMap renders the item white.
Note the @ marker and the absent size line. Writing this positionally is what
made the game throw NullReferenceException in SurfaceThumbnailManager.Start()
at every launch: the mod was not adding a surface, it was replacing all 950 of
them with one.
There is no slot for a smoothness texture anywhere, only a single
SmoothnessValue per surface, used by 329 of the shipped ones. A Smoothness map
is therefore averaged into that number on export.
The game reads the presence of a colour attribute, not its contents. Any
attribute at all makes the mesh ZoneDefinition:VertexZones and demands a
recolourable shader that a plain surface cannot provide:
Material builder got given parameters that don't match any shaders -
ShaderType:Simple ZoneDefinition:VertexZones ...
The item then loads, takes its footprint, and draws nothing. Exporting a single
white zone is therefore not neutral, it is what makes the item invisible. The
game's own meshes confirm it: CityGravelPile.fbx and
ClutterKitchenIngredientCereal.fbx carry no colour attribute at all.
ParaForge only exports them when the item really is recolourable.
Triangle budget. 159 meshes taken at random from Environments/Items and
imported into Blender: median 294 triangles, 90th percentile 1 380, maximum
4 060. That maximum is ParaForge's default budget. A downloaded asset at
560 000 triangles is a hundred times the largest object in the game.
Texture resolution. Across 1 446 item textures: 512 px leads, then 256, then 1 024. Nothing above 2 048. A 4K map is four times the largest in the game.
Map usage. Detail 524, GrayMask 474, ColorZone 52, NormalOcclusion 41, Smoothness 22, Master 8. A normal map appears on one item in twenty.
Catalogue tags. The 298 Build Mode tags are extracted from the game by
tools/extract_catalog.py into paraforge/catalog.py, with their GUID and
their hierarchy. Run it again after a game update.
A door is 2.112 m tall, a single leaf 1.04 m wide. Useful for judging the scale of an imported model by eye.
Everything the game imposes is gathered in
paraforge/spec.py, so a game update should never require
touching another file.
Items in this mod lists every item the mod puts in the catalogue, with the picture the game itself rendered for it, and a button to take one out again.
Removing an item is not removing a file. An item is a prefab, a mesh, its
textures, a sidecar for each of those, an entry in Items.setting, an entry in
Translations.setting and a thumbnail the game cached somewhere else entirely.
Leave any of them and the mod carries a name with nothing behind it, or a
catalogue entry pointing at a prefab that is gone.
So the tree is walked from the item's own GUID outwards, by GUID and never by
name, because names collide and prefixes lie: Chaise1 is a prefix of
Chaise10. A mesh or a texture another item in the same mod still points at is
listed as shared and kept. Everything goes through the journal, so Undo the
last write puts a removal back.
The pictures are the game's own: it renders one for every item it loads and
caches it as a PNG named after the item's GUID, under
_GeneratedThumbnails/Items in one of the mod folders. An item the game has
never loaded has none, and the panel says how many rather than showing blanks.
Every generation is journalled in _paraforge/journal.json inside the mod,
with a copy of any file it changed. Undo the last write removes what was
created, restores what was changed, and cleans up folders left empty. Pressing
it twice walks back two generations. Regenerating an unchanged item does
nothing and does not add a step.
Export refuses any folder inside the Paralives installation. Assets belong in a
.mod under AppData\LocalLow, or a game update wipes them and they cannot be
shared.
Three values the developers have never published live in the Calibration panel, so a game update can be absorbed without a new release:
- Tile size, the size of one Build Mode grid tile in metres
- Triangle budget, your own ceiling, used for a warning only
- FBX units per metre, measured at 100
- Script mods are out of scope. The developers provide no tools for them and they are not allowed on the Steam Workshop. ParaForge only ever produces mods that can be uploaded.
- A smoothness map becomes one number. The game has no slot for a smoothness texture, only a value per surface.
- Recolourable items are only partly tested. The GrayMask base and the colour zones are written, but swatch groups have had far less use than the plain path.
- The format is not a published contract. Paralives is in early access. Every finding above is recorded with the game build it was measured on.
python build.pyOr let Blender validate the manifest while it packages:
python build.py --blender "C:/Program Files/Blender Foundation/Blender 5.2/blender.exe"The zip lands in dist/. Releases are built the same way by
the release workflow.
python tests/test_i18n.pyblender --background --factory-startup --python tests/test_headless.pyblender --background --factory-startup --python tests/test_ui_contract.pyThe first checks that no string was left out of the French catalogue. The
second covers geometry, zones, texture detection, baking, units, axes, the
.setting merge and the export. The third draws every panel and validates every
icon, property and operator against the live API, which catches the errors that
only show up on screen.
All three run on every push, against Blender 4.2 and 5.2, in the CI workflow.
paraforge/
spec.py everything Paralives imposes, in one place
validate.py the checklist
fixes.py one button per failed check
geo.py measurements shared by the validator, overlay and fixers
zones.py colour zone authoring, including the texture picker
textures.py role detection, naming, export
imaging.py channel rebuilding for glTF and ORM sources
bake.py merging several materials into one surface
exporter.py FBX and texture export into a .mod
item.py the prefab and the catalogue entry
setting.py reading and extending a .setting file
sidecar.py .meta files and GUID derivation
journal.py undo history for everything written into a mod
catalog.py Build Mode tags, generated from the game
overlay.py viewport guides and heads up checklist
ui.py the sidebar panel
i18n.py French and English strings
tools/
extract_catalog.py regenerate catalog.py from an installed game
mod_diff.py snapshot and diff a mod folder, without Blender
tests/
Created by infinition.
Thanks to the Paralives team for shipping a moddable game with a transparent, plain text data format, and to paralives-modgen for documenting the collection merge syntax.
Licensed under the GNU General Public License v3.0 or later. See
LICENSE for the full text. Blender add-ons link against bpy and are
distributed under the GPL for that reason.
