Skip to content

Upon preroll, destroy ranged ingredient.#5051

Open
DilithiumThoride wants to merge 11 commits into
dt/ranged-prerollsfrom
dt/ranged-prerolls-destroy-ranged
Open

Upon preroll, destroy ranged ingredient.#5051
DilithiumThoride wants to merge 11 commits into
dt/ranged-prerollsfrom
dt/ranged-prerolls-destroy-ranged

Conversation

@DilithiumThoride

@DilithiumThoride DilithiumThoride commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

What

Big enough structural change that it deserves its own PR into the PR branch.

Upon prerolling a Ranged Ingredient, destroy it and replace it with a Sized Ingredient of the rolled size.

Implementation Details

I realized this was a necessity after trying to fix #5048 to account for NeoForge's versions of SizedIngredient and SizedFluidIngredient.

AI Usage

  • [ X ] No AI driven tools were used for this pull request.

Outcome

The version of a recipe a machine actually runs will now never contain a Ranged Ingredient. This hugely simplifies the logic needed in Notifiable[X]Handler.handleRecipe().

How Was This Tested

Automated tests pass after being edited to account for it.

Additional Information

Four tests (ranged ingredient sabotage; ranged ingredients json) have been deleted as (1) there isn't a way to do those tests in this new system and (2) the behavior those tests were designed to catch for cannot exist in the new system either.

This is being done as an explicit PR into #5015 to verify it, and will then be replicated as a part of #5048.

Currently logged as a draft because, while it passes tests, I want to do a clean-up pass to remove any cases where something tries to get amount for of a ranged ingredient and really shouldn't.

Potential Compatibility Issues

If anything this resolves potential compatibility issues.

@DilithiumThoride DilithiumThoride added 1.20 type: refactor Suggestion to refactor a section of code Release: API - X.0.0 Major Breaking Refactors that MUST be in a API-Breaking Release labels Jul 2, 2026
@github-actions github-actions Bot added the Tests: Passed Game Tests have passed on this PR label Jul 2, 2026
…IllegalCallerException. TODO: replace this with something less destructive.
@github-actions github-actions Bot added Tests: Failed Game Tests have failed on this PR and removed Tests: Passed Game Tests have passed on this PR labels Jul 2, 2026
@github-actions github-actions Bot added Tests: Passed Game Tests have passed on this PR and removed Tests: Failed Game Tests have failed on this PR labels Jul 2, 2026
@DilithiumThoride DilithiumThoride marked this pull request as ready for review July 2, 2026 22:05
@DilithiumThoride DilithiumThoride requested a review from a team as a code owner July 2, 2026 22:05

@jurrejelle jurrejelle left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I like .replace() as a name, maybe could be replaced with something like .collapse() or the like, since replace usually gets used in stuff like string.replace("a","b"). Apart from that LGTM

syncDataHolder.markClientSyncFieldDirty("lastDisplayedRecipe");
markLastRecipeDirty();
}
recipe.doTickPrerolls(this.chanceCaches, this.lastDisplayedRecipe);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this taking lastDisplayedRecipe is a bit iffy since the name implies it's used for displaying, but that might have to wait until 9.0.0 recipelogic refactor

@screret screret left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly some formatting nitpicks, code itself looks fine to me.

P.S. Maybe rename the PR to "Destroy ranged ingredients when prerolling" - I think that'd convey what it does a bit better

FluidIngredient recipeStack = FluidRecipeCapability.CAP.of(fluidInputs.get(i).content());
if (!recipeStack.test(fluidStack) || (recipeStack.getAmount()) > fluidStack.getAmount()) {
if (!recipeStack.test(fluidStack) ||
(recipeStack instanceof IRangedIngredient ? ((IRangedIngredient) recipeStack).getMaxRoll() :

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
(recipeStack instanceof IRangedIngredient ? ((IRangedIngredient) recipeStack).getMaxRoll() :
(recipeStack instanceof IRangedIngredient ranged ? ranged.getMaxRoll() :

content = new Content(ranged.collapse(), content.chance(), content.maxChance());
ranged.reset();
}
tickInputs.computeIfAbsent(capability, c -> new ArrayList<>()).add(content);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
tickInputs.computeIfAbsent(capability, c -> new ArrayList<>()).add(content);
this.tickInputs.computeIfAbsent(capability, c -> new ArrayList<>()).add(content);

content = new Content(ranged.collapse(), content.chance(), content.maxChance());
ranged.reset();
}
tickOutputs.computeIfAbsent(capability, c -> new ArrayList<>()).add(content);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
tickOutputs.computeIfAbsent(capability, c -> new ArrayList<>()).add(content);
this.tickOutputs.computeIfAbsent(capability, c -> new ArrayList<>()).add(content);

ranged.rollSampledCount();
for (var handler : inputs.values()) {
for (var iterator = handler.listIterator(); iterator.hasNext();) {
var content = iterator.next();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Less var, please

Suggested change
var content = iterator.next();
for (List<Content> input : this.inputs.values()) {
for (ListIterator<Content> iterator = input.listIterator(); iterator.hasNext();) {
Content content = iterator.next();

Comment on lines +247 to +249
for (var handler : outputs.values()) {
for (var iterator = handler.listIterator(); iterator.hasNext();) {
var content = iterator.next();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for (var handler : outputs.values()) {
for (var iterator = handler.listIterator(); iterator.hasNext();) {
var content = iterator.next();
for (List<Content> output : outputs.values()) {
for (ListIterator<Content> iterator = output.listIterator(); iterator.hasNext();) {
Content content = iterator.next();


int amount = ing.getAmount();
int amount;
if (ing instanceof IRangedIngredient provider) amount = provider.getMaxRoll();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (ing instanceof IRangedIngredient provider) amount = provider.getMaxRoll();
if (ing instanceof IRangedIngredient ranged) amount = ranged.getMaxRoll();

@Override
public long kjs$getAmount() {
return ingredient.getAmount();
return (ingredient instanceof IRangedIngredient ? ((IRangedIngredient) ingredient).getMaxRoll() :

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return (ingredient instanceof IRangedIngredient ? ((IRangedIngredient) ingredient).getMaxRoll() :
return (ingredient instanceof IRangedIngredient ranged ? ranged.getMaxRoll() :

.map(FluidRecipeCapability.CAP::of)
.filter(i -> !i.isEmpty())
.mapToInt(FluidIngredient::getAmount)
.mapToInt((i -> i instanceof IRangedIngredient ? ((IRangedIngredient) i).getMaxRoll() : i.getAmount()))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.mapToInt((i -> i instanceof IRangedIngredient ? ((IRangedIngredient) i).getMaxRoll() : i.getAmount()))
.mapToInt((i -> i instanceof IRangedIngredient ranged ? ranged.getMaxRoll() : i.getAmount()))

Comment on lines +220 to +228
// This will print a "Cannot get stacks" warning to the log. Ignore it.
helper.assertTrue(ingredient.getStacks().length == 0, "A ranged fluid ingredient " +
"should not return items!");
ingredient.rollSampledCount();
var stacks = ingredient.collapse().getStacks();
helper.assertTrue(stacks.length == 1, "Replaced IntProviderFluidIngredient should only " +
"return 1 fluid when made with 1 fluid");
helper.assertTrue(stacks[0].isFluidEqual(GTMaterials.Water.getFluid(1)),
"IntProviderFluidIngredient should have fluid equal to what it was made with");
helper.assertTrue(stacks[0].isFluidStackIdentical(ingredient.getStacks()[0]),
"IntProviderFluidIngredient.getStacks shouldn't change between getStacks calls");
ingredient.reset();
helper.assertFalse(stacks[0].isFluidStackIdentical(ingredient.getStacks()[0]),
"IntProviderFluidIngredient.getStacks should have changed after rerolling");
"Replaced IntProviderFluidIngredient should have fluid equal to what it was made with");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add some newlines here, for spacing

Suggested change
// This will print a "Cannot get stacks" warning to the log. Ignore it.
helper.assertTrue(ingredient.getStacks().length == 0, "A ranged fluid ingredient " +
"should not return items!");
ingredient.rollSampledCount();
var stacks = ingredient.collapse().getStacks();
helper.assertTrue(stacks.length == 1, "Replaced IntProviderFluidIngredient should only " +
"return 1 fluid when made with 1 fluid");
helper.assertTrue(stacks[0].isFluidEqual(GTMaterials.Water.getFluid(1)),
"IntProviderFluidIngredient should have fluid equal to what it was made with");
helper.assertTrue(stacks[0].isFluidStackIdentical(ingredient.getStacks()[0]),
"IntProviderFluidIngredient.getStacks shouldn't change between getStacks calls");
ingredient.reset();
helper.assertFalse(stacks[0].isFluidStackIdentical(ingredient.getStacks()[0]),
"IntProviderFluidIngredient.getStacks should have changed after rerolling");
"Replaced IntProviderFluidIngredient should have fluid equal to what it was made with");
// This will print a "Cannot get stacks" warning to the log. Ignore it.
helper.assertTrue(ingredient.getStacks().length == 0,
"A ranged fluid ingredient should not return items!");
ingredient.rollSampledCount();
var stacks = ingredient.collapse().getStacks();
helper.assertTrue(stacks.length == 1,
"Replaced IntProviderFluidIngredient should only return 1 fluid when made with 1 fluid");
helper.assertTrue(stacks[0].isFluidEqual(GTMaterials.Water.getFluid(1)),
"Replaced IntProviderFluidIngredient should have fluid equal to what it was made with");

Comment on lines +215 to +223
// This will print a "Cannot get items" warning to the log. Ignore it.
helper.assertTrue(ingredient.getItems().length == 0, "A ranged ingredient " +
"should not return items!");
ingredient.rollSampledCount();
var stacks = ingredient.collapse().getItems();
helper.assertTrue(stacks.length == 1, "Replaced IntProviderIngredient should only " +
"return 1 item when made with 1 item");
helper.assertTrue(stacks[0].is(new ItemStack(Items.BRICK, 1).getItem()),
"IntProviderIngredient should have item equal to what it was made with");
helper.assertTrue(TestUtils.areItemStacksEqual(stacks, ingredient.getItems()),
"IntProviderIngredient.getItems shouldn't change between getStacks calls");
ingredient.reset();
helper.assertFalse(TestUtils.areItemStacksEqual(stacks, ingredient.getItems()),
"IntProviderIngredient.getItems should have changed after rerolling");
"Replaced IntProviderIngredient should have item equal to what it was made with");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More spacing here too

Suggested change
// This will print a "Cannot get items" warning to the log. Ignore it.
helper.assertTrue(ingredient.getItems().length == 0, "A ranged ingredient " +
"should not return items!");
ingredient.rollSampledCount();
var stacks = ingredient.collapse().getItems();
helper.assertTrue(stacks.length == 1, "Replaced IntProviderIngredient should only " +
"return 1 item when made with 1 item");
helper.assertTrue(stacks[0].is(new ItemStack(Items.BRICK, 1).getItem()),
"IntProviderIngredient should have item equal to what it was made with");
helper.assertTrue(TestUtils.areItemStacksEqual(stacks, ingredient.getItems()),
"IntProviderIngredient.getItems shouldn't change between getStacks calls");
ingredient.reset();
helper.assertFalse(TestUtils.areItemStacksEqual(stacks, ingredient.getItems()),
"IntProviderIngredient.getItems should have changed after rerolling");
"Replaced IntProviderIngredient should have item equal to what it was made with");
// This will print a "Cannot get items" warning to the log. Ignore it.
helper.assertTrue(ingredient.getItems().length == 0,
"A ranged ingredient should not return items!");
ingredient.rollSampledCount();
var stacks = ingredient.collapse().getItems();
helper.assertTrue(stacks.length == 1,
"Replaced IntProviderIngredient should only return 1 item when made with 1 item");
helper.assertTrue(stacks[0].is(new ItemStack(Items.BRICK, 1).getItem()),
"Replaced IntProviderIngredient should have item equal to what it was made with");

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

1.20 Release: API - X.0.0 Major Breaking Refactors that MUST be in a API-Breaking Release Tests: Passed Game Tests have passed on this PR type: refactor Suggestion to refactor a section of code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants