Skip to content

Commit d4a22dc

Browse files
committed
Fix a handful of QIO Crafting related bugs:
- Fixed a dupe bug caused by changes mojang made to remainders - Fixed breaking a QIO Dashboard not persisting crafting window contents - Fixed the QIO Portable Dashboard's Crafting Windows - Also fixed some issue with the formulaic assemblicator relating to the changes mojang made crafting input overall size
1 parent 124c9fa commit d4a22dc

18 files changed

+202
-97
lines changed

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,12 @@ of things that we haven't finished porting yet.
2828

2929
## Known Bugs/Things that aren't done being ported yet ##
3030
- Options in the Module Tweaker that have side effects don't currently have those side effects displayed
31-
- Fixed (Upcoming 10.6.2): Resistive Heaters do not keep their set Energy Usage when breaking and placing again
32-
- Fixed (Upcoming 10.6.2): Bins forget if they are locked and what they are locked too if broken and placed
3331
- When breaking and placing blocks that have fluid item input slots, they do not persist whether the item was actively being drained or whether it was being filled
34-
- The QIO Dashboard does not persist items stored in crafting windows when broken
35-
- The Portable QIO Dashboard's Crafting windows do not work at all. DO NOT TRY TO USE THEM
3632
- The Gravitational Modulation Unit does not currently provide flight
33+
- Fixed (Upcoming 10.6.2): Resistive Heaters do not keep their set Energy Usage when breaking and placing again
34+
- Fixed (Upcoming 10.6.2): Bins forget if they are locked and what they are locked too if broken and placed
35+
- Fixed (Upcoming 10.6.2): The QIO Dashboard does not persist items stored in crafting windows when broken
36+
- Fixed (Upcoming 10.6.2): The Portable QIO Dashboard's Crafting windows do not work at all. DO NOT TRY TO USE THEM
3737
- Probably many more things as we have done barely any testing so far
3838

3939
## Configuration ##

src/datagen/generated/mekanism/.cache/59eb3dbb5f86130e09b3c62d89b9525ee01cf52d

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/datagen/generated/mekanism/data/mekanism/loot_table/blocks/qio_dashboard.json

Lines changed: 2 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/main/java/mekanism/common/attachments/FormulaAttachment.java

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import com.mojang.serialization.Codec;
44
import com.mojang.serialization.codecs.RecordCodecBuilder;
5-
import java.util.ArrayList;
65
import java.util.Collections;
76
import java.util.List;
87
import java.util.Optional;
@@ -42,30 +41,23 @@ public static Optional<FormulaAttachment> existingFormula(ItemStack stack) {
4241
}
4342

4443
public static FormulaAttachment create(RecipeFormula formula) {
45-
List<ItemStack> stacks = new ArrayList<>(formula.craftingInput.size());
46-
for (ItemStack stack : formula.craftingInput.items()) {
47-
stacks.add(stack.copy());
48-
}
49-
return new FormulaAttachment(stacks, false);
44+
return new FormulaAttachment(formula.getCopy(true), false);
5045
}
5146

5247
public FormulaAttachment asInvalid() {
5348
if (invalid) {
5449
return this;
5550
}
56-
List<ItemStack> stacks = new ArrayList<>(inventory.size());
57-
for (ItemStack stack : inventory) {
58-
stacks.add(stack.copy());
59-
}
60-
return new FormulaAttachment(stacks, true);
51+
//Note: We don't have to copy the inventory as FormulaAttachment is immutable, so nothing should be mutating the backing stacks
52+
return new FormulaAttachment(inventory, true);
6153
}
6254

6355
public boolean isEmpty() {
6456
return inventory.stream().allMatch(ItemStack::isEmpty);
6557
}
6658

6759
public boolean hasItems() {
68-
return inventory.stream().anyMatch(slot -> !slot.isEmpty());
60+
return inventory.stream().anyMatch(stack -> !stack.isEmpty());
6961
}
7062

7163
@Override

src/main/java/mekanism/common/attachments/containers/item/ItemSlotsBuilder.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import mekanism.common.capabilities.Capabilities;
3131
import mekanism.common.capabilities.MultiTypeCapability;
3232
import mekanism.common.content.oredictionificator.OredictionificatorItemFilter;
33+
import mekanism.common.content.qio.IQIOCraftingWindowHolder;
3334
import mekanism.common.integration.energy.EnergyCompatUtils;
3435
import mekanism.common.inventory.slot.BasicInventorySlot;
3536
import mekanism.common.inventory.slot.EnergyInventorySlot;
@@ -80,12 +81,19 @@ public class ItemSlotsBuilder {
8081
private static final IBasicContainerCreator<ComponentBackedInventorySlot> FORMULA_SLOT_CREATOR = (type, attachedTo, containerIndex) -> new ComponentBackedInventorySlot(attachedTo,
8182
containerIndex, BasicInventorySlot.alwaysTrueBi, BasicInventorySlot.alwaysTrueBi, TileEntityFormulaicAssemblicator.FORMULA_SLOT_VALIDATOR);
8283

83-
//QOP drive slot
84+
//QIO drive slot
8485
//Note: As we don't have to update the presence of a drive or remove it from the frequency we can make do with just using a basic slot
8586
//TODO - 1.20.4: Evaluate if copy the notExternal is correct or do we want this to have some other checks
8687
private static final IBasicContainerCreator<ComponentBackedInventorySlot> QIO_DRIVE_SLOT_CREATOR = (type, attachedTo, containerIndex) -> new ComponentBackedInventorySlot(attachedTo,
8788
containerIndex, BasicInventorySlot.notExternal, BasicInventorySlot.notExternal, QIODriveSlot.IS_QIO_ITEM);
8889

90+
//QIO Dashboard Crafting WINDOW
91+
private static final IBasicContainerCreator<ComponentBackedInventorySlot> QIO_DASHBOARD_INPUT_SLOT_CREATOR = (type, attachedTo, containerIndex) -> new ComponentBackedInventorySlot(attachedTo,
92+
containerIndex, BasicInventorySlot.notExternal, BasicInventorySlot.alwaysTrueBi, BasicInventorySlot.alwaysTrue);
93+
//Note: We don't allow external means to modify this slot as it truthfully only exists to make logic easier
94+
private static final IBasicContainerCreator<ComponentBackedInventorySlot> QIO_DASHBOARD_OUTPUT_SLOT_CREATOR = (type, attachedTo, containerIndex) -> new ComponentBackedInventorySlot(attachedTo,
95+
containerIndex, BasicInventorySlot.internalOnly, BasicInventorySlot.internalOnly, BasicInventorySlot.alwaysTrue);
96+
8997
//EnergyInventorySlot
9098
//Note: As energy is untyped we don't have to do extra checks about what is currently stored or not on the attached stack
9199
private static final BiPredicate<@NotNull ItemStack, @NotNull AutomationType> FILL_CONVERT_ENERGY_SLOT_CAN_EXTRACT = (stack, automationType) ->
@@ -169,8 +177,10 @@ public ItemSlotsBuilder addQIODriveSlots(int count) {
169177
}
170178

171179
public ItemSlotsBuilder addQIODashboardSlots() {
172-
//TODO - 1.21: IMPLEMENT Figure out how to do this
173-
//new PortableQIODashboardInventory(null, stack).getSlots()
180+
for (byte window = 0; window < IQIOCraftingWindowHolder.MAX_CRAFTING_WINDOWS; window++) {
181+
addSlots(9, QIO_DASHBOARD_INPUT_SLOT_CREATOR);
182+
addSlot(QIO_DASHBOARD_OUTPUT_SLOT_CREATOR);
183+
}
174184
return this;
175185
}
176186

src/main/java/mekanism/common/attachments/qio/PortableDashboardContents.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22

33
import com.mojang.serialization.Codec;
44
import com.mojang.serialization.codecs.RecordCodecBuilder;
5+
import java.util.ArrayList;
56
import java.util.Collections;
67
import java.util.List;
78
import mekanism.api.SerializationConstants;
89
import mekanism.common.content.qio.IQIOCraftingWindowHolder;
10+
import net.minecraft.core.NonNullList;
911
import net.minecraft.network.RegistryFriendlyByteBuf;
1012
import net.minecraft.network.codec.StreamCodec;
1113
import net.minecraft.world.item.ItemStack;
@@ -14,6 +16,9 @@ public record PortableDashboardContents(List<ItemStack> contents) {
1416

1517
public static final int TOTAL_SLOTS = (3 * 3) * IQIOCraftingWindowHolder.MAX_CRAFTING_WINDOWS;
1618

19+
//TODO: Do we want to try and make this an empty list? It not being empty means it is easier to not serialize things when the windows become empty
20+
public static final PortableDashboardContents EMPTY = new PortableDashboardContents(NonNullList.withSize(TOTAL_SLOTS, ItemStack.EMPTY));
21+
1722
public static final Codec<PortableDashboardContents> CODEC = RecordCodecBuilder.create(instance -> instance.group(
1823
ItemStack.OPTIONAL_CODEC.listOf(TOTAL_SLOTS, TOTAL_SLOTS).fieldOf(SerializationConstants.ITEMS).forGetter(PortableDashboardContents::contents)
1924
).apply(instance, PortableDashboardContents::new));
@@ -25,6 +30,12 @@ public record PortableDashboardContents(List<ItemStack> contents) {
2530
contents = Collections.unmodifiableList(contents);
2631
}
2732

33+
public PortableDashboardContents with(int window, int index, ItemStack stack) {
34+
List<ItemStack> copy = new ArrayList<>(contents);
35+
copy.set(9 * window + index, stack);
36+
return new PortableDashboardContents(copy);
37+
}
38+
2839
@Override
2940
public boolean equals(Object o) {
3041
if (this == o) {

src/main/java/mekanism/common/content/assemblicator/RecipeFormula.java

Lines changed: 43 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package mekanism.common.content.assemblicator;
22

3-
import java.util.ArrayList;
43
import java.util.List;
54
import java.util.Objects;
65
import mekanism.api.inventory.IInventorySlot;
@@ -19,13 +18,15 @@
1918

2019
public class RecipeFormula {
2120

22-
public final CraftingInput craftingInput;
21+
public static final RecipeFormula EMPTY = new RecipeFormula();
22+
23+
public final CraftingInput.Positioned craftingInput;
2324
@Nullable
24-
public RecipeHolder<CraftingRecipe> recipe;
25+
public final RecipeHolder<CraftingRecipe> recipe;
2526

26-
public RecipeFormula() {
27-
//TODO - 1.21: Re-evaluate this
28-
craftingInput = CraftingInput.EMPTY;
27+
private RecipeFormula() {
28+
craftingInput = CraftingInput.Positioned.EMPTY;
29+
recipe = null;
2930
}
3031

3132
public RecipeFormula(Level world, FormulaAttachment attachment) {
@@ -34,7 +35,7 @@ public RecipeFormula(Level world, FormulaAttachment attachment) {
3435
recipe = getRecipeFromGrid(craftingInput, world);
3536
}
3637

37-
public RecipeFormula(Level world, CraftingInput craftingInput) {
38+
private RecipeFormula(Level world, CraftingInput.Positioned craftingInput) {
3839
this.craftingInput = craftingInput;
3940
recipe = getRecipeFromGrid(this.craftingInput, world);
4041
}
@@ -46,26 +47,33 @@ public RecipeFormula(Level world, List<IInventorySlot> craftingGridSlots) {
4647
}
4748

4849
public ItemStack getInputStack(int slot) {
49-
return craftingInput.getItem(slot);
50+
int row = slot / 3;
51+
int column = slot % 3;
52+
CraftingInput input = craftingInput.input();
53+
if (row < craftingInput.top() || row >= craftingInput.top() + input.height() ||
54+
column < craftingInput.left() || column >= craftingInput.left() + input.width()) {
55+
return ItemStack.EMPTY;
56+
}
57+
return input.getItem(column - craftingInput.left(), row - craftingInput.top());
5058
}
5159

5260
public boolean matches(Level world, List<IInventorySlot> craftingGridSlots) {
5361
if (recipe == null) {
5462
return false;
5563
}
5664
//Should always be a 3x3 grid for the size
57-
return recipe.value().matches(MekanismUtils.getCraftingInputSlots(3, 3, craftingGridSlots, true), world);
65+
return recipe.value().matches(MekanismUtils.getCraftingInputSlots(3, 3, craftingGridSlots, true).input(), world);
5866
}
5967

6068
//Must have matches be called before this and be true as it assumes that the dummy inventory was set by it
6169
public ItemStack assemble(RegistryAccess registryAccess) {
62-
return recipe == null ? ItemStack.EMPTY : recipe.value().assemble(craftingInput, registryAccess);
70+
return recipe == null ? ItemStack.EMPTY : recipe.value().assemble(craftingInput.input(), registryAccess);
6371
}
6472

6573
//Must have matches be called before this and be true as it assumes that the dummy inventory was set by it
6674
public NonNullList<ItemStack> getRemainingItems() {
6775
//Should never be null given the assumption matches is called first
68-
return recipe == null ? NonNullList.create() : recipe.value().getRemainingItems(craftingInput);
76+
return recipe == null ? NonNullList.create() : recipe.value().getRemainingItems(craftingInput.input());
6977
}
7078

7179
public boolean isIngredientInPos(Level world, ItemStack stack, int i) {
@@ -86,20 +94,20 @@ public boolean isIngredientInPos(Level world, ItemStack stack, int i) {
8694
return true;
8795
}
8896

89-
List<ItemStack> dummy = new ArrayList<>(craftingInput.items());
97+
List<ItemStack> dummy = getCopy(false);
9098
dummy.set(i, stack);
9199
return recipe.value().matches(CraftingInput.of(3, 3, dummy), world);
92100
}
93101

94102
public boolean isValidIngredient(Level world, ItemStack stack) {
95103
if (recipe != null) {
96-
for (ItemStack inputItem : craftingInput.items()) {
104+
for (ItemStack inputItem : craftingInput.input().items()) {
97105
//Short circuit if it is one of the items we already know about
98106
if (!inputItem.isEmpty() && ItemStack.isSameItemSameComponents(inputItem, stack)) {
99107
return true;
100108
}
101109
}
102-
List<ItemStack> dummy = new ArrayList<>(craftingInput.items());
110+
List<ItemStack> dummy = getCopy(false);
103111
for (int i = 0; i < 9; i++) {
104112
ItemStack inputItem = dummy.get(i);
105113
//Skip slots that aren't expected to be empty
@@ -128,15 +136,31 @@ public boolean isFormulaEqual(RecipeFormula formula) {
128136
return Objects.equals(formula.getRecipe(), getRecipe());
129137
}
130138

131-
//TODO - 1.21: Re-evaluate this entire class, in particular this with stack stuff. Should we be copying stacks when we do dummy based stuff?
132139
public RecipeFormula withStack(Level world, int index, ItemStack stack) {
133-
List<ItemStack> copy = new ArrayList<>(craftingInput.items());
140+
if (this == EMPTY && stack.isEmpty()) {
141+
return this;
142+
}
143+
List<ItemStack> copy = getCopy(false);
134144
copy.set(index, stack);
135-
return new RecipeFormula(world, CraftingInput.of(3, 3, copy));
145+
return new RecipeFormula(world, CraftingInput.ofPositioned(3, 3, copy));
136146
}
137147

138148
@Nullable
139-
private static RecipeHolder<CraftingRecipe> getRecipeFromGrid(CraftingInput input, Level world) {
140-
return MekanismRecipeType.getRecipeFor(RecipeType.CRAFTING, input, world).orElse(null);
149+
private static RecipeHolder<CraftingRecipe> getRecipeFromGrid(CraftingInput.Positioned input, Level world) {
150+
return MekanismRecipeType.getRecipeFor(RecipeType.CRAFTING, input.input(), world).orElse(null);
151+
}
152+
153+
public List<ItemStack> getCopy(boolean copyStacks) {
154+
CraftingInput input = craftingInput.input();
155+
List<ItemStack> stacks = NonNullList.withSize(9, ItemStack.EMPTY);
156+
for (int row = 0; row < input.height(); row++) {
157+
int shiftedRow = 3 * (craftingInput.top() + row);
158+
for (int column = 0; column < input.width(); column++) {
159+
int index = shiftedRow + craftingInput.left() + column;
160+
ItemStack stack = input.getItem(column, row);
161+
stacks.set(index, copyStacks ? stack.copy() : stack);
162+
}
163+
}
164+
return stacks;
141165
}
142166
}

src/main/java/mekanism/common/attachments/qio/PortableQIODashboardInventory.java renamed to src/main/java/mekanism/common/content/qio/PortableQIODashboardInventory.java

Lines changed: 17 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,52 @@
1-
package mekanism.common.attachments.qio;
1+
package mekanism.common.content.qio;
22

33
import java.util.ArrayList;
44
import java.util.List;
55
import mekanism.api.inventory.IInventorySlot;
66
import mekanism.common.attachments.FrequencyAware;
7-
import mekanism.common.content.qio.IQIOCraftingWindowHolder;
8-
import mekanism.common.content.qio.QIOCraftingWindow;
9-
import mekanism.common.content.qio.QIOFrequency;
7+
import mekanism.common.attachments.qio.PortableDashboardContents;
108
import mekanism.common.registries.MekanismDataComponents;
119
import net.minecraft.world.item.ItemStack;
1210
import net.minecraft.world.level.Level;
1311
import org.jetbrains.annotations.Nullable;
1412

15-
//TODO - 1.21: Move this to a different package?
1613
public class PortableQIODashboardInventory implements IQIOCraftingWindowHolder {
1714

1815
private final QIOCraftingWindow[] craftingWindows;
1916
private final List<IInventorySlot> slots;
2017
private final ItemStack stack;
2118
@Nullable
2219
private final Level level;
20+
private boolean initializing;
2321

2422
public PortableQIODashboardInventory(@Nullable Level level, ItemStack stack) {
2523
this.stack = stack;
2624
this.level = level;
2725
List<IInventorySlot> slots = new ArrayList<>();
2826
craftingWindows = new QIOCraftingWindow[MAX_CRAFTING_WINDOWS];
29-
PortableDashboardContents contents = stack.get(MekanismDataComponents.QIO_DASHBOARD);
27+
List<ItemStack> contents = stack.getOrDefault(MekanismDataComponents.QIO_DASHBOARD, PortableDashboardContents.EMPTY).contents();
28+
initializing = true;
3029
for (int tableIndex = 0; tableIndex < craftingWindows.length; tableIndex++) {
31-
QIOCraftingWindow craftingWindow = new QIOCraftingWindow(this, (byte) tableIndex);
30+
int finalTableIndex = tableIndex;
31+
QIOCraftingWindow craftingWindow = new QIOCraftingWindow(this, (byte) tableIndex, slot -> () -> {
32+
//Skip contents change handling until we actually have our crafting window updated
33+
if (!initializing) {
34+
ItemStack stored = craftingWindows[finalTableIndex].getInputSlot(slot).getStack().copy();
35+
PortableDashboardContents content = stack.getOrDefault(MekanismDataComponents.QIO_DASHBOARD, PortableDashboardContents.EMPTY);
36+
stack.set(MekanismDataComponents.QIO_DASHBOARD, content.with(finalTableIndex, slot, stored));
37+
}
38+
});
3239
craftingWindows[tableIndex] = craftingWindow;
3340
for (int slot = 0; slot < 9; slot++) {
3441
IInventorySlot inputSlot = craftingWindow.getInputSlot(slot);
3542
slots.add(inputSlot);
36-
if (contents != null) {
37-
//Note: setStack will ensure the stack is copied
38-
inputSlot.setStack(contents.contents().get(tableIndex * 9 + slot));
39-
}
43+
//Note: setStack will ensure the stack is copied
44+
inputSlot.setStack(contents.get(tableIndex * 9 + slot));
4045
}
4146
slots.add(craftingWindow.getOutputSlot());
4247
}
4348
this.slots = List.copyOf(slots);
44-
//TODO - 1.21: Is this still necessary given we are doing it from the constructor?
45-
//Force refresh the recipe
46-
for (QIOCraftingWindow craftingWindow : craftingWindows) {
47-
craftingWindow.invalidateRecipe();
48-
}
49+
initializing = false;
4950
}
5051

5152
public List<IInventorySlot> getSlots() {
@@ -77,12 +78,5 @@ public QIOFrequency getFrequency() {
7778

7879
@Override
7980
public void onContentsChanged() {
80-
List<ItemStack> stacks = new ArrayList<>(PortableDashboardContents.TOTAL_SLOTS);
81-
for (QIOCraftingWindow craftingWindow : craftingWindows) {
82-
for (int slot = 0; slot < 9; slot++) {
83-
stacks.add(craftingWindow.getInputSlot(slot).getStack().copy());
84-
}
85-
}
86-
stack.set(MekanismDataComponents.QIO_DASHBOARD, new PortableDashboardContents(stacks));
8781
}
8882
}

0 commit comments

Comments
 (0)