Skip to content

Upgrading from 21.1.2 ​

Applies to after 21.1.2

What changed after MBD2 21.1.2 that can break a pack, a script, a blueprint or an addon, or make something behave differently. Most packs need nothing: a machine with one recipe logic and the default slot settings saves, loads and runs as before. For what is new, see What's new.

Requirements ​

Dependency21.1.2Newer
NeoForge21.1.217+unchanged
LDLib22.2.39.a+2.2.46+
KilaGraph21.1.0.12+, bundled21.1.0.16+, bundled
Photon (optional)2.2.5+unchanged
Astral Sorcery (optional)—2.0.3, which itself needs Curios 9.5.1+

KilaGraph ships inside the MBD2 jar, so every pack gets its newer version, and with it the blueprint changes below.

Worlds and packs ​

These need no code to show up.

ChangeWhat to check
A finished recipe whose outputs no longer fit waits at full progress until they do. Up to 21.1.2 whatever did not fit was voided.Machines whose output was full stall instead of eating items. Empty the outputs, or let auto IO do it.
With Consume inputs after working, the inputs are checked again at the finish: if any are gone the recipe is dropped without outputs. If the outputs do not fit, the inputs are taken first and the recipe then waits.Machines that relied on finishing with their inputs gone.
An idle machine searches only when something changed — a slot, a tank, a runtime value, a recipe reload — and otherwise every 100 ticks. Recipes start up to about 5 ticks sooner.Nothing, unless a script changes stacks in place (see KubeJS). asyncRecipeSearching now only matters for addons with their own search and inputs MBD2 cannot index, such as an ME interface.
Handlers that work along with a recipe — a Create rotation, a heat source — are told it stopped whenever it stops: suspended, interrupted, unloaded. A machine loaded mid-recipe tells them it started again.Create machines stop turning while suspended.
A fluid tank's GUI IO now also applies to buckets clicked on it.Tanks set to output only no longer accept poured fluid.
A recipe type proxying a type that does not exist no longer stops the other proxied types from loading.More proxied recipes may show up.
A recipe's duration is at least 1 tick.A modifier taking the duration to 0 no longer leaves the machine working forever.

Saves and downgrading ​

  • The primary recipe logic still saves where it always did, so worlds from 21.1.2 load unchanged.
  • Further threads and additional logics save under recipeLogics.<name>.<thread>. Removing or renaming one, lowering its threads, or going back to 21.1.2 drops the recipe it was running, whose inputs it already took.
  • A slot past a stack saves counts over 99 in a field 21.1.2 does not read: after a downgrade such a slot holds 1.
  • Machine items carrying kept trait data use a data component 21.1.2 does not know.
  • Project files gain keys for the new settings. 21.1.2 ignores them, which quietly switches those features off.

KubeJS scripts ​

ChangeWhat to do
Every recipe event carries event.recipeLogic, and fires once per logic on a machine with several. onRecipeStatusChanged reports that logic's own statuses; the machine state follows them all together.Nothing for a machine with one logic. With several, check event.recipeLogic before acting. See the event page.
onFuelBurningFinish fires. It never reached scripts before.A handler that was dead code now runs.
Changing a stack in place — getStackInSlot(0).shrink(1), growing a FluidStack — reports no change, so an idle machine notices it only at the next 100-tick search.Use insertItem, extractItem, setStackInSlot or the fluid handler's fill/drain.
While a recipe waits for room for its outputs, onRecipeWaiting fires every tick and onAfterRecipeWorking waits too. Interrupting from onAfterRecipeWorking drops the recipe without outputs; before, it re-entered the event until the server crashed.Handlers that assumed the finish always produced.
recipeLogic.setStatus(...) starts and stops the handlers working with the recipe.Forcing a status now also stops a Create rotation and the like.
With Ignore item stack limit on, getStackInSlot can return stacks larger than the item's max stack size; extractItem gives at most one stack.Scripts reading item counts from those slots.

New in the recipe schema: inputLumen, outputLumen, inputStarlight and the three Astral Sorcery conditions.

Blueprints ​

ChangeWhat to do
KilaGraph 21.1.0.16: a pure node runs again for every exec node that reads it, where it used to run once per pass, and a read after a flow, a loop or a subgraph sees the last write.Graphs that compute a value from something they change in the same pass, for example n + 1 written back to a tag several nodes read, give new results. Store the value in a variable first.
KilaGraph 21.1.0.16: NBT setters write into the tag they are given, and have a copy pin; a new NBT Copy node copies a tag.Copy a tag before changing it when it is not yours.
Recipe Info → Data and the entity ingredient NBT nodes give a copy. Editing it no longer changes the shared recipe every machine uses.To change a recipe: Copy Recipe, edit the copy, Set Event Recipe.
Machine Info → Custom Data has a copy input. Off, the default, it gives the machine's own tag, as before.Turn it on when the graph changes the tag but must not change the machine's.
A recipe logic node with its logic left unwired acts on the logic of the recipe event being handled, else on every logic of the machine; one reading the logic reads the event's, else the primary.Nothing for a machine with one logic. See Recipe logics.
The Fuel Burning Finish entry node fires.A graph wired to it starts running.
The built-in mbd2:auto_io_panel graph was fixed for KilaGraph 21.1.0.16.A machine using the built-in gets the fix. A copy made from 21.1.2 does not: copy the built-in again.

Java addons ​

Custom recipe capabilities, traits and handlers written for 21.1.2 need no change to keep working; Recipe search and your extensions explains what they can add to be searched faster.

Breaking ​

ChangeWhat to do
ItemSlotCapabilityTrait.storage is a LargeItemStackTransfer; createStorage() returns one and shouldSyncStorage takes one.Recompile. A compiled addon reading the field fails with NoSuchFieldError, and an old createStorage() override is silently skipped.
Recipe matching and IO are handed the RecipeLogic as their IRecipeCapabilityHolder, not the machine: matchRecipe, matchTickRecipe, handleRecipeIO, applyParallel.Code that casts the holder to IMachine fails: check for RecipeLogic and use its getMachine().
MBDRecipe.ActionResult.FAIL_NO_REASON is a failure. It used to count as success.Code relying on it passing. A recipe with nothing to match on a machine with no handlers still passes.

Behaviour changes ​

ChangeWhat to do
MachineRecipeStatusChangedEvent is posted from the new onRecipeLogicStatusChanged(logic, old, new), once per logic. The legacy notifyRecipeStatusChanged(old, new) runs only when the machine's summed-up status changes; it sets the machine state. IMultiPart.notifyControllerRecipeStatusChanged(controller, old, new) gets the summed-up status too, and a new overload gets each logic's.An override of the legacy hook that skipped super to silence the event no longer does.
Handlers are started and stopped through MBDRecipe.preWorking(RecipeLogic) / postWorking(RecipeLogic, List), after the inputs are taken, and exactly once each. The holder-based preWorking(holder) / postWorking(holder) are no longer called.A RecipeLogic subclass calling the old ones itself now tells the handlers twice.
The search goes through a recipe index. An addon overriding RecipeLogic.searchRecipe() or MBDRecipeType.searchRecipe(...) keeps the old full search.A subclass of a built-in item, fluid, energy or chemical trait inherits notifiesAllChanges() == true and lookup keys read from storage. If it changes contents without notifying, or consumes from elsewhere, override both to return false / null. See Recipe search and your extensions.
The primary logic is named: MBDMachine.createRecipeLogic(Object...) builds it with the definition's name, and createRecipeLogic(settings, name, thread) builds the others.An override returning new MyLogic(this) runs as main even when the editor renamed the primary, so scopes and UI addresses miss it. Pass the name on.
IMultiController hands modifyControllerRecipe and getMaxControllerParallel the logic the recipe is for. MBDMultiblockMachine.initCapabilitiesProxy ends with rebuildRecipeLogicProxies().An override that skips super must call it.
MBDRecipe.toBuilder() no longer starts from the recipe type's template, so the template's contents and conditions are not added twice. Built recipes no longer share lists with their builder, and copy() keeps the tier chance boost, XEI hiding and priority.Code that relied on the duplicated template.

Additive ​

Every recipe hook of IMachine has an overload taking the RecipeLogic first; the defaults call the old hooks, so a machine class written for one logic keeps working. An addon class with a same-named method of another return type no longer compiles. Events implement RecipeLogicEvent, with their old constructors kept. See Several recipe logics in Java, custom traits and custom recipe capabilities for the new hooks.

Released under the MIT License.