This page says how to lay out a resource pack repository so that MCME's release pipeline can build it: which files go where, the rules each file must follow, which branches the pipeline builds, and how to see the result on your own computer before you release. It applies to new packs and to existing ones. What happens after you push is in The release pipeline.
Sodium pack or vanilla-only pack?
Decide this first. It sets the layout, the release script and the zip names.
| Sodium pack | Vanilla-only pack | |
|---|---|---|
Has 3D .obj models in assets/mcme/ |
Yes | No |
| Release script | releaseVanillaSodium.sh |
releaseGeneral.sh |
| Zips per release | <Pack>-Sodium, -Vanilla, -Lite, each also as -Footprints |
<Pack>.zip and <Pack>-Footprints.zip |
| Conversion | generateVanilla makes the Vanilla and Lite zips | None: the repository is zipped as it is |
| Examples | Human, Mordor, Paths of the Dead | Dwarven, Rohan, Lothlorien |
As soon as one blockstate or item points at an
mcme:model with an.objfile, the pack must be a Sodium pack. A vanilla-only release zips those models unconverted. Only players with the Special Model Loader mod can render them, so everyone else sees missing-model blocks.
Layout of a Sodium pack
This is the layout of RP-Human, RP-Mordor and RP-PathOfTheDead on master.
RP-<Name>/
├── pack.mcmeta in every zip; see below
├── pack.png in every zip
├── license.txt in every zip
├── README.md in every zip
├── .gitignore never in a zip (nor is any name starting with a dot)
├── .github/workflows/check-shaders.yml the shader check on GitHub
├── .mcme-shaders.json which shader modules the pack turns on
├── .mcme-shaders.lock written by the shader sync; commit it, don't edit it
├── assets/
│ ├── minecraft/
│ │ ├── blockstates/ which model each block state uses; may name mcme: models
│ │ ├── items/ item definitions; may name mcme: models
│ │ ├── models/ ordinary JSON models
│ │ ├── textures/
│ │ └── shaders/ the shader base (written by the sync) and your hooks
│ ├── sodium/shaders/ the shader base's Sodium copies (written by the sync)
│ └── mcme/
│ ├── models/block/ the 3D models: <name>.json + .obj + .mtl (+ .objmeta)
│ ├── textures/block/ the textures the .mtl files name
│ └── sml_load_scopes/ Special Model Loader's list of models (Sodium zip only)
├── mc26_3/ the shaders as Minecraft 26.3 needs them (written by the sync)
├── vanilla/ optional: files only the Vanilla and Lite zips get
└── 1_*/ optional version overlays
The shader files, .mcme-shaders.lock and mc26_3/ are written by the shader sync, never by hand: see The shader base.
What ends up in which zip
| In the repository | Sodium zip | Vanilla and Lite zips |
|---|---|---|
pack.mcmeta, pack.png, license.txt, README.md |
Yes | Yes. In pack.mcmeta, the word Sodium in the description becomes Vanilla. |
Any other file at the top (changelog.txt, blockList.txt, …) |
Yes | No |
assets/ |
Yes, as it is | Rebuilt: see below |
vanilla/ |
No | Merged in: see The vanilla folder |
Top-level folders named 1_… |
Yes | Yes, copied as they are |
Overlay folders listed in pack.mcmeta, such as mc26_3/ |
Yes | Yes, copied; the build then writes the newest shader base into mc26_3/ |
| Any other top-level folder | Yes | No |
| Anything at the top whose name starts with a dot | No | No |
How assets/ is rebuilt for the Vanilla and Lite zips:
- Everything under
assets/is copied, except these folders in every namespace:blockstates,items,models,textures/blockandtextures/item.assets/mcme/sml_load_scopesis left out too. (Themodelenginenamespace keeps itsmodelsanditems.) - Those folders are rebuilt from what the blockstates and item definitions actually use: every
mcme:model is baked into a vanilla model plus a texture, and every other model the pack overrides is copied, with its parents and textures. - Anything nothing points at is left out. A texture in
textures/blockthat only a font or a GUI uses is lost, so keep such textures elsewhere, for example intextures/gui.
How the conversion works has the full details.
pack.mcmeta
RP-Human's, as an example:
{
"pack": {
"pack_format": 88,
"min_format": 88,
"max_format": 97,
"description": "MCME Human v4.1.5 for MC 26.3 (and 26.2)"
},
"sodium": {
"ignored_shaders": ["fog.glsl", "terrain.fsh", "terrain.vsh"]
},
"overlays": {
"entries": [
{ "min_format": 97, "max_format": 97, "directory": "mc26_3" }
]
}
}
- Pack formats: 88 is Minecraft 26.2 and 97 is 26.3. A range from 88 to 97, with the
mc26_3overlay for 97, lets one zip serve both versions. The shader sync writesmin_format,max_formatand the overlay entry for you when the pack has anmc26_3/folder. - The release note reads these formats. It names every Minecraft version all the zips support, for example "Version v4.1.5 for Minecraft 26.2 and 26.3".
- The description must be a plain string. A text component (
{"text": …}) stops generateVanilla with an error. - Put the version in the description, and update it before each release. Nothing does it for you.
- If the description contains
Sodium(case-sensitive), the Vanilla and Lite zips getVanillathere instead, so players can tell the variants apart in their pack list. Without it, all three variants show the same description. sodium.ignored_shadersis written by the shader sync. It tells Sodium that the pack's vanilla terrain shaders are there on purpose. Leave it in.- Overlays: a folder only does something in game if
overlayslists it. Folders named1_…are copied into every zip whether listed or not; any other overlay folder reaches the Vanilla and Lite zips only whenoverlayslists it.
The 3D models in assets/mcme
Every 3D model is a set of files with the same name in assets/mcme/models/block/:
| File | Needed | What it is |
|---|---|---|
<name>.json |
Yes | The model in Special Model Loader's format |
<name>.obj |
Yes, or a shared …_parent.obj |
The geometry, in block units (0 to 1 is one block) |
<name>.mtl |
Yes, unless the JSON names another one in mtl_override, or the .objmeta sets texture |
Names the texture in a map_Kd line |
<name>.objmeta |
No | Conversion settings: see How the conversion works |
RP-Human's pine_leaves.json, shortened:
{
"parent": "special-model-loader:builtin/obj",
"model": "mcme:models/block/leaves_parent.obj",
"mtl_override": "mcme:models/block/pine_leaves.mtl",
"particle": "mcme:block/beech_leaves",
"flip_v": true
}
The .mtl it names:
newmtl m_b2bff805-3e5a-c860-a1cb-7ffdc3695c42
map_Kd mcme:block/pine_leaves
Rules:
modelmust point at an.objthat is committed. A model JSON withoutmodel, or a missing.obj, is skipped without any message, and the block shows as a missing model in the Vanilla and Lite zips.modelandmtl_overrideuse themcme:namespace.map_Kdmust start its line and name the texture asmcme:block/<texture>(inassets/mcme/textures/) orminecraft:block/<texture>(in the pack's ownassets/minecraft/textures/; the vanilla client's textures can't be used). Only the firstmap_Kdcounts: one texture per model.- The texture must be at least 8 pixels wide.
- Textures used with one shared parent must all be the same size. Several models can share one
.objnamedparent, or ending in_parent, optionally followed by one number:leaves_parent.obj,leaves_parent_2.obj,parent.obj. The Vanilla zip then stores that geometry once. Treat the warningshares parent group … but its baked texture is …as an error and fix the texture sizes (see Shared parents). - Names like
leaves_vertical_slab_parent_1_2.objaren't shared parents (only one number may follow_parent). They work, but every model using them gets its own copy of the geometry. - Cutout textures (leaves, plants) should use only fully transparent and fully opaque pixels. One semi-transparent pixel makes the whole texture render with soft edges in the Vanilla zip.
- Animated textures need a
.png.mcmetanext to the texture. The sheet must divide evenly into frames. - Keep
sml_load_scopesin step with your models. Special Model Loader reads it; the Vanilla and Lite zips leave it out. The packs keep one file,assets/mcme/sml_load_scopes/test_scopes.json, which lists models as"mcme:block/<name>"under"entry". - The old
.objmetakeyparent:is ignored. Many of RP-Human's.objmetafiles still have it; it does no harm.
Blockstates and item definitions
They live in assets/minecraft/blockstates/ and assets/minecraft/items/, and point at 3D models with an mcme: identifier:
{
"variants": {
"": [
{ "model": "mcme:block/hornbeam_leaves" },
{ "model": "mcme:block/hornbeam_leaves_2" },
{ "model": "mcme:block/hornbeam_leaves_3" }
]
}
}
- Put the most important models first in a list. The Lite zip keeps only the first two of every list.
- Rotate around one axis only. The converter bakes an
x,yorzrotation into a model, not two. Withxandyon one entry,yis dropped with a warning. Make a pre-rotated model instead. - Rotations must be numbers (90, 180, 270).
- Only blocks and items that exist in Minecraft 26.2 reach the Vanilla and Lite zips (see Known limits).
The vanilla folder
vanilla/ is optional (RP-Human, RP-Mordor and RP-PathOfTheDead have none). It holds files only the Vanilla and Lite zips need, merged into the converted pack's assets/.
blockstatesanditemshere replace the pack's own, for blocks that should look different without the mod.models,textures/blockandtextures/itemhere are ignored. Those folders are rebuilt from what the blockstates use, so old pre-baked models invanilla/do nothing.- Everything else is copied on top: fonts, GUI textures and so on.
vanilla/1_…folders are merged into the overlay folders of the same name.- No shaders here. The shader base lives in the pack's root
assets/(see The shader base). A changed copy of a base file anywhere,vanilla/included, stops the build.
Without a vanilla/ folder the converter prints WARNING!!! Missing vanilla overrides folder. That is expected and harmless.
Layout of a vanilla-only pack
RP-<Name>/
├── pack.mcmeta
├── pack.png
├── license.txt
├── README.md
├── .gitignore
├── .mcme-shaders.json if the pack uses the shader base, with what the sync writes
├── .mcme-shaders.lock
├── assets/
│ └── minecraft/
│ ├── blockstates/
│ ├── models/
│ ├── textures/
│ └── shaders/
└── mc26_3/
- Everything at the top except names starting with a dot goes into the zip, as it is. A folder named
inventoriesis left out. - No
mcme:models with.objfiles. If you start adding them, turn the pack into a Sodium pack first. - The shader base is synced into vanilla-only packs too (Dwarven and Lothlorien have it). A vanilla-only release zips the repository as it is, so whatever the sync last wrote is what players get.
- The same
pack.mcmetarules apply, apart from theSodiumword.
Branches
| Branch | Built by | Released to |
|---|---|---|
development |
Test releases on the RP server | A test repository |
master |
Production releases on the main servers | The pack's own repository |
| Anything else | Nothing | Nothing |
- Work on a feature branch and open pull requests into
development, so several people can work on the pack at once. - Bring tested work to production with a pull request from
developmentintomaster. - A release builds exactly what GitHub has on the branch its stage builds. Work that is only on your computer, or on another branch, isn't in it.
.gitignore
Keep it short. Nothing in a pack needs ignoring except editor and OS clutter:
# OS and editor files
.DS_Store
Thumbs.db
desktop.ini
.idea/
.vscode/
*.bak
*.tmp
Never ignore
*.obj, and don't start from the stock Visual Studio or Python.gitignore. Those ignore*.obj, folders namedobj/,build/orrelease/, and*.metaand*.logfiles. An ignored.objnever reaches GitHub, so the server builds the pack without it. That happened to a test release in October 2026: its 3D blocks showed as missing models.
To check that a file is really committed, run git check-ignore -v <file>. It prints the rule that ignores the file, or nothing.
Don't commit OS clutter deeper in the tree either: only names that start with a dot at the top are kept out of the zips, so a committed assets/.DS_Store ships.
Try the conversion on your computer
The server runs the same converter, so a local run shows what a release will contain, with every warning, in a few minutes. You need Python 3.10 or newer and Git.
-
Get the converter:
git clone https://github.com/MCME/ResourcePackScripts.git cd ResourcePackScripts pip install -r requirements.txt -
Get Minecraft 26.2's resources. Start 26.2 once in the launcher, so the client jar is downloaded to
versions/26.2/26.2.jarin your Minecraft folder (%APPDATA%\.minecrafton Windows). A.jaris a zip file: open it with any zip tool and extract itsassetsfolder into a new folder, for exampleminecraft-26.2, next to ResourcePackScripts. Use 26.2, because the server does. -
Convert, from the ResourcePackScripts folder, with your pack checked out next to it:
# Vanilla variant python generateVanilla/generateVanilla.py ../RP-Human ../out-vanilla ../minecraft-26.2 --objmc generateVanilla/objmc.py # Lite variant python generateVanilla/generateVanilla.py ../RP-Human ../out-lite ../minecraft-26.2 --objmc generateVanilla/objmc.py --limit 2- Always pass
--objmc. Without it the converter looks forobjmc.pyin the folder you run it from. - Use a new, empty output folder every time. The converter adds to an existing folder but never deletes from it.
- Read every
WARNING,Error,MissingandNote:line. Not every message starts withWARNING. Troubleshooting says what each one means.
- Always pass
-
Try it in game. Zip the contents of the output folder, so that
pack.mcmetais at the top of the zip, and put the zip in yourresourcepacksfolder.
Bring a new pack into the pipeline
-
Create the repository under the MCME organisation as
RP-<Name>, withmasteranddevelopmentbranches. -
Lay it out as a Sodium pack or a vanilla-only pack. For a Sodium pack, RP-Human's
pack.mcmetaandlicense.txtare a good start. Don't copy RP-Human's.gitignore: use the short one above. -
Add the shader base: write a
.mcme-shaders.json, run the sync and commit what it wrote. Copy ResourcePackScripts'ci/check-shaders.ymlto.github/workflows/check-shaders.yml. See The shader base. -
Try the conversion locally and fix the warnings.
-
Ask an operator to add the pack to the release automation, with:
They need Example The pack's name in the pipeline: one word, letters only. Every zip is named after it, and staff type its first letters. RivendellSodium pack or vanilla-only Sodium The repository MCME/RP-RivendellThe test release repository A test repository, or MCME/RP-RivendellitselfThe Minecraft versions it is for 26.2 and 26.3 Where players should get it An RP region on the RP server, then the main servers Choose a name whose first letter isn't taken by another pack, and keep it short: every download URL (
https://github.com/<owner>/<repo>/releases/download/<tag>/<zip>) must stay under 100 characters. The operators' side is in Resource pack server (operators only). -
Make the first test release on the RP server and check it in game (see The release pipeline).
Turn a vanilla-only pack into a Sodium pack
For a pack that so far is zipped as it is, and now gets .obj models.
- Commit the models properly. Remove
*.objand similar rules from.gitignore, then add every.obj,.mtl,.objmetaand model JSON underassets/mcme/models/block/, and the textures underassets/mcme/textures/. - Bring the shader base in, if the pack doesn't have it yet: replace the pack's own
text.vsh,fog.glsland any objmc shaders by adding a.mcme-shaders.jsonand running the sync, with--forcethe first time. Commit what it wrote. - Check
pack.mcmeta. Optionally addSodiumto the description. - List the models in
assets/mcme/sml_load_scopes/, as RP-Human does. - Try the conversion locally. Check that blocks only your pack has still appear in the Vanilla output. If one is missing, see Known limits.
- Ask an operator to switch the pack to
releaseVanillaSodium.sh. Its zips get new names (<Pack>-Vanilla.zipinstead of<Pack>.zip, and so on), and its server slots get the six-zip layout at the same time. - Make a test release on the RP server and check it with a vanilla client, with the MCME modpack, and with
/rp client lite.
Checklist before every release
- Everything is pushed to
development(test) or merged intomaster(production). - The version in
pack.mcmeta's description is updated. -
git statusshows nothing left uncommitted, and no.objis ignored. - If you changed a shader, a fluid texture or
.mcme-shaders.json, the sync has run and its changes are committed, and the shader check on GitHub is green. - For a Sodium pack, a local conversion runs without new warnings.
- You know the new tag, and nobody else is releasing right now.
Known limits
- Blocks newer than Minecraft 26.2 are dropped from the Vanilla and Lite zips, 26.3's included. The converter walks the block and item list of the vanilla resources it is given, and the server gives it 26.2's.
- Only one rotation axis per model entry is baked.
- Overlay folders aren't converted. They are copied as they are, so an
mcme:model inside one isn't baked. - No biome tint on baked models. The converter strips
tintindex, so baked grass and leaves don't take the biome colour. - The Sodium-Footprints zip swaps only
activator_rail.png, notactivator_rail_on.pngas the other Footprints zips do. - A release may run for 30 minutes before MCME-Architect stops it, unless an operator gives the pack a longer time.