diff --git a/.github/workflows/branches.yml b/.github/workflows/branches.yml index 1f9cc0224..4ac445b70 100644 --- a/.github/workflows/branches.yml +++ b/.github/workflows/branches.yml @@ -21,6 +21,8 @@ jobs: with: path: ~/.gradle/caches key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }} + - name: Clean gradle + run: ./gradlew clean --no-daemon - name: Validate no assets run: test ! -d ./src/generated - name: Generate assets @@ -28,6 +30,6 @@ jobs: with: run: ./gradlew runData --no-daemon - name: Validate assets - run: test -d ./src/generated + run: test -d ./src/generated -a -f ./src/generated/resources/.cache/cache - name: Build with Gradle run: ./gradlew build --no-daemon diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index f53b0b928..d55923846 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -21,6 +21,8 @@ jobs: with: path: ~/.gradle/caches key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }} + - name: Clean gradle + run: ./gradlew clean --no-daemon - name: Validate no assets run: test ! -d ./src/generated - name: Generate assets @@ -28,6 +30,6 @@ jobs: with: run: ./gradlew runData --no-daemon - name: Validate assets - run: test -d ./src/generated + run: test -d ./src/generated -a -f ./src/generated/resources/.cache/cache - name: Build with Gradle run: ./gradlew build --no-daemon diff --git a/.github/workflows/pull_requests.yml b/.github/workflows/pull_requests.yml index 16847d1fc..397ea6c5d 100644 --- a/.github/workflows/pull_requests.yml +++ b/.github/workflows/pull_requests.yml @@ -21,6 +21,8 @@ jobs: with: path: ~/.gradle/caches key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }} + - name: Clean gradle + run: ./gradlew clean --no-daemon - name: Validate no assets run: test ! -d ./src/generated - name: Generate assets @@ -28,7 +30,7 @@ jobs: with: run: ./gradlew runData --no-daemon - name: Validate assets - run: test -d ./src/generated + run: test -d ./src/generated -a -f ./src/generated/resources/.cache/cache - name: Build with Gradle env: PR_NUMBER: ${{ github.event.pull_request.number }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b787cd4ee..b450879f2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -35,7 +35,7 @@ jobs: with: run: ./gradlew runData --no-daemon - name: Validate assets - run: test -d ./src/generated + run: test -d ./src/generated -a -f ./src/generated/resources/.cache/cache - name: Build with Gradle env: KEY_STORE_PASS: ${{ secrets.KEY_STORE_PASS }} diff --git a/README.md b/README.md index 270e9e5c8..daf3b6c0b 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ A Mod about Matter, Energy and using them to conquer the world.. ## Downloads -Downloads can be found on [CurseForge](http://www.curse.com/mc-mods/minecraft/223794-applied-energistics-2) or on the [official website](http://ae-mod.info/Downloads/). +Downloads can be found on [CurseForge](https://www.curseforge.com/minecraft/mc-mods/applied-energistics-2) or on the [official website](http://ae-mod.info/Downloads/). ## Installation @@ -103,7 +103,7 @@ However the dependency might need to be wrapped in `fg.deobf()` like `runtimeOnl 1. Clone this repository via - SSH `git clone git@github.com:AppliedEnergistics/Applied-Energistics-2.git` or - HTTPS `git clone https://github.com/AppliedEnergistics/Applied-Energistics-2.git` -2. Build using the gradle `build` task. Jar will be in `build/libs` +2. Build using the `gradlew runData build` command. Jar will be in `build/libs` 3. For core developer: Setup IDE - IntelliJ: Import as gradle project - Eclipse: Import as gradle project or execute gradle task `eclipse` and potentially `genEclipseRuns` @@ -132,12 +132,13 @@ Here are a few things to keep in mind that will help get your PR approved. * SSH `git clone git@github.com:/Applied-Energistics-2.git` or * HTTPS `git clone https://github.com//Applied-Energistics-2.git` 3. Change code base -4. Add changes to git `git add -A` -5. Commit changes to your clone `git commit -m ""` -6. Push to your fork `git push` -7. Create a Pull-Request on GitHub -8. Wait for review -9. Squash commits for cleaner history +4. Run `gradlew spotlessApply` to apply automatic code formatting +5. Add changes to git `git add -A` +6. Commit changes to your clone `git commit -m ""` +7. Push to your fork `git push` +8. Create a Pull-Request on GitHub +9. Wait for review +10. Squash commits for cleaner history If you are only doing single file pull requests, GitHub supports using a quick way without the need of cloning your fork. Also read up about [synching](https://help.github.com/articles/syncing-a-fork) if you plan to contribute on regular basis. diff --git a/gradle.properties b/gradle.properties index b8fcb6a9a..5d718fee0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -30,6 +30,6 @@ fabric_kotlin_version=1.3.61+build.1 ######################################################### # Deployment # ######################################################### -website_version=1.15.2 -curse_versions=1.15.2 +website_version=1.16.1 +curse_versions=1.16.1 curseforge_project=223794 diff --git a/gradle/scripts/dependencies.gradle b/gradle/scripts/dependencies.gradle index b24a7ea5d..064d84dde 100644 --- a/gradle/scripts/dependencies.gradle +++ b/gradle/scripts/dependencies.gradle @@ -23,51 +23,6 @@ repositories { name 'modmaven' url "https://modmaven.k-4u.nl/" } - - maven { - name "Mobius" - url "http://mobiusstrip.eu/maven" - } - - maven { - name = "JEI repo" - url "http://dvs1.progwml6.com/files/maven" - } - - maven { - name = "Tesla repo" - url "http://maven.mcmoddev.com" - } - - maven { - name = "IC2 repo" - url = "http://maven.ic2.player.to" - } - - maven { - name = "CoFH Maven" - url = "http://maven.covers1624.net" - } - - maven { // TheOneProbe - name 'tterrag maven' - url "http://maven.tterrag.com/" - } - - maven { - name 'mcjty' - url "http://maven.k-4u.nl/" - } - - maven { // CraftTweaker - name 'jared maven' - url "http://maven.blamejared.com/" - } - - maven { - name = "CurseForge" - url = "https://minecraft.curseforge.com/api/maven/" - } } dependencies { @@ -80,7 +35,7 @@ dependencies { // Runtime, Mods runtimeOnly fg.deobf("mezz.jei:jei-${minecraft_version}:${jei_version}") runtimeOnly fg.deobf("mcjty.theoneprobe:TheOneProbe-${minecraft_release}:${minecraft_release}-${top_version}") - runtimeOnly fg.deobf("team.chisel.ctm:CTM:${ctm_version}") + //runtimeOnly fg.deobf("team.chisel.ctm:CTM:${ctm_version}") // unit test dependencies testCompile "junit:junit:4.13" diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index deedc7fa5..0d4a95168 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/src/api/java/appeng/api/networking/crafting/ICraftingPatternDetails.java b/src/api/java/appeng/api/networking/crafting/ICraftingPatternDetails.java index f2ac98821..8a82fa609 100644 --- a/src/api/java/appeng/api/networking/crafting/ICraftingPatternDetails.java +++ b/src/api/java/appeng/api/networking/crafting/ICraftingPatternDetails.java @@ -23,6 +23,8 @@ package appeng.api.networking.crafting; +import java.util.List; + import net.minecraft.inventory.CraftingInventory; import net.minecraft.item.ItemStack; import net.minecraft.world.World; @@ -60,24 +62,65 @@ public interface ICraftingPatternDetails { boolean isCraftable(); /** + * Equal itemstacks will be aggregated into one, respectively 3*64 will be + * returned as one stack of 192, up to 576 of a single type. + *

+ * This should be the preferred way to deal with the list of inputs. + *

+ * The list will be sorted in descending order by stack size. However there is + * no guarantee about maintaining the placement order of the inputs in case of + * equal values. + * + * @return an immutable list of inputs without nulls + */ + List getInputs(); + + /** + * Equal itemstacks will be aggregated into one, respectively 2*32 will be + * returned as one stack of 64, up to 192 of a single type. + *

+ * This should be the preferred way to deal with the list of outputs. + *

+ * + * The list will be sorted in descending order by stack size. However there is + * no guarantee about maintaining the placement order of the outputs in case of + * equal values. + * + * @return an immutable list of outputs without nulls + */ + List getOutputs(); + + /** + * A sparse list representing the placement order of a crafting grid, left to + * right, then top to bottom. + *

+ * Only use when absolutely necessary, always prefer + * {@link ICraftingPatternDetails#getInputs()} + *

+ * This will contain exactly 9 entries. + *

+ * This can return a copy from the internal structure, so there are no + * guarantees about modifications. + * * @return a list of the inputs, will include nulls. */ - IAEItemStack[] getInputs(); - - /** - * @return a list of the inputs, will be clean - */ - IAEItemStack[] getCondensedInputs(); - - /** - * @return a list of the outputs, will be clean - */ - IAEItemStack[] getCondensedOutputs(); + IAEItemStack[] getSparseInputs(); /** + * A sparse list representing the placement order of the respective output + * slots. + *

+ * Only use when absolutely necessary, always prefer + * {@link ICraftingPatternDetails#getOutputs()} + *

+ * This will either contain 1 entry for crafting patterns or 3 for processing. + *

+ * This can return a copy from the internal structure, so there are no + * guarantees about modifications. + * * @return a list of the outputs, will include nulls. */ - IAEItemStack[] getOutputs(); + IAEItemStack[] getSparseOutputs(); /** * @return if this pattern is enabled to support substitutions. diff --git a/src/api/java/appeng/api/parts/IPartHost.java b/src/api/java/appeng/api/parts/IPartHost.java index d71506028..ae0f1d9ab 100644 --- a/src/api/java/appeng/api/parts/IPartHost.java +++ b/src/api/java/appeng/api/parts/IPartHost.java @@ -30,7 +30,7 @@ import net.minecraft.item.ItemStack; import net.minecraft.block.entity.BlockEntity; import net.minecraft.util.math.Direction; import net.minecraft.util.Hand; -import net.minecraft.util.math.Vec3d; +import net.minecraft.util.math.vector.Vector3d; import appeng.api.util.AEColor; import appeng.api.util.AEPartLocation; @@ -146,7 +146,7 @@ public interface IPartHost extends ICustomCableConnection { * * @return a new SelectedPart, this is never null. */ - SelectedPart selectPart(Vec3d pos); + SelectedPart selectPart(Vector3d pos); /** * can be used by parts to trigger the tile or part to save. diff --git a/src/main/java/appeng/client/gui/implementations/CraftingStatusScreen.java b/src/main/java/appeng/client/gui/implementations/CraftingStatusScreen.java index 466162021..792d4a7be 100644 --- a/src/main/java/appeng/client/gui/implementations/CraftingStatusScreen.java +++ b/src/main/java/appeng/client/gui/implementations/CraftingStatusScreen.java @@ -22,10 +22,13 @@ package appeng.client.gui.implementations; +import com.mojang.blaze3d.matrix.MatrixStack; + import net.minecraft.client.gui.widget.ButtonWidget; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.entity.player.PlayerInventory; import net.minecraft.text.Text; +import net.minecraft.util.text.StringTextComponent; import appeng.container.implementations.CraftingStatusContainer; import appeng.core.localization.GuiText; @@ -81,7 +84,7 @@ public class CraftingStatusScreen extends CraftingCPUScreen extends AEBas @Override public boolean mouseClicked(final double xCoord, final double yCoord, final int btn) { if (this.searchField.mouseClicked(xCoord, yCoord, btn)) { - if (btn == 1 && this.searchField.isMouseOver(xCoord, yCoord)) { - this.searchField.setText(""); - this.repo.setSearchString(""); - this.repo.updateView(); - this.setScrollBar(); - } + return true; + } + + // Right-clicking on the search field should clear it + if (this.searchField.isMouseOver(xCoord, yCoord) && btn == 1) { + this.searchField.setText(""); + this.repo.setSearchString(""); + this.repo.updateView(); + this.setScrollBar(); return true; } diff --git a/src/main/java/appeng/client/gui/widgets/NumberBox.java b/src/main/java/appeng/client/gui/widgets/NumberBox.java index cb1bd742f..24240d076 100644 --- a/src/main/java/appeng/client/gui/widgets/NumberBox.java +++ b/src/main/java/appeng/client/gui/widgets/NumberBox.java @@ -38,7 +38,7 @@ public class NumberBox extends TextFieldWidget { final Class type, LongConsumer changeListener) { super(fontRenderer, x, y, width, height, new LiteralText("0")); this.setText("0"); - setChangedListener(this::onTextChanged); + setChangedListener(this::handleTextChanged); this.lastValue = 0; this.changeListener = changeListener; if (type == int.class || type == Integer.class) { @@ -48,7 +48,11 @@ public class NumberBox extends TextFieldWidget { } } - private void onTextChanged(String text) { + // NOTE: This method MUST NOT BE NAMED like a method in the superclass because + // of a ForgeGradle remapping bug + // since it'll not remap this method's name, but it will remap the name of the + // method reference above. + private void handleTextChanged(String text) { if (text.isEmpty()) { setText("0"); // Will call onTextChanged recursively return; diff --git a/src/main/java/appeng/container/implementations/PatternTermContainer.java b/src/main/java/appeng/container/implementations/PatternTermContainer.java index bb9fc3c00..7e913b3e5 100644 --- a/src/main/java/appeng/container/implementations/PatternTermContainer.java +++ b/src/main/java/appeng/container/implementations/PatternTermContainer.java @@ -273,21 +273,13 @@ public class PatternTermContainer extends MEMonitorableContainer return new ItemStack[] { out }; } } else { - final List list = new ArrayList<>(3); - boolean hasValue = false; + final ItemStack[] list = new ItemStack[3]; - for (final OptionalFakeSlot outputSlot : this.outputSlots) { - final ItemStack out = outputSlot.getStack(); - - if (!out.isEmpty() && out.getCount() > 0) { - list.add(out); - hasValue = true; - } - } - - if (hasValue) { - return list.toArray(new ItemStack[list.size()]); + for (int i = 0; i < this.outputSlots.length; i++) { + final ItemStack out = this.outputSlots[i].getStack(); + list[i] = out; } + return list; } return null; diff --git a/src/main/java/appeng/core/AEConfig.java b/src/main/java/appeng/core/AEConfig.java index 760dc7403..3538c1610 100644 --- a/src/main/java/appeng/core/AEConfig.java +++ b/src/main/java/appeng/core/AEConfig.java @@ -89,7 +89,7 @@ public final class AEConfig { private boolean useColoredCraftingStatus; private boolean disableColoredCableRecipesInJEI; private int craftingCalculationTimePerTick; - private PowerUnits selectedPowerUnit; + private PowerUnits selectedPowerUnit = PowerUnits.AE; // GUI Buttons private final int[] craftByStacks = new int[4]; diff --git a/src/main/java/appeng/core/sync/BasePacket.java b/src/main/java/appeng/core/sync/BasePacket.java index c4041d7c8..21ecb4675 100644 --- a/src/main/java/appeng/core/sync/BasePacket.java +++ b/src/main/java/appeng/core/sync/BasePacket.java @@ -32,6 +32,14 @@ import net.minecraft.util.Identifier; public abstract class BasePacket { + /** + * Sadly {@link PacketBuffer#readString()} gets inlined by Proguard which means + * it's not available on the Server. This field has the default string length + * that is used for writeString, which then also should be used for readString + * when it has no special length requirements. + */ + public static final int MAX_STRING_LENGTH = 32767; + // KEEP THIS SHORT. It's serialized as a string! public static final Identifier CHANNEL = new Identifier("ae2:m"); diff --git a/src/main/java/appeng/core/sync/packets/ConfigValuePacket.java b/src/main/java/appeng/core/sync/packets/ConfigValuePacket.java index 6a6f11e15..f9709fe48 100644 --- a/src/main/java/appeng/core/sync/packets/ConfigValuePacket.java +++ b/src/main/java/appeng/core/sync/packets/ConfigValuePacket.java @@ -45,8 +45,8 @@ public class ConfigValuePacket extends BasePacket { private final String Value; public ConfigValuePacket(final PacketByteBuf stream) { - this.Name = stream.readString(); - this.Value = stream.readString(); + this.Name = stream.readString(MAX_STRING_LENGTH); + this.Value = stream.readString(MAX_STRING_LENGTH); } // api diff --git a/src/main/java/appeng/crafting/CraftingTreeNode.java b/src/main/java/appeng/crafting/CraftingTreeNode.java index 67b5382d1..5b636d5dd 100644 --- a/src/main/java/appeng/crafting/CraftingTreeNode.java +++ b/src/main/java/appeng/crafting/CraftingTreeNode.java @@ -85,7 +85,7 @@ public class CraftingTreeNode { } boolean notRecursive(final ICraftingPatternDetails details) { - IAEItemStack[] o = details.getCondensedOutputs(); + Collection o = details.getOutputs(); for (final IAEItemStack i : o) { if (i.equals(this.what)) { @@ -93,7 +93,7 @@ public class CraftingTreeNode { } } - o = details.getCondensedInputs(); + o = details.getInputs(); for (final IAEItemStack i : o) { if (i.equals(this.what)) { diff --git a/src/main/java/appeng/crafting/CraftingTreeProcess.java b/src/main/java/appeng/crafting/CraftingTreeProcess.java index fabcc92fa..d3517eded 100644 --- a/src/main/java/appeng/crafting/CraftingTreeProcess.java +++ b/src/main/java/appeng/crafting/CraftingTreeProcess.java @@ -62,10 +62,10 @@ public class CraftingTreeProcess { final World world = job.getWorld(); if (details.isCraftable()) { - final IAEItemStack[] list = details.getInputs(); + final IAEItemStack[] list = details.getSparseInputs(); final CraftingInventory ic = new CraftingInventory(new ContainerNull(), 3, 3); - final IAEItemStack[] is = details.getInputs(); + final IAEItemStack[] is = details.getSparseInputs(); for (int x = 0; x < ic.size(); x++) { ic.setStack(x, is[x] == null ? ItemStack.EMPTY : is[x].createItemStack()); } @@ -81,11 +81,11 @@ public class CraftingTreeProcess { } } - for (final IAEItemStack part : details.getCondensedInputs()) { + for (final IAEItemStack part : details.getInputs()) { final ItemStack g = part.createItemStack(); boolean isAnInput = false; - for (final IAEItemStack a : details.getCondensedOutputs()) { + for (final IAEItemStack a : details.getOutputs()) { if (!g.isEmpty() && a != null && a.equals(g)) { isAnInput = true; } @@ -113,7 +113,7 @@ public class CraftingTreeProcess { } else { // this is minor different then below, this slot uses the pattern, but kinda // fudges it. - for (final IAEItemStack part : details.getCondensedInputs()) { + for (final IAEItemStack part : details.getInputs()) { for (int x = 0; x < list.length; x++) { final IAEItemStack comparePart = list[x]; if (part != null && part.equals(comparePart)) { @@ -126,11 +126,11 @@ public class CraftingTreeProcess { } } } else { - for (final IAEItemStack part : details.getCondensedInputs()) { + for (final IAEItemStack part : details.getInputs()) { final ItemStack g = part.createItemStack(); boolean isAnInput = false; - for (final IAEItemStack a : details.getCondensedOutputs()) { + for (final IAEItemStack a : details.getOutputs()) { if (!g.isEmpty() && a != null && a.equals(g)) { isAnInput = true; } @@ -141,7 +141,7 @@ public class CraftingTreeProcess { } } - for (final IAEItemStack part : details.getCondensedInputs()) { + for (final IAEItemStack part : details.getInputs()) { this.nodes.put(new CraftingTreeNode(cc, job, part.copy(), this, -1, depth + 1), part.getStackSize()); } } @@ -208,7 +208,7 @@ public class CraftingTreeProcess { // assume its possible. // add crafting results.. - for (final IAEItemStack out : this.details.getCondensedOutputs()) { + for (final IAEItemStack out : this.details.getOutputs()) { final IAEItemStack o = out.copy(); o.setStackSize(o.getStackSize() * i); inv.injectItems(o, Actionable.MODULATE, src); @@ -227,7 +227,7 @@ public class CraftingTreeProcess { } IAEItemStack getAmountCrafted(IAEItemStack what2) { - for (final IAEItemStack is : this.details.getCondensedOutputs()) { + for (final IAEItemStack is : this.details.getOutputs()) { if (is.equals(what2)) { what2 = what2.copy(); what2.setStackSize(is.getStackSize()); @@ -236,7 +236,7 @@ public class CraftingTreeProcess { } // more fuzzy! - for (final IAEItemStack is : this.details.getCondensedOutputs()) { + for (final IAEItemStack is : this.details.getOutputs()) { if (is.getItem() == what2.getItem() && (is.getItem().isDamageable() || is.getItemDamage() == what2.getItemDamage())) { what2 = is.copy(); diff --git a/src/main/java/appeng/fluids/client/gui/FluidTerminalScreen.java b/src/main/java/appeng/fluids/client/gui/FluidTerminalScreen.java index 32daae137..de70793a4 100644 --- a/src/main/java/appeng/fluids/client/gui/FluidTerminalScreen.java +++ b/src/main/java/appeng/fluids/client/gui/FluidTerminalScreen.java @@ -263,12 +263,15 @@ public class FluidTerminalScreen extends AEBaseMEScreen @Override public boolean mouseClicked(final double xCoord, final double yCoord, final int btn) { if (this.searchField.mouseClicked(xCoord, yCoord, btn)) { - if (btn == 1 && this.searchField.isMouseOver(xCoord, yCoord)) { - this.searchField.setText(""); - this.repo.setSearchString(""); - this.repo.updateView(); - this.setScrollBar(); - } + return true; + } + + // Right-clicking on the search field should clear it + if (this.searchField.isMouseOver(xCoord, yCoord) && btn == 1) { + this.searchField.setText(""); + this.repo.setSearchString(""); + this.repo.updateView(); + this.setScrollBar(); return true; } @@ -329,4 +332,5 @@ public class FluidTerminalScreen extends AEBaseMEScreen protected String getBackground() { return "guis/terminal.png"; } + } diff --git a/src/main/java/appeng/fluids/parts/FluidLevelEmitterPart.java b/src/main/java/appeng/fluids/parts/FluidLevelEmitterPart.java index ba3e67395..da0cfa754 100644 --- a/src/main/java/appeng/fluids/parts/FluidLevelEmitterPart.java +++ b/src/main/java/appeng/fluids/parts/FluidLevelEmitterPart.java @@ -11,7 +11,7 @@ import net.minecraft.particle.DustParticleEffect; import net.minecraft.util.Hand; import net.minecraft.util.Identifier; import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Vec3d; +import net.minecraft.util.math.vector.Vector3d; import net.minecraft.world.World; import appeng.api.config.RedstoneMode; @@ -281,7 +281,7 @@ public class FluidLevelEmitterPart extends UpgradeablePart } @Override - public boolean onPartActivate(final PlayerEntity player, final Hand hand, final Vec3d pos) { + public boolean onPartActivate(final PlayerEntity player, final Hand hand, final Vector3d pos) { if (Platform.isServer()) { ContainerOpener.openContainer(FluidLevelEmitterContainer.TYPE, player, ContainerLocator.forPart(this)); } diff --git a/src/main/java/appeng/helpers/CraftingPatternDetails.java b/src/main/java/appeng/helpers/CraftingPatternDetails.java index d9dc95d28..c87857132 100644 --- a/src/main/java/appeng/helpers/CraftingPatternDetails.java +++ b/src/main/java/appeng/helpers/CraftingPatternDetails.java @@ -19,13 +19,17 @@ package appeng.helpers; import java.util.ArrayList; -import java.util.HashMap; +import java.util.Collection; +import java.util.Comparator; import java.util.HashSet; import java.util.List; -import java.util.Map; +import java.util.Objects; import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; import net.minecraft.inventory.CraftingInventory; import net.minecraft.item.Item; @@ -46,14 +50,21 @@ import appeng.util.Platform; public class CraftingPatternDetails implements ICraftingPatternDetails, Comparable { + private static final int ALL_INPUT_LIMIT = 9; + private static final int CRAFTING_OUTPUT_LIMIT = 1; + private static final int PROCESSING_OUTPUT_LIMIT = 3; + + private static final Comparator COMPARE_BY_STACKSIZE = (left, right) -> Long + .compare(right.getStackSize(), left.getStackSize()); + private final CraftingInventory crafting = new CraftingInventory(new ContainerNull(), 3, 3); private final CraftingInventory testFrame = new CraftingInventory(new ContainerNull(), 3, 3); private final ItemStack correctOutput; private final CraftingRecipe standardRecipe; - private final IAEItemStack[] condensedInputs; - private final IAEItemStack[] condensedOutputs; - private final IAEItemStack[] inputs; - private final IAEItemStack[] outputs; + private final List inputs; + private final List outputs; + private final IAEItemStack[] sparseInputs; + private final IAEItemStack[] sparseOutputs; private final boolean isCraftable; private final boolean canSubstitute; private final Set failCache = new HashSet<>(); @@ -79,7 +90,7 @@ public class CraftingPatternDetails implements ICraftingPatternDetails, Comparab final List in = new ArrayList<>(); final List out = new ArrayList<>(); - for (int x = 0; x < 9; x++) { + for (int x = 0; x < ALL_INPUT_LIMIT; x++) { final IAEItemStack ais = ingredients.get(x); final ItemStack gs = ais != null ? ais.createItemStack() : ItemStack.EMPTY; @@ -109,70 +120,19 @@ public class CraftingPatternDetails implements ICraftingPatternDetails, Comparab this.standardRecipe = null; this.correctOutput = ItemStack.EMPTY; - for (int x = 0; x < 3; x++) { + for (int x = 0; x < PROCESSING_OUTPUT_LIMIT; x++) { final IAEItemStack ais = products.get(x); - final ItemStack gs = ais.createItemStack(); - if (!gs.isEmpty()) { - out.add(ais.copy()); - } + out.add(ais != null ? ais.copy() : null); } } - this.inputs = in.toArray(new IAEItemStack[0]); - this.outputs = out.toArray(new IAEItemStack[0]); + final int outputLength = this.isCraftable ? CRAFTING_OUTPUT_LIMIT : PROCESSING_OUTPUT_LIMIT; + this.sparseInputs = in.toArray(new IAEItemStack[ALL_INPUT_LIMIT]); + this.sparseOutputs = out.toArray(new IAEItemStack[outputLength]); - final Map tmpOutputs = new HashMap<>(); - - for (final IAEItemStack io : this.outputs) { - if (io == null) { - continue; - } - - final IAEItemStack g = tmpOutputs.get(io); - - if (g == null) { - tmpOutputs.put(io, io.copy()); - } else { - g.add(io); - } - } - - final Map tmpInputs = new HashMap<>(); - - for (final IAEItemStack io : this.inputs) { - if (io == null) { - continue; - } - - final IAEItemStack g = tmpInputs.get(io); - - if (g == null) { - tmpInputs.put(io, io.copy()); - } else { - g.add(io); - } - } - - if (tmpOutputs.isEmpty() || tmpInputs.isEmpty()) { - throw new IllegalStateException("No pattern here!"); - } - - this.condensedInputs = new IAEItemStack[tmpInputs.size()]; - int offset = 0; - - for (final IAEItemStack io : tmpInputs.values()) { - this.condensedInputs[offset] = io; - offset++; - } - - offset = 0; - this.condensedOutputs = new IAEItemStack[tmpOutputs.size()]; - - for (final IAEItemStack io : tmpOutputs.values()) { - this.condensedOutputs[offset] = io; - offset++; - } + this.inputs = this.condenseStacks(in); + this.outputs = this.condenseStacks(out); } private void markItemAs(final int slotIndex, final ItemStack i, final TestStatus b) { @@ -213,8 +173,8 @@ public class CraftingPatternDetails implements ICraftingPatternDetails, Comparab this.testFrame.setStack(slotIndex, i); // If we cannot substitute, the items must match exactly - if (!canSubstitute && slotIndex < inputs.length) { - if (!inputs[slotIndex].isSameType(i)) { + if (!canSubstitute && slotIndex < sparseInputs.length) { + if (!sparseInputs[slotIndex].isSameType(i)) { this.markItemAs(slotIndex, i, TestStatus.DECLINE); return false; } @@ -240,25 +200,25 @@ public class CraftingPatternDetails implements ICraftingPatternDetails, Comparab } @Override - public IAEItemStack[] getInputs() { + public IAEItemStack[] getSparseInputs() { + return this.sparseInputs; + } + + @Override + public List getInputs() { return this.inputs; } @Override - public IAEItemStack[] getCondensedInputs() { - return this.condensedInputs; - } - - @Override - public IAEItemStack[] getCondensedOutputs() { - return this.condensedOutputs; - } - - @Override - public IAEItemStack[] getOutputs() { + public List getOutputs() { return this.outputs; } + @Override + public IAEItemStack[] getSparseOutputs() { + return this.sparseOutputs; + } + @Override public boolean canSubstitute() { return this.canSubstitute; @@ -276,8 +236,8 @@ public class CraftingPatternDetails implements ICraftingPatternDetails, Comparab } } - if (this.outputs != null && this.outputs.length > 0) { - return this.outputs[0].createItemStack(); + if (this.sparseOutputs != null && this.sparseOutputs.length > 0) { + return this.sparseOutputs[0].createItemStack(); } return ItemStack.EMPTY; @@ -344,6 +304,29 @@ public class CraftingPatternDetails implements ICraftingPatternDetails, Comparab return false; } + /** + * Merges all equal entries into a single one while adding their total stack + * sizes. + * + * @throws IllegalStateException if the result would be empty. + * + * @param collection the collection to condense + * + * @return a non empty list of condensed stacks. + */ + private List condenseStacks(Collection collection) { + final List merged = collection.stream().filter(Objects::nonNull) + .collect(Collectors.toMap(Function.identity(), IAEItemStack::copy, + (left, right) -> left.setStackSize(left.getStackSize() + right.getStackSize()))) + .values().stream().sorted(COMPARE_BY_STACKSIZE).collect(ImmutableList.toImmutableList()); + + if (merged.isEmpty()) { + throw new IllegalStateException("No pattern here!"); + } + + return merged; + } + private enum TestStatus { ACCEPT, DECLINE, TEST } diff --git a/src/main/java/appeng/items/misc/EncodedPatternItem.java b/src/main/java/appeng/items/misc/EncodedPatternItem.java index dcf84ed00..aac370d27 100644 --- a/src/main/java/appeng/items/misc/EncodedPatternItem.java +++ b/src/main/java/appeng/items/misc/EncodedPatternItem.java @@ -19,6 +19,7 @@ package appeng.items.misc; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.WeakHashMap; @@ -152,8 +153,8 @@ public class EncodedPatternItem extends AEBaseItem implements AEToolItem { final boolean isCrafting = details.isCraftable(); final boolean substitute = details.canSubstitute(); - final IAEItemStack[] in = details.getCondensedInputs(); - final IAEItemStack[] out = details.getCondensedOutputs(); + final Collection in = details.getInputs(); + final Collection out = details.getOutputs(); final Text label = (isCrafting ? GuiText.Crafts.text() : GuiText.Creates.text()) .copy().append(": "); @@ -200,7 +201,7 @@ public class EncodedPatternItem extends AEBaseItem implements AEToolItem { final ICraftingPatternDetails details = Api.instance().crafting().decodePattern(item, w); - out = details != null ? details.getOutputs()[0].createItemStack() : ItemStack.EMPTY; + out = details != null ? details.getOutputs().get(0).createItemStack() : ItemStack.EMPTY; SIMPLE_CACHE.put(item, out); return out; @@ -218,7 +219,9 @@ public class EncodedPatternItem extends AEBaseItem implements AEToolItem { final CompoundTag tag = itemStack.getTag(); Preconditions.checkArgument(tag != null, "itemStack missing a NBT tag"); - return new Identifier(tag.getString(NBT_RECIPE_ID)); + return tag.contains(NBT_RECIPE_ID, Constants.NBT.TAG_STRING) + ? new Identifier(tag.getString(NBT_RECIPE_ID)) + : null; } public List getIngredients(ItemStack itemStack) { @@ -271,7 +274,7 @@ public class EncodedPatternItem extends AEBaseItem implements AEToolItem { Preconditions.checkArgument(tag != null, "itemStack missing a NBT tag"); - return getCraftingRecipeId(itemStack) == null && tag.getBoolean(NBT_SUBSITUTE); + return getCraftingRecipeId(itemStack) != null && tag.getBoolean(NBT_SUBSITUTE); } /** diff --git a/src/main/java/appeng/me/cluster/implementations/CraftingCPUCluster.java b/src/main/java/appeng/me/cluster/implementations/CraftingCPUCluster.java index 51e4df72c..b69dfa3e1 100644 --- a/src/main/java/appeng/me/cluster/implementations/CraftingCPUCluster.java +++ b/src/main/java/appeng/me/cluster/implementations/CraftingCPUCluster.java @@ -18,7 +18,12 @@ package appeng.me.cluster.implementations; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; import java.util.Map.Entry; import com.google.common.collect.ImmutableList; @@ -36,9 +41,15 @@ import appeng.api.config.FuzzyMode; import appeng.api.config.PowerMultiplier; import appeng.api.crafting.ICraftingHelper; import appeng.api.networking.IGrid; -import appeng.api.networking.IGridHost; import appeng.api.networking.IGridNode; -import appeng.api.networking.crafting.*; +import appeng.api.networking.crafting.CraftingItemList; +import appeng.api.networking.crafting.ICraftingCPU; +import appeng.api.networking.crafting.ICraftingGrid; +import appeng.api.networking.crafting.ICraftingJob; +import appeng.api.networking.crafting.ICraftingLink; +import appeng.api.networking.crafting.ICraftingMedium; +import appeng.api.networking.crafting.ICraftingPatternDetails; +import appeng.api.networking.crafting.ICraftingRequester; import appeng.api.networking.energy.IEnergyGrid; import appeng.api.networking.events.MENetworkCraftingCpuChange; import appeng.api.networking.security.IActionSource; @@ -51,7 +62,11 @@ import appeng.api.storage.data.IItemList; import appeng.container.ContainerNull; import appeng.core.AELog; import appeng.core.Api; -import appeng.crafting.*; +import appeng.crafting.CraftBranchFailure; +import appeng.crafting.CraftingJob; +import appeng.crafting.CraftingLink; +import appeng.crafting.CraftingWatcher; +import appeng.crafting.MECraftingInventory; import appeng.me.cache.CraftingGridCache; import appeng.me.cluster.IAECluster; import appeng.me.cluster.MBCalculator; @@ -427,7 +442,7 @@ public final class CraftingCPUCluster implements IAECluster, ICraftingCPU { return null; } - private boolean canCraft(final ICraftingPatternDetails details, final IAEItemStack[] condensedInputs) { + private boolean canCraft(final ICraftingPatternDetails details, final Collection condensedInputs) { for (IAEItemStack g : condensedInputs) { if (details.isCraftable()) { @@ -555,7 +570,7 @@ public final class CraftingCPUCluster implements IAECluster, ICraftingCPU { final ICraftingPatternDetails details = e.getKey(); - if (this.canCraft(details, details.getCondensedInputs())) { + if (this.canCraft(details, details.getInputs())) { CraftingInventory ic = null; for (final ICraftingMedium m : cc.getMediums(e.getKey())) { @@ -565,7 +580,7 @@ public final class CraftingCPUCluster implements IAECluster, ICraftingCPU { if (!m.isBusy()) { if (ic == null) { - final IAEItemStack[] input = details.getInputs(); + final IAEItemStack[] input = details.getSparseInputs(); double sum = 0; for (final IAEItemStack anInput : input) { @@ -661,7 +676,7 @@ public final class CraftingCPUCluster implements IAECluster, ICraftingCPU { this.somethingChanged = true; this.remainingOperations--; - for (final IAEItemStack out : details.getCondensedOutputs()) { + for (final IAEItemStack out : details.getOutputs()) { this.postChange(out, this.machineSrc); this.waitingFor.add(out.copy()); this.postCraftingStatusChange(out.copy()); @@ -894,7 +909,7 @@ public final class CraftingCPUCluster implements IAECluster, ICraftingCPU { break; case PENDING: for (final Entry t : this.tasks.entrySet()) { - for (IAEItemStack ais : t.getKey().getCondensedOutputs()) { + for (IAEItemStack ais : t.getKey().getOutputs()) { ais = ais.copy(); ais.setStackSize(ais.getStackSize() * t.getValue().value); list.add(ais); @@ -913,7 +928,7 @@ public final class CraftingCPUCluster implements IAECluster, ICraftingCPU { } for (final Entry t : this.tasks.entrySet()) { - for (IAEItemStack ais : t.getKey().getCondensedOutputs()) { + for (IAEItemStack ais : t.getKey().getOutputs()) { ais = ais.copy(); ais.setStackSize(ais.getStackSize() * t.getValue().value); list.add(ais); @@ -958,7 +973,7 @@ public final class CraftingCPUCluster implements IAECluster, ICraftingCPU { is.setStackSize(0); for (final Entry t : this.tasks.entrySet()) { - for (final IAEItemStack ais : t.getKey().getCondensedOutputs()) { + for (final IAEItemStack ais : t.getKey().getOutputs()) { if (ais.equals(is)) { is.setStackSize(is.getStackSize() + ais.getStackSize() * t.getValue().value); } diff --git a/src/main/java/appeng/parts/automation/LevelEmitterPart.java b/src/main/java/appeng/parts/automation/LevelEmitterPart.java index 19eda5151..890260983 100644 --- a/src/main/java/appeng/parts/automation/LevelEmitterPart.java +++ b/src/main/java/appeng/parts/automation/LevelEmitterPart.java @@ -31,7 +31,7 @@ import net.minecraft.block.entity.BlockEntity; import net.minecraft.util.Hand; import net.minecraft.util.Identifier; import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Vec3d; +import net.minecraft.util.math.vector.Vector3d; import net.minecraft.world.World; import appeng.api.config.FuzzyMode; @@ -402,7 +402,7 @@ public class LevelEmitterPart extends UpgradeablePart implements IEnergyWatcherH } @Override - public boolean onPartActivate(final PlayerEntity player, final Hand hand, final Vec3d pos) { + public boolean onPartActivate(final PlayerEntity player, final Hand hand, final Vector3d pos) { if (Platform.isServer()) { ContainerOpener.openContainer(LevelEmitterContainer.TYPE, player, ContainerLocator.forPart(this)); } diff --git a/src/main/java/appeng/parts/reporting/AbstractReportingPart.java b/src/main/java/appeng/parts/reporting/AbstractReportingPart.java index bfa0a7fe1..24703efac 100644 --- a/src/main/java/appeng/parts/reporting/AbstractReportingPart.java +++ b/src/main/java/appeng/parts/reporting/AbstractReportingPart.java @@ -28,7 +28,7 @@ import net.minecraft.block.entity.BlockEntity; import net.minecraft.util.Hand; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.MathHelper; -import net.minecraft.util.math.Vec3d; +import net.minecraft.util.math.vector.Vector3d; import net.minecraft.world.BlockView; import net.minecraft.world.World; @@ -171,7 +171,7 @@ public abstract class AbstractReportingPart extends AEBasePart implements IMonit } @Override - public boolean onPartActivate(final PlayerEntity player, final Hand hand, final Vec3d pos) { + public boolean onPartActivate(final PlayerEntity player, final Hand hand, final Vector3d pos) { final BlockEntity te = this.getTile(); if (Platform.isWrench(player, player.inventory.getMainHandStack(), te.getPos())) { diff --git a/src/main/java/appeng/parts/reporting/AbstractTerminalPart.java b/src/main/java/appeng/parts/reporting/AbstractTerminalPart.java index 20946a9ef..dde773b97 100644 --- a/src/main/java/appeng/parts/reporting/AbstractTerminalPart.java +++ b/src/main/java/appeng/parts/reporting/AbstractTerminalPart.java @@ -26,7 +26,7 @@ import net.minecraft.screen.ScreenHandlerType; import net.minecraft.item.ItemStack; import net.minecraft.nbt.CompoundTag; import net.minecraft.util.Hand; -import net.minecraft.util.math.Vec3d; +import net.minecraft.util.math.vector.Vector3d; import appeng.api.config.Settings; import appeng.api.config.SortDir; @@ -104,7 +104,7 @@ public abstract class AbstractTerminalPart extends AbstractDisplayPart } @Override - public boolean onPartActivate(final PlayerEntity player, final Hand hand, final Vec3d pos) { + public boolean onPartActivate(final PlayerEntity player, final Hand hand, final Vector3d pos) { if (!super.onPartActivate(player, hand, pos)) { if (!player.world.isClient) { ContainerOpener.openContainer(getContainerType(player), player, ContainerLocator.forPart(this)); diff --git a/src/main/java/appeng/parts/reporting/PatternTerminalPart.java b/src/main/java/appeng/parts/reporting/PatternTerminalPart.java index 99a9d6b7c..01f8cb3b8 100644 --- a/src/main/java/appeng/parts/reporting/PatternTerminalPart.java +++ b/src/main/java/appeng/parts/reporting/PatternTerminalPart.java @@ -111,13 +111,13 @@ public class PatternTerminalPart extends AbstractTerminalPart { this.setCraftingRecipe(details.isCraftable()); this.setSubstitution(details.canSubstitute()); - for (int x = 0; x < this.crafting.getSlotCount() && x < details.getInputs().length; x++) { - final IAEItemStack item = details.getInputs()[x]; + for (int x = 0; x < this.crafting.getSlotCount() && x < details.getSparseInputs().length; x++) { + final IAEItemStack item = details.getSparseInputs()[x]; this.crafting.forceSetInvStack(x, item == null ? ItemStack.EMPTY : item.createItemStack()); } - for (int x = 0; x < this.output.getSlotCount() && x < details.getOutputs().length; x++) { - final IAEItemStack item = details.getOutputs()[x]; + for (int x = 0; x < this.output.getSlotCount() && x < details.getSparseOutputs().length; x++) { + final IAEItemStack item = details.getSparseOutputs()[x]; this.output.forceSetInvStack(x, item == null ? ItemStack.EMPTY : item.createItemStack()); } } diff --git a/src/main/java/appeng/recipes/handlers/GrinderRecipeSerializer.java b/src/main/java/appeng/recipes/handlers/GrinderRecipeSerializer.java index 2f4e3675f..ff3af338b 100644 --- a/src/main/java/appeng/recipes/handlers/GrinderRecipeSerializer.java +++ b/src/main/java/appeng/recipes/handlers/GrinderRecipeSerializer.java @@ -19,6 +19,7 @@ import net.minecraft.util.Identifier; import net.minecraft.util.JsonHelper; import appeng.core.AEConfig; +import appeng.core.sync.BasePacket; public class GrinderRecipeSerializer implements RecipeSerializer { @@ -63,7 +64,7 @@ public class GrinderRecipeSerializer implements RecipeSerializer @Override public GrinderRecipe read(Identifier recipeId, PacketByteBuf buffer) { - String group = buffer.readString(); + String group = buffer.readString(BasePacket.MAX_STRING_LENGTH); Ingredient ingredient = Ingredient.fromPacket(buffer); int ingredientCount = buffer.readVarInt(); ItemStack result = buffer.readItemStack(); diff --git a/src/main/java/appeng/recipes/handlers/InscriberRecipeSerializer.java b/src/main/java/appeng/recipes/handlers/InscriberRecipeSerializer.java index 256dd8eb6..39a08a68b 100644 --- a/src/main/java/appeng/recipes/handlers/InscriberRecipeSerializer.java +++ b/src/main/java/appeng/recipes/handlers/InscriberRecipeSerializer.java @@ -13,6 +13,7 @@ import net.minecraft.util.Identifier; import net.minecraft.util.JsonHelper; import appeng.api.features.InscriberProcessType; +import appeng.core.sync.BasePacket; public class InscriberRecipeSerializer implements RecipeSerializer { @@ -60,7 +61,7 @@ public class InscriberRecipeSerializer implements RecipeSerializer