Skip to content

Commit 5960fb0

Browse files
committed
Merge remote-tracking branch 'origin/master-1.20-lts' into master-1.21-lts
2 parents b65b7b7 + 37c4776 commit 5960fb0

8 files changed

Lines changed: 154 additions & 46 deletions

File tree

CHANGELOG-1.19.2.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
11
# Changelog for Minecraft 1.19.2
22
All notable changes to this project will be documented in this file.
33

4+
<a name="1.19.2-1.6.16"></a>
5+
## [1.19.2-1.6.16](/compare/1.19.2-1.6.15...1.19.2-1.6.16) - 2026-02-17 11:41:13
6+
7+
8+
### Added
9+
* Show ingredient alternatives in flat crafting plan, Closes #189
10+
411
<a name="1.19.2-1.6.15"></a>
5-
## [1.19.2-1.6.15](/compare/1.19.2-1.6.14...1.19.2-1.6.15) - 2026-01-17 14:24:55
12+
## [1.19.2-1.6.15](/compare/1.19.2-1.6.14...1.19.2-1.6.15) - 2026-01-17 14:24:55 +0100
613

714

815
### Changed

CHANGELOG-1.20.1.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
11
# Changelog for Minecraft 1.20.1
22
All notable changes to this project will be documented in this file.
33

4+
<a name="1.20.1-1.6.18"></a>
5+
## [1.20.1-1.6.18](/compare/1.20.1-1.6.17...1.20.1-1.6.18) - 2026-02-17 11:43:36
6+
7+
8+
### Changed
9+
* Show ingredient alternatives in flat crafting plan, Closes #189
10+
411
<a name="1.20.1-1.6.17"></a>
5-
## [1.20.1-1.6.17](/compare/1.20.1-1.6.16...1.20.1-1.6.17) - 2026-01-17 14:26:49
12+
## [1.20.1-1.6.17](/compare/1.20.1-1.6.16...1.20.1-1.6.17) - 2026-01-17 14:26:49 +0100
613

714

815
### Changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
As always, don't forget to backup your world before updating!
2+
Requires CyclopsCore version 1.19.4 or higher.
3+
4+
Additions:
5+
* Show ingredient alternatives in flat crafting plan, Closes #189
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
As always, don't forget to backup your world before updating!
2+
Requires CyclopsCore version 1.19.4 or higher.
3+
4+
Changes:
5+
* Show ingredient alternatives in flat crafting plan, Closes #189

src/main/java/org/cyclops/integratedterminals/api/terminalstorage/crafting/ITerminalCraftingPlanFlat.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,16 @@ public static interface IEntry {
6868

6969
/**
7070
* @return The entry instance.
71+
* @deprecated Use {@link #getInstances()} instead. TODO: rm in next major
7172
*/
73+
@Deprecated
7274
public IPrototypedIngredient<?, ?> getInstance();
7375

76+
/**
77+
* @return The alternative entry instances for this entry. Never empty.
78+
*/
79+
public List<IPrototypedIngredient<?, ?>> getInstances();
80+
7481
/**
7582
* @return The number of instances to craft.
7683
*/

src/main/java/org/cyclops/integratedterminals/api/terminalstorage/crafting/TerminalCraftingPlanFlatStatic.java

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -211,27 +211,44 @@ public static <I> TerminalCraftingPlanFlatStatic<I> deserialize(HolderLookup.Pro
211211

212212
public static class Entry implements ITerminalCraftingPlanFlat.IEntry {
213213

214-
private final IPrototypedIngredient<?, ?> instance;
214+
private final List<IPrototypedIngredient<?, ?>> instances;
215215
private long quantityToCraft;
216216
private long quantityCrafting;
217217
private long quantityInStorage;
218218
private long quantityMissing;
219219

220-
public Entry(IPrototypedIngredient<?, ?> instance, long quantityToCraft, long quantityCrafting, long quantityInStorage, long quantityMissing) {
221-
this.instance = instance;
220+
public Entry(List<IPrototypedIngredient<?, ?>> instances, long quantityToCraft, long quantityCrafting, long quantityInStorage, long quantityMissing) {
221+
this.instances = instances;
222222
this.quantityToCraft = quantityToCraft;
223223
this.quantityCrafting = quantityCrafting;
224224
this.quantityInStorage = quantityInStorage;
225225
this.quantityMissing = quantityMissing;
226226
}
227227

228+
public Entry(List<IPrototypedIngredient<?, ?>> instances) {
229+
this(instances, 0, 0, 0, 0);
230+
}
231+
232+
/**
233+
* @deprecated Use {@link #Entry(List)} instead. TODO: rm in next major
234+
*/
235+
@Deprecated
228236
public Entry(IPrototypedIngredient<?, ?> instance) {
229-
this(instance, 0, 0, 0, 0);
237+
this(List.of(instance), 0, 0, 0, 0);
230238
}
231239

240+
/**
241+
* @deprecated Use {@link #getInstances()} instead. TODO: rm in next major
242+
*/
232243
@Override
244+
@Deprecated
233245
public IPrototypedIngredient<?, ?> getInstance() {
234-
return instance;
246+
return instances.get(0);
247+
}
248+
249+
@Override
250+
public List<IPrototypedIngredient<?, ?>> getInstances() {
251+
return instances;
235252
}
236253

237254
@Override
@@ -272,7 +289,11 @@ public void setQuantityMissing(long quantityMissing) {
272289

273290
public static CompoundTag serialize(HolderLookup.Provider lookupProvider, TerminalCraftingPlanFlatStatic.Entry entry) {
274291
CompoundTag tag = new CompoundTag();
275-
tag.put("instance", IPrototypedIngredient.serialize(lookupProvider, entry.getInstance()));
292+
ListTag instancesTag = new ListTag();
293+
for (IPrototypedIngredient<?, ?> instance : entry.getInstances()) {
294+
instancesTag.add(IPrototypedIngredient.serialize(lookupProvider, (PrototypedIngredient) instance));
295+
}
296+
tag.put("instances", instancesTag);
276297
tag.putLong("quantityToCraft", entry.getQuantityToCraft());
277298
tag.putLong("quantityCrafting", entry.getQuantityCrafting());
278299
tag.putLong("quantityInStorage", entry.getQuantityInStorage());
@@ -297,13 +318,21 @@ public static TerminalCraftingPlanFlatStatic.Entry deserialize(HolderLookup.Prov
297318
throw new IllegalArgumentException("Could not find a quantityMissing entry in the given tag");
298319
}
299320

300-
IPrototypedIngredient<?, ?> instance = IPrototypedIngredient.deserialize(lookupProvider, tag.getCompound("instance"));
321+
List<IPrototypedIngredient<?, ?>> instances = Lists.newArrayList();
322+
if (tag.contains("instances", Tag.TAG_LIST)) {
323+
ListTag instancesTag = tag.getList("instances", Tag.TAG_COMPOUND);
324+
for (Tag base : instancesTag) {
325+
instances.add(IPrototypedIngredient.deserialize(lookupProvider, (CompoundTag) base));
326+
}
327+
} else {
328+
instances.add(IPrototypedIngredient.deserialize(lookupProvider, tag.getCompound("instance"))); // TODO: rm in next major
329+
}
301330
long quantityToCraft = tag.getLong("quantityToCraft");
302331
long quantityCrafting = tag.getLong("quantityCrafting");
303332
long quantityInStorage = tag.getLong("quantityInStorage");
304333
long quantityMissing = tag.getLong("quantityMissing");
305334

306-
return new TerminalCraftingPlanFlatStatic.Entry(instance, quantityToCraft, quantityCrafting, quantityInStorage, quantityMissing);
335+
return new TerminalCraftingPlanFlatStatic.Entry(instances, quantityToCraft, quantityCrafting, quantityInStorage, quantityMissing);
307336
}
308337

309338
}

src/main/java/org/cyclops/integratedterminals/api/terminalstorage/crafting/TerminalCraftingPlanStatic.java

Lines changed: 61 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -177,25 +177,64 @@ public ITerminalCraftingPlanFlat<I> flatten() {
177177
}
178178

179179
public static class IndexedEntries {
180-
private final Map<IPrototypedIngredient<?, ?>, TerminalCraftingPlanFlatStatic.Entry> indexedEntries;
180+
/**
181+
* Entries indexed by a canonical list of prototype ingredients.
182+
* <p>
183+
* Each key is a list of {@link IPrototypedIngredient} where:
184+
* <ul>
185+
* <li>Every element has quantity 1.</li>
186+
* <li>Every element uses the exact-match-no-quantity condition.</li>
187+
* </ul>
188+
* The original (possibly non-normalized) alternatives list is stored inside the
189+
* {@link TerminalCraftingPlanFlatStatic.Entry} for rendering purposes.
190+
*/
191+
private final Map<List<IPrototypedIngredient<?, ?>>, TerminalCraftingPlanFlatStatic.Entry> indexedEntries;
181192

182193
public IndexedEntries() {
183194
this.indexedEntries = Maps.newHashMap();
184195
}
185196

186-
public TerminalCraftingPlanFlatStatic.Entry get(IPrototypedIngredient<?, ?> prototypedIngredient) {
187-
IPrototypedIngredient<?, ?> prototype = getPrototype(prototypedIngredient);
188-
return indexedEntries.computeIfAbsent(prototype, k -> new TerminalCraftingPlanFlatStatic.Entry(new PrototypedIngredient(prototypedIngredient.getComponent(), prototype.getPrototype(), prototypedIngredient.getCondition())));
189-
}
190-
191-
protected <T, M> IPrototypedIngredient<T, M> getPrototype(IPrototypedIngredient<T, M> prototypedIngredient) {
192-
IIngredientMatcher<T, M> matcher = prototypedIngredient.getComponent().getMatcher();
193-
return new PrototypedIngredient(prototypedIngredient.getComponent(), matcher.withQuantity(prototypedIngredient.getPrototype(), 1L), matcher.getExactMatchNoQuantityCondition());
194-
}
195-
196-
public static long getQuantity(IPrototypedIngredient<?, ?> prototypedIngredient) {
197-
IIngredientMatcher matcher = prototypedIngredient.getComponent().getMatcher();
198-
return matcher.getQuantity(prototypedIngredient.getPrototype());
197+
/**
198+
* Get (or create) the entry corresponding to the given list of alternative ingredients.
199+
*
200+
* @param prototypedIngredients A non-empty list of alternatives.
201+
* @return The corresponding flat plan entry.
202+
*/
203+
public TerminalCraftingPlanFlatStatic.Entry get(List<IPrototypedIngredient<?, ?>> prototypedIngredients) {
204+
List<IPrototypedIngredient<?, ?>> key = getPrototypes(prototypedIngredients);
205+
return indexedEntries.computeIfAbsent(key,
206+
k -> new TerminalCraftingPlanFlatStatic.Entry(k));
207+
}
208+
209+
/**
210+
* Build a canonical list of prototype ingredients for the given alternatives.
211+
* Quantities are normalized to 1 and the exact-match-no-quantity condition is used.
212+
*/
213+
protected List<IPrototypedIngredient<?, ?>> getPrototypes(List<IPrototypedIngredient<?, ?>> prototypedIngredients) {
214+
List<IPrototypedIngredient<?, ?>> result = new ArrayList<>(prototypedIngredients.size());
215+
for (IPrototypedIngredient<?, ?> ingredient : prototypedIngredients) {
216+
IIngredientMatcher matcher = ingredient.getComponent().getMatcher();
217+
result.add(new PrototypedIngredient(
218+
ingredient.getComponent(),
219+
matcher.withQuantity(ingredient.getPrototype(), 1L),
220+
matcher.getExactMatchNoQuantityCondition()));
221+
}
222+
return result;
223+
}
224+
225+
/**
226+
* Get the quantity associated with the given alternatives list.
227+
* <p>
228+
* This is derived from the first element, which is consistent with prior behaviour
229+
* when only a single prototype was available.
230+
*/
231+
public static long getQuantity(List<IPrototypedIngredient<?, ?>> prototypedIngredients) {
232+
if (prototypedIngredients.isEmpty()) {
233+
return 0;
234+
}
235+
IPrototypedIngredient<?, ?> first = prototypedIngredients.get(0);
236+
IIngredientMatcher matcher = first.getComponent().getMatcher();
237+
return matcher.getQuantity(first.getPrototype());
199238
}
200239

201240
public Collection<TerminalCraftingPlanFlatStatic.Entry> getEntries() {
@@ -212,8 +251,9 @@ protected static <I> void groupDependenciesByPrototype(IndexedEntries indexedEnt
212251

213252
// Determine outputs that are invalid or will be crafted
214253
for (IPrototypedIngredient<?, ?> output : plan.getOutputs()) {
215-
TerminalCraftingPlanFlatStatic.Entry entry = indexedEntries.get(output);
216-
long quantity = IndexedEntries.getQuantity(output);
254+
List<IPrototypedIngredient<?, ?>> outputs = List.of(output);
255+
TerminalCraftingPlanFlatStatic.Entry entry = indexedEntries.get(outputs);
256+
long quantity = IndexedEntries.getQuantity(outputs);
217257

218258
if (plan.getStatus() == TerminalCraftingJobStatus.ERROR
219259
|| plan.getStatus() == TerminalCraftingJobStatus.INVALID
@@ -238,16 +278,16 @@ protected static <I> void groupDependenciesByPrototype(IndexedEntries indexedEnt
238278

239279
// Determine storage ingredients
240280
for (IPrototypedIngredient<?, ?> output : plan.getBufferedIngredients()) {
241-
TerminalCraftingPlanFlatStatic.Entry entry = indexedEntries.get(output);
242-
long quantity = IndexedEntries.getQuantity(output);
281+
List<IPrototypedIngredient<?, ?>> outputs = List.of(output);
282+
TerminalCraftingPlanFlatStatic.Entry entry = indexedEntries.get(outputs);
283+
long quantity = IndexedEntries.getQuantity(outputs);
243284
entry.setQuantityInStorage(entry.getQuantityInStorage() + quantity);
244285
}
245286

246287
// Determine missing ingredients
247288
for (List<IPrototypedIngredient<?, ?>> outputVariants : plan.getLastMissingIngredients()) {
248-
IPrototypedIngredient<?, ?> output = outputVariants.stream().findFirst().get();
249-
TerminalCraftingPlanFlatStatic.Entry entry = indexedEntries.get(output);
250-
long quantity = IndexedEntries.getQuantity(output);
289+
TerminalCraftingPlanFlatStatic.Entry entry = indexedEntries.get(outputVariants);
290+
long quantity = IndexedEntries.getQuantity(outputVariants);
251291
entry.setQuantityMissing(entry.getQuantityMissing() + quantity * plan.getCraftingQuantity());
252292
}
253293

src/main/java/org/cyclops/integratedterminals/client/gui/container/component/GuiCraftingPlanFlat.java

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,10 @@ public void setFirstRow(int firstRow) {
102102
this.firstRow = Math.max(0, firstRow);
103103
}
104104

105+
protected int getTick() {
106+
return (int) Minecraft.getInstance().level.getGameTime() / TICK_DELAY;
107+
}
108+
105109
@Override
106110
public void renderWidget(GuiGraphics guiGraphics, int p_268034_, int p_268009_, float p_268085_) {
107111

@@ -133,15 +137,19 @@ private void drawElement(GuiGraphics guiGraphics, Element element, int x, int y,
133137

134138
int xOriginal = x;
135139

136-
// Draw instance
137-
IPrototypedIngredient<?, ?> output = element.getInstance();
138-
IngredientComponent<?, ?> ingredientComponent = output.getComponent();
139-
long quantity = ((IngredientComponent) ingredientComponent).getMatcher().getQuantity(output.getPrototype());
140-
int finalX = x;
141-
int finalY = y;
142-
ingredientComponent.getCapability(Capabilities.IngredientComponentTerminalStorageHandler.INGREDIENT)
143-
.ifPresent(h -> h.drawInstance(guiGraphics, output.getPrototype(), quantity,
144-
"", this.parentGui, layer, partialTick, finalX, finalY, mouseX, mouseY, null));
140+
// Draw instance (rotate over alternatives if present)
141+
List<IPrototypedIngredient<?, ?>> instances = element.getInstances();
142+
if (!instances.isEmpty()) {
143+
int tick = getTick();
144+
IPrototypedIngredient<?, ?> output = instances.get(tick % instances.size());
145+
IngredientComponent<?, ?> ingredientComponent = output.getComponent();
146+
long quantity = ((IngredientComponent) ingredientComponent).getMatcher().getQuantity(output.getPrototype());
147+
int finalX = x;
148+
int finalY = y;
149+
ingredientComponent.getCapability(Capabilities.IngredientComponentTerminalStorageHandler.INGREDIENT)
150+
.ifPresent(h -> h.drawInstance(guiGraphics, output.getPrototype(), quantity,
151+
"", this.parentGui, layer, partialTick, finalX, finalY, mouseX, mouseY, null));
152+
}
145153

146154
x = xOriginal + width - 50;
147155
if (layer == ContainerScreenTerminalStorage.DrawLayer.BACKGROUND) {
@@ -264,7 +272,7 @@ public static List<GuiCraftingPlanFlat.Element> getElements(ITerminalCraftingPla
264272

265273
protected static void addElements(ITerminalCraftingPlanFlat.IEntry craftingPlan, List<GuiCraftingPlanFlat.Element> elements) {
266274
elements.add(new Element(
267-
craftingPlan.getInstance(),
275+
craftingPlan.getInstances(),
268276
craftingPlan.getQuantityInStorage(),
269277
craftingPlan.getQuantityToCraft(),
270278
craftingPlan.getQuantityCrafting(),
@@ -283,23 +291,23 @@ protected void updateWidgetNarration(NarrationElementOutput p_259858_) {
283291

284292
public static class Element {
285293

286-
private final IPrototypedIngredient<?, ?> instance;
294+
private final List<IPrototypedIngredient<?, ?>> instances;
287295
private final long storageQuantity;
288296
private final long toCraftQuantity;
289297
private final long craftingQuantity;
290298
private final long missingQuantity;
291299

292-
public Element(IPrototypedIngredient<?, ?> instance,
300+
public Element(List<IPrototypedIngredient<?, ?>> instances,
293301
long storageQuantity, long toCraftQuantity, long craftingQuantity, long missingQuantity) {
294-
this.instance = instance;
302+
this.instances = instances;
295303
this.storageQuantity = storageQuantity;
296304
this.toCraftQuantity = toCraftQuantity;
297305
this.craftingQuantity = craftingQuantity;
298306
this.missingQuantity = missingQuantity;
299307
}
300308

301-
public IPrototypedIngredient<?, ?> getInstance() {
302-
return instance;
309+
public List<IPrototypedIngredient<?, ?>> getInstances() {
310+
return instances;
303311
}
304312

305313
public long getStorageQuantity() {

0 commit comments

Comments
 (0)