Go to file
Amir Alexander Abdelbaki 85eee5aeec File material extensions under their own name
An extension was written to the same path as the material it extends,
one pack over. A datapack file is identified by its path, so that never
added an extension to anything: it replaced the material with a stub,
whichever of the two packs loaded last. The stub then named itself as
its own parent, which MIAPI reads as a cycle and drops, and the material
was gone entirely for anyone with both mods installed:

  Unresolved extension cmmodular:metal/arcane_metal for target
  cmmodular:metal/arcane_metal (missing or cyclic dependency)

That is arcane metal and mithril, the two materials Create: Wizardry
adds forms of, and it explains why they worked before the extensions
existed. Ice and Fire's amethyst was never affected - it extends a
miapi: material, so its own cmmodular: path collided with nothing.

The generated name carries the pack that owns the extension, since the
pack is why the extension exists and there could be another from a
different mod later. Nothing else about the files changes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 15:33:05 +02:00
gradle/wrapper Add Truly Modular materials for Create, Mekanism, Ice and Fire and friends 2026-08-12 18:40:07 +02:00
src/main File material extensions under their own name 2026-08-14 15:33:05 +02:00
tools File material extensions under their own name 2026-08-14 15:33:05 +02:00
.gitignore Add Truly Modular materials for Create, Mekanism, Ice and Fire and friends 2026-08-12 18:40:07 +02:00
README.md Stop build.sh cleaning away the jar it just built 2026-08-14 15:04:57 +02:00
build.gradle Add Truly Modular materials for Create, Mekanism, Ice and Fire and friends 2026-08-12 18:40:07 +02:00
build.sh Stop build.sh cleaning away the jar it just built 2026-08-14 15:04:57 +02:00
gradle.properties Sand phasing, fire on the strike, faster swimming, and the arcana core 2026-08-14 12:02:19 +02:00
gradlew Add Truly Modular materials for Create, Mekanism, Ice and Fire and friends 2026-08-12 18:40:07 +02:00
gradlew.bat Add Truly Modular materials for Create, Mekanism, Ice and Fire and friends 2026-08-12 18:40:07 +02:00
settings.gradle Add Truly Modular materials for Create, Mekanism, Ice and Fire and friends 2026-08-12 18:40:07 +02:00

README.md

Create/Mekanism Modular

A Truly Modular addon for Minecraft 1.21.1 / NeoForge. It adds 128 materials drawn from 22 mods - Create, Mekanism, Ice and Fire, Immersive Engineering, Ender IO, Applied Energistics, Occultism, Ars Nouveau, Iron's Spells, Pastel, Crystal Chronicles, NauTec, Superb Warfare, Industrial Foregoing, RFTools, Create Deco, Steampunk Dimension and the Create aerospace addons - so their metals, gems, scales and essences can all be used to build modular tools and armour.

On top of the materials it adds a few modules of its own: a khopesh blade for Arsenal, and a sealed helmet and life support pack for Armory that between them let you breathe in the vacuum Create: Cosmonautics puts above the atmosphere. It adds one item as well, the arcana core, which is an endgame gem that ends every limit Iron's Spells puts on a caster.

Every source mod is optional. Install any subset - the materials for mods you do not have are never registered.

How it is put together

Almost all of this mod is data: MIAPI material JSON under src/main/resources/packs/<mod>/data/cmmodular/miapi/materials/.

The small amount of Java exists for the handful of things data cannot do.

Conditional loading. MIAPI reads materials with its own loader, which does not honour neoforge:conditions or anything else that could switch a file off, and a material whose ingredient item is missing fails to parse. So each source mod's materials are a separate built-in datapack, registered in CMModular.java only when that mod is present.

Identical materials are one material. Several metals and gems come from more than one mod - silver from Ice and Fire, Immersive Engineering, Occultism and Superb Warfare, steel from Mekanism, Immersive Engineering and Superb Warfare, and so on. There is one entry per metal, and every provider's version feeds it: alongside the primary provider's own items each material also accepts the c: common tag that all of them register into. Any mod's silver ingot is just silver.

Nothing records where a stack came from, because there is nothing to record - MIAPI stores only the material id on the item, so a tool built from Immersive Engineering silver is indistinguishable from one built with Ice and Fire silver. The primary provider's item is listed first, so anywhere a single representative item is read back out - repair and deconstruct previews - it resolves to the default rather than to whichever variant was used. Primaries are Mekanism for steel/lead/uranium, Ice and Fire for silver, Cosmonautics for titanium; each is a one-line change in tools/materials.py.

The material still lives in its primary provider's pack, so its icon and its first ingredient always point at an item that exists. Create: The Air War is the one provider that ships no common tags, so data/c/ adds its titanium to c:ingots/titanium with optional entries.

Fire immunity. Cinder essence grants immunity to fire, which is not an attribute the game has and not a property MIAPI offers. Instead of coupling to MIAPI's internals, FireImmunity.java registers a cmmodular:fire_immunity attribute, the material grants it like any other attribute, and a damage handler watches for it. Neither side knows about the other.

Breathing in a vacuum. The same trick, for the space set - see below.

Breathing underwater. And again, for sea serpent fang: WaterBreathing.java answers the same LivingBreatheEvent, but only when the bearer's eyes are in water, so a fang is a fang rather than half a spacesuit.

Moving through sand. Deathworm chitin does what the worm does, and this is the one thing an attribute and an event cannot carry on their own: whether a block collides is asked of the block, with the entity along for the ride in the collision context and nothing in between. So there is one mixin, BlockStateBaseMixin, and it is two lines - SandPhasing.java owns the decision. A block in cmmodular:sand_like returns no collision to a bearer of cmmodular:sand_phasing, and sand you are standing inside of cannot suffocate you.

It does not drop you through the desert. A block wholly below the feet still holds the bearer up, so sand stays something to walk on and a dune becomes something to walk into: step in and you keep your level, jump and the sand under you turns solid and puts you back on top. Without that rule the first sand block you stood on would swallow you down to the sandstone and leave you there, which is a trap rather than an ability.

Attributes that go missing. Modifiers granted by worn and held items are transient - vanilla reads them when equipment changes and the entity's attribute map is the only place they live. Go through a portal and they can be gone, with no equipment change to follow, so nothing puts them back until the piece is taken off and put on again; cinder essence's fire immunity is the one that gets noticed, because the Nether is exactly where it was wanted. EquipmentAttributes.java rebuilds the map from the equipment after a dimension change or a respawn, which is what re-equipping would have done and which fixes every attribute rather than only this mod's, and reads this mod's own attributes back off the equipment as well, so the answer is the same whether or not the map ever lost them.

One item. The arcana core, which is the only thing here that is not a material or a module - see below.

Modules

Three of the packs hold modules rather than materials. They are hand-written rather than generated, and gated on the Truly Modular addon whose slots they use.

Khopesh blade (packs/arsenal) fits any sword_blade slot, so it is an option wherever the plain sword blade is. It is the chopping sword it looks like: axe damage off hardness and density, a full point of knockback, an axe's mining level so it fells trees and strips logs, and a fast swing. The cost is everything a hook gives up - shorter reach than a sword, no sweep, and a quarter less durability than the blade it replaces.

There was no khopesh to borrow. Nothing in the Truly Modular ecosystem ships one, and the khopesh art that exists elsewhere is either another mod's item or a retexture of a vanilla sword - neither is a MIAPI module and neither is ours to ship, so the blade is drawn here.

Socketed pommels (packs/arsenal) give the bigger gems somewhere to sit. Arsenal does ship a socket pommel, but it takes a small gem, and a sweep of Arsenal, Armory, Archery and MIAPI turns up no module anywhere that offers a gem_melee_large slot - the large gemstone is defined and then has nowhere to go. So there are two here: a medium socket, and a great socket for the large gem. Each is a heavier counterweight than the last, paid for in swing speed.

The space set (packs/armory) is Create: Cosmonautics' answer to vacuum, rebuilt as modules. Cosmonautics suffocates you above the atmosphere by answering NeoForge's LivingBreatheEvent with "no", and exempts its own space helmet through Create's diving code, which asks whether the worn helmet is a DivingHelmetItem. A modular helmet cannot be one, so the exemption is unreachable and the set brings its own: VacuumSeal.java registers a cmmodular:vacuum_seal attribute and answers the same event last.

It takes two modules to reach the seal. The sealed helmet grants 0.5 and the life support pack on the chest grants 0.5, and only at 1 does the wearer breathe - so it is a suit, not a hat, which is the same trade Cosmonautics makes by pairing its helmet with a jetpack. Both come in a socketed version that trades 5% armour for a medium gem slot, exactly as Armory's own socket pieces do, and each socketed version has its own model and texture with the setting drawn in, rather than wearing the plain piece's art.

All of the art here is placeholder-grade and meant to be repainted - see Textures below.

The arcana core

The one item this mod adds, and the only late-game thing in it that is not simply a good material. Four arcane essence at the corners, four cinder essence on the edges, a nether star in the middle - all of which are already endgame - and what comes out is a gem that fits any socket and ends Iron's Spells' limits: a mana pool of a million refilled a hundred times over, cooldowns and cast times at zero, and ten times the spell power. It is deliberately absurd, which is the point of the recipe.

Cooldown and cast time are read by Iron's Spells as a factor on a base of 1 and spent as time * (2 - value), so a * modifier of 1 puts the value at 2 and the time at nothing; spell power is the same reading, where 1 + 9 is ten times. There is no "infinite mana" to ask for, so a pool nothing can empty and a regeneration that refills it instantly is what that means here.

The item is always registered, because an item cannot be added conditionally the way a datapack can. Nothing that makes it worth having is: the material and the recipe both live in the Iron's Spells pack, so without that mod the core cannot be crafted, is not a material, and grants nothing.

Its texture is Minecraft's own heart of the sea with its hue rotated into violet - the only art here that is not drawn for this mod, and Mojang's rather than ours, which is worth knowing given what the khopesh section above says about shipping other people's. Nothing but the hue is touched, so the shading is the vanilla item's, including how dark its underside is: the palette the material hands out is therefore a deep one with little highlight, which is what the item actually looks like rather than what a gem usually does.

Stats

Tinkers' Construct 3 is the reference, re-scaled onto MIAPI's vanilla-derived numbers (iron 235 durability, diamond 1546, netherite 2016). Materials with no Tinkers counterpart are built from what the material actually is, then fitted to the nearest tier band:

Tinkers here shape
cobalt titanium light, fast, durable, unremarkable damage
steel steel balanced workhorse, poor enchantability
hepatizon brass, bronze copper alloys - quick and enchantable, not durable
manyullyn dragonsteel top damage, endgame gate
rose gold refined glowstone, pixie dust fragile, huge enchantability
ancient hide dragon scales, chitin armour-first, high toughness

Tiers 6 and 7 sit above netherite: 6 is refined obsidian, shadow steel, refined radiance, atomic alloy and the two Iron's Spells essences; 7 is the three dragonsteels and antimatter.

Real-material character does the rest of the work - lead is the heaviest thing here and swings slowest, osmium is dense and tough, HDPE weighs nothing and cannot cut rock, silver smites the undead.

All of it lives in tools/materials.py, one entry per material with the reasoning in comments next to the numbers.

Palettes

Colours are not hand-picked. tools/generate_materials.py opens the source mods' jars, resolves each material's item model to its texture, and samples that texture to build MIAPI's seven-stop grayscale_map.

The sampling deliberately does not normalise a stop's colour to its brightness number. A stop key is the brightness of MIAPI's grayscale template; its value is whatever the material looks like at that point in its own shading, which is why MIAPI's own netherite tops out at a murky 847a84 while iron runs to white. Samples are taken at percentiles of the texture's brightness order, so a texture with four shades still yields a full ramp and any hue drift between shadow and highlight survives. Pure-black outline pixels are excluded - they are not a shade of the material - and the darkest stop is extrapolated below the texture, which is what MIAPI's own palettes do.

The sample points were tuned against MIAPI's hand-authored copper palette and reproduce it to within about 2% per channel.

Textures

The modules' own art is hand-drawn and lives where it can be found and changed:

assets/cmmodular/textures/items/       flat 16x16 art - gui icons, the khopesh blade
assets/cmmodular/textures/equipment/   32x32 cube nets for the worn armour models
assets/cmmodular/textures/templates_guide.png

Everything is greyscale, because MIAPI recolours a module's texture through the material's palette: the seven shades a texture may use are the brightness stops of that palette (24, 68, 107, 150, 190, 216, 255), and a pixel drawn between two of them lands somewhere interpolated and muddy.

textures/*/templates/ holds blanks to draw over - the same size as the real thing, with every region a model samples outlined and the seven shades parked in a corner no model reads, so they can be picked straight out of the file. templates_guide.png is a single sheet showing the shades with their values, the cube net with each face labelled, and the vanilla armour layout, for when a new piece is wanted rather than a repaint of an existing one.

Building

./build.sh

The jar lands in build/libs/. Needs nothing but NeoForge - Truly Modular is a runtime dependency only, since this mod talks to it purely through datapack JSON.

./gradlew build does the same thing, and is fine if the java on your path is one Gradle can run on. Gradle 8.12 cannot run on a JDK newer than 23, which a distro's default java may well be by now, and the failure when it is says Could not create task ':test' and Type T not present without ever mentioning Java. build.sh exists to pick a JDK between 17 and 23 - honouring JAVA_HOME if it already points at one - and to say what is wrong in as many words when there is none to pick. Arguments are passed through, with any task named replacing the default build rather than adding to it, so ./build.sh clean build cleans and then builds; a bare flag such as ./build.sh --info still gets the default task.

None of this touches what the mod is compiled against: the toolchain in build.gradle is Java 21 either way, and Gradle provisions it if it is missing.

Regenerating materials

Only needed after editing tools/materials.py or when a source mod changes its textures. The generated JSON is committed, so a plain build does not need this.

python3 tools/generate_materials.py --jars ~/path/to/mod/jars

Point it at the pack's mods folder. This mod's own namespace is served out of src/main/resources/assets/cmmodular/ rather than a jar, so the arcana core is checked and sampled like everything else. Every item id in materials.py is checked against those jars and the run fails on a typo, rather than shipping a material that silently refuses to load.

A mod whose jar is not there is skipped instead, with a line saying so, and its committed files are left untouched - the pack this follows adds and drops mods, and neither losing the materials of a mod you no longer have nor being unable to regenerate anything without a complete set of jars is useful. Nothing else is relaxed: a typo in a mod you do have still fails the run. Where several jars claim the same asset namespace - Applied Mekanistics writes into ae2, half the Create addons into create - the one with the most files in it wins, which is the mod that owns the namespace rather than whichever sorted first.

Requires pillow and numpy.

Module JSON and textures are not generated. A run only clears the material folders, so the hand-written packs survive it.

Notes

  • Titanium's palette comes from Cosmonautics' ingot, which is violet. Air War's is blue-white; swap palette_from on the titanium entry to prefer it.
  • Cinder essence is nearly black, because its texture is a charred ember. It is emissive, so it still reads as fire in game.
  • Dragon scales are one material per colour (12 dragon, 7 sea serpent, 3 death worm chitin). Colours within an element share stats and differ only in palette.
  • Sea serpent scales add miapi:generic.swim_speed per worn slot, 0.10 a module. It stacks per module like everything else here, so a full suit of them lands near +1 - about what Armory's own scuba set is worth, which is the company a sea serpent belongs in.
  • Deathworm chitin's sand phasing is gated on a tag, cmmodular:sand_like, so a pack can widen it: the common #c:sands, #c:gravels and #c:concrete_powders (the c: namespace being where Forge's tags went), each optional in case a set is not registered, plus the vanilla loose blocks by name so it works with none of them.
  • Dragon bone burns what it hits: a fake Fire Aspect I on handheld modules, the way Ice and Fire's own flamed dragonbone sword does. MIAPI's immolate is not that - despite the name it is bonus damage against something that is already alight, and MIAPI's own wiki marks it unimplemented. Ice and Fire ships one dragon bone item for all three kinds of dragon, so this cannot be the fire dragon's alone; the fire dragonscales are the only element-specific dragon material there is.
  • Bones are the other half of that. Dragon bone already built grips, handles and hafts - bone is on every tool and bow whitelist - and now carries gem_armor as a hidden group as well, so a piece can be set into an armour socket instead of only shaped into the armour. Sea serpent fang is new and does both: Ice and Fire has no sea serpent bone, and the fang is what a serpent leaves behind besides its scales. It grants water breathing, which is what Ice and Fire's own Tide Guardian armour does, from slot any - so one fang socketed, worn or held is enough, and a second neither adds nor dilutes.
  • Scales are in the scute group, and no module in Truly Modular accepts that group - not Armory's armour, not Arsenal's or Archery's parts, and MIAPI's own turtle and armadillo scutes are stuck the same way. So each scale material carries bone as a hidden group: matched when a module decides what it will accept, ignored when the workbench decides what heading to file the material under, which is how Armory hands its own gem slots vanilla materials. Bone is on every armour whitelist except the scuba set - that one takes fabric/metal/glass, being a sealed suit rather than a plated one - so a scale builds every other piece of armour, and tool and bow parts besides.
  • Interstellar Expansion is mostly machinery. Only three of its items are a material rather than a component; solid E-710 is left out because it is rocket fuel.
  • Gem sockets do not check the gem group. Arsenal gates melee gems on gem_melee and Armory gates armour gems on gem_armor, so every gem-like material carries both and fits any gem slot.
  • Immersive Engineering contributes aluminium, nickel, constantan, electrum and graphite. Its steel, lead, uranium and silver are not separate materials - see overlapping metals above. Superb Warfare's steel/lead/silver and Create Crafts & Additions' electrum are skipped for the same reason.
  • Crystal Chronicles and Pastel add whole families of gems that differ by colour and lore rather than by kind, so those are generated from a table with shared stat bands per tier. What actually tells them apart in game is the palette, and that still comes from each gem's own texture.
  • Arcane essence gives max mana per slot: 1300 on a helmet, 900 chest, 800 legs, 700 boots, 900 held in the main hand, 400 offhand. Separate per-slot modifiers rather than one armor-wide one, so a weapon reads as a held bonus instead of claiming something "when worn". They stack per module - a piece built out of arcane essence that also carries an arcane gem counts twice.
  • Titanium adds miapi:generic.elytra_glide_efficiency on the chest. Heavy armour subtracts -30 - density*2 from that stat, so this hands a little back rather than cancelling it; titanium's flexibility is nudged up too, since Armory's wing modules read flexibility and density for glide.
  • Cinder essence uses slot any, so one gem grants fire immunity wherever it sits, worn or held. The attribute caps at 1 and the handler treats anything at or above 1 as immune, so extra pieces neither add nor dilute.
  • Material properties belong under the default key, not armor or handheld. A gem socketed into armour is not tagged armor: gem modules declare which material properties they accept and list only default plus their own gem tags. Anything filed under armor/handheld silently never applies to a socketed gem. The attribute's own slot is what decides where it counts.
  • Arcane metal shortens Iron's Spells cooldowns, and that is the point of it: 0.10 held, 0.05 on a helmet, 0.03 on the lower armour, filed under default so a suit plated in it counts as well as a staff made of it. Iron's Spells reads the attribute as a factor on a base of 1 and spends it as cooldown * (2 - value), so the operation is * - a flat + would be added to a base of zero and do nothing - and 0.10 means 10% off the wait. It stacks per module, so a weapon built entirely of arcane metal is around 40%, against 25% for Iron's Spells' own best staff; a full set adds roughly another 45%, which keeps even an all-arcane player under the 100% where the formula bottoms out. The material is called Arcane Metal because the item is an Arcane Ingot and a material is named for the substance, not the shape it is traded in; plain Arcane, which is what Iron's Spells calls the material itself, is taken here by the essence. Create: Wizardry's arcane sheet and block are the same metal - they arrive as a material extension in that mod's own pack, since naming them directly would break the material for anyone without it.
  • Platinum comes from Create Propulsion: Simulated, the only platinum in the pack. It is soft for its density, so it swings slowly and hits like steel; it is worth mining for gold-grade enchantability that does not fall apart, and it is heat proof because platinum melts at 1768C.
  • Aviation alloy is AeroEngine's, iron superheated with netherite powder. A turbine superalloy: light and heat proof like titanium, harder, worse to enchant. It sits between titanium and titanium alloy, which is where its recipe sits too.
  • Extensions are material files with a different shape - parent naming the material to edit and data merged onto a copy of it, the same form MIAPI's stained glass uses to vary plain glass. The merge replaces a field rather than appending to it, so an extension that adds an ingredient repeats the ones it keeps; _extend_with_extra_forms builds those lists from the material itself.
  • Ice and Fire's amethyst gem is spelled amythest_gem in Ice and Fire. The extension named the correct spelling, which is an item that does not exist, so it did nothing. Extensions are item-checked like materials now.
  • Pastel's paltaeria and stratine gems break into eight fragments on an anvil, so a fragment counts as an eighth of a gem, the way a nugget counts as a ninth of an ingot.