From 16e685ed9470d0c777323ad9b45b65e82813d929 Mon Sep 17 00:00:00 2001 From: DStrand1 Date: Mon, 12 Apr 2021 00:33:07 -0500 Subject: [PATCH 01/17] Create checks on master commits --- .github/workflows/checks.yml | 42 ++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 .github/workflows/checks.yml diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml new file mode 100644 index 000000000..777948a4a --- /dev/null +++ b/.github/workflows/checks.yml @@ -0,0 +1,42 @@ +name: Checks +on: + push: + branches: + - master + tags-ignore: + - '**' + + pull_request: + +jobs: + checks: + name: Checks + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Set up JDK 1.8 + uses: actions/setup-java@v1 + with: + java-version: 1.8 + + - name: Restore cached Gradle packages + uses: actions/cache@v2 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle.properties') }} + restore-keys: ${{ runner.os }}-gradle- + + - name: Run checks and tests + run: ./gradlew check test + + - name: Cleanup Gradle cache + run: | + rm -f ~/.gradle/caches/modules-2/modules-2.lock + rm -f ~/.gradle/caches/modules-2/gc.properties From 4a78b110c011fbbb04979a9d5949a015ffab77ac Mon Sep 17 00:00:00 2001 From: dan Date: Mon, 12 Apr 2021 00:47:46 -0500 Subject: [PATCH 02/17] Add badges to README update required licensing information --- README.md | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1e5c692aa..89c18cb79 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,19 @@ -Join me on [Discord](https://discord.gg/y5dkPtF) +# Gregicality + +[![Downloads](http://cf.way2muchnoise.eu/full_gregicality_downloads.svg)](https://www.curseforge.com/minecraft/mc-mods/gregicality) [![MCVersion](http://cf.way2muchnoise.eu/versions/gregicality.svg)](https://www.curseforge.com/minecraft/mc-mods/gregicality) + +[![GitHub issues](https://img.shields.io/github/issues/Gregicality/gregicality.svg)](https://github.com/Gregicality/gregicality/issues) [![GitHub pull requests](https://img.shields.io/github/issues-pr/Gregicality/gregicality.svg)](https://github.com/Gregicality/gregicality/pulls) [![license](https://img.shields.io/github/license/Gregicality/gregicality.svg)](../master/LICENSE) + +---- + +Join us on [Discord](https://discord.gg/y5dkPtF) thanks to magcicada and ChromaPIE for Chinese translation. # LICENSE +Original foundation from [Shadows of Greg](https://github.com/Shadows-of-Fire/Shadows-of-Greg) + Magneto Resonatic circuit are from emeraldsemerald. Thanks to him. https://github.com/bartimaeusnek/bartworks @@ -14,4 +24,4 @@ Autogenerated Metal Casings are from Asther, SMD texture are from hjae78, powercell are from hjae78 -My work is under No License. \ No newline at end of file +Gregicality is under the LGPL3.0 License. From 2e131dbc44e52d3e0740808051c83a3e2cc4d1d4 Mon Sep 17 00:00:00 2001 From: dan Date: Mon, 12 Apr 2021 01:08:21 -0500 Subject: [PATCH 03/17] remove gradle.properties from checks --- .github/workflows/checks.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 777948a4a..f03cee8b3 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -30,7 +30,7 @@ jobs: path: | ~/.gradle/caches ~/.gradle/wrapper - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle.properties') }} + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }} restore-keys: ${{ runner.os }}-gradle- - name: Run checks and tests From 475e0b17c75b1cf8075e18795f2ee71ec6c68167 Mon Sep 17 00:00:00 2001 From: DStrand1 Date: Mon, 12 Apr 2021 01:11:26 -0500 Subject: [PATCH 04/17] Revert "remove gradle.properties from checks" This reverts commit 2e131dbc44e52d3e0740808051c83a3e2cc4d1d4. --- .github/workflows/checks.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index f03cee8b3..777948a4a 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -30,7 +30,7 @@ jobs: path: | ~/.gradle/caches ~/.gradle/wrapper - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }} + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle.properties') }} restore-keys: ${{ runner.os }}-gradle- - name: Run checks and tests From a2388e9258fa3e0bfc67bc38e277eeba59a6c35d Mon Sep 17 00:00:00 2001 From: DStrand1 Date: Mon, 12 Apr 2021 01:15:29 -0500 Subject: [PATCH 05/17] Add command handler Remove BuildCraftAPI as its unneeded --- build.gradle | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/build.gradle b/build.gradle index 9f5ec9ba0..1c31ec1c8 100644 --- a/build.gradle +++ b/build.gradle @@ -2,6 +2,7 @@ buildscript { repositories { mavenCentral() maven { url = "http://files.minecraftforge.net/maven" } + maven { url "https://plugins.gradle.org/m2/" } } dependencies { classpath 'net.minecraftforge.gradle:ForgeGradle:2.3-SNAPSHOT' @@ -57,9 +58,6 @@ repositories { maven { url = "http://chickenbones.net/maven/" } - maven { - url = "https://mod-buildcraft.com/maven/" - } maven { url "http://maven.shadowfacts.net/" } @@ -74,12 +72,6 @@ repositories { maven { url "http://maven.k-4u.nl" } -// maven { // TOP -// setUrl("https://repo.erins.net/maven") -// } -// maven { -// setUrl("https://modmaven.k-4u.nl/") -// } } dependencies { @@ -90,7 +82,6 @@ dependencies { deobfCompile "codechicken:ChickenASM:1.12-1.0.2.9" deobfCompile "codechicken-lib-1-8:CodeChickenLib-1.12.2:3.2.3.358:universal" deobfCompile "forge-multipart-cbe:ForgeMultipart-1.12.2:2.6.2.83:universal" - deobfCompile "com.mod-buildcraft:buildcraft-api:+" deobfCompile "slimeknights.mantle:Mantle:1.12-1.3.3.42" deobfCompile "slimeknights:TConstruct:1.12.2-2.12.0.115" deobfCompile "mcjty.theoneprobe:TheOneProbe-1.12:1.12-1.4.23-16" @@ -121,3 +112,18 @@ processResources { exclude 'mcmod.info' } } + +def run(command) { + def process = command.execute() + def outputStream = new StringBuffer(); + def errorStream = new StringBuffer(); + process.waitForProcessOutput(outputStream, errorStream) + + errorStream.toString().with { + if (it) { + throw new GradleException("Error executing ${command}:\n> ${it}") + } + } + + return outputStream.toString().trim() +} From 165ce9a0f6bbda6290dafdd64a701e5d147722f3 Mon Sep 17 00:00:00 2001 From: DStrand1 Date: Mon, 12 Apr 2021 01:19:23 -0500 Subject: [PATCH 06/17] Update fs permission --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 1c31ec1c8..1973d69a8 100644 --- a/build.gradle +++ b/build.gradle @@ -123,7 +123,7 @@ def run(command) { if (it) { throw new GradleException("Error executing ${command}:\n> ${it}") } - } + } return outputStream.toString().trim() } From 97ea73dbc0488d82b842334bb8438a522bfe18b2 Mon Sep 17 00:00:00 2001 From: dan Date: Mon, 12 Apr 2021 01:21:53 -0500 Subject: [PATCH 07/17] add execution permission --- .github/workflows/checks.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 777948a4a..0f1644a20 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -32,6 +32,9 @@ jobs: ~/.gradle/wrapper key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle.properties') }} restore-keys: ${{ runner.os }}-gradle- + + - name: Update gradlew execution + run: chmod +x ./gradlew - name: Run checks and tests run: ./gradlew check test From f2b1171bc4a4a91c0e1b5454d911a9724d0334e9 Mon Sep 17 00:00:00 2001 From: DStrand1 Date: Mon, 12 Apr 2021 01:46:20 -0500 Subject: [PATCH 08/17] Add executable bit to gradlew --- gradlew | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 gradlew diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 From e90f81f1c3ca39dfcc0ccc47d0e743660ca885ff Mon Sep 17 00:00:00 2001 From: dan Date: Mon, 12 Apr 2021 01:48:14 -0500 Subject: [PATCH 09/17] remove old execution enabling --- .github/workflows/checks.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 0f1644a20..777948a4a 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -32,9 +32,6 @@ jobs: ~/.gradle/wrapper key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle.properties') }} restore-keys: ${{ runner.os }}-gradle- - - - name: Update gradlew execution - run: chmod +x ./gradlew - name: Run checks and tests run: ./gradlew check test From 3134a5e61d4023ec1cf40f731933adce2577ac76 Mon Sep 17 00:00:00 2001 From: DStrand1 Date: Wed, 14 Apr 2021 23:14:31 -0500 Subject: [PATCH 10/17] Add logic for distinct bus multiblocks First attempt --- ...eMapDistinctBusesMultiblockController.java | 305 ++++++++++++++++++ 1 file changed, 305 insertions(+) create mode 100644 src/main/java/gregicadditions/capabilities/impl/RecipeMapDistinctBusesMultiblockController.java diff --git a/src/main/java/gregicadditions/capabilities/impl/RecipeMapDistinctBusesMultiblockController.java b/src/main/java/gregicadditions/capabilities/impl/RecipeMapDistinctBusesMultiblockController.java new file mode 100644 index 000000000..a63514afe --- /dev/null +++ b/src/main/java/gregicadditions/capabilities/impl/RecipeMapDistinctBusesMultiblockController.java @@ -0,0 +1,305 @@ +package gregicadditions.capabilities.impl; + +import codechicken.lib.render.CCRenderState; +import codechicken.lib.render.pipeline.IVertexOperation; +import codechicken.lib.vec.Matrix4; +import com.google.common.collect.Lists; +import gregtech.api.GTValues; +import gregtech.api.capability.IEnergyContainer; +import gregtech.api.capability.IMultipleTankHandler; +import gregtech.api.capability.impl.*; +import gregtech.api.metatileentity.MetaTileEntity; +import gregtech.api.metatileentity.multiblock.IMultiblockPart; +import gregtech.api.metatileentity.multiblock.MultiblockAbility; +import gregtech.api.metatileentity.multiblock.MultiblockWithDisplayBase; +import gregtech.api.metatileentity.multiblock.RecipeMapMultiblockController; +import gregtech.api.multiblock.PatternMatchContext; +import gregtech.api.recipes.Recipe; +import gregtech.api.recipes.RecipeMap; +import gregtech.api.render.OrientedOverlayRenderer; +import gregtech.api.render.Textures; +import gregtech.api.util.GTUtility; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.Style; +import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.util.text.TextFormatting; +import net.minecraftforge.items.IItemHandler; +import net.minecraftforge.items.IItemHandlerModifiable; +import net.minecraftforge.items.ItemStackHandler; + +import javax.annotation.Nonnull; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +public abstract class RecipeMapDistinctBusesMultiblockController extends MultiblockWithDisplayBase { + + public final RecipeMap recipeMap; + protected DistinctBusesMultiblockRecipeLogic recipeMapWorkable; + + protected List inputInventory; + protected IItemHandlerModifiable outputInventory; + protected IMultipleTankHandler inputFluidInventory; + protected IMultipleTankHandler outputFluidInventory; + protected IEnergyContainer energyContainer; + + public RecipeMapDistinctBusesMultiblockController(ResourceLocation metaTileEntityId, RecipeMap recipeMap) { + super(metaTileEntityId); + this.recipeMap = recipeMap; + this.recipeMapWorkable = new DistinctBusesMultiblockRecipeLogic(this); // TODO + resetTileAbilities(); + } + + public IEnergyContainer getEnergyContainer() { + return energyContainer; + } + + public List getInputInventory() { + return inputInventory; + } + + public IItemHandlerModifiable getOutputInventory() { + return outputInventory; + } + + public IMultipleTankHandler getInputFluidInventory() { + return inputFluidInventory; + } + + public IMultipleTankHandler getOutputFluidInventory() { + return outputFluidInventory; + } + + public boolean checkRecipe(Recipe recipe, boolean consumeIfSuccess) { + return true; + } + + @Override + protected void formStructure(PatternMatchContext context) { + super.formStructure(context); + initializeAbilities(); + } + + @Override + public void invalidateStructure() { + super.invalidateStructure(); + resetTileAbilities(); + } + + @Override + protected void updateFormedValid() { + this.recipeMapWorkable.updateWorkable(); + } + + private void initializeAbilities() { + this.inputInventory = getAbilities(MultiblockAbility.IMPORT_ITEMS); + this.inputFluidInventory = new FluidTankList(allowSameFluidFillForOutputs(), getAbilities(MultiblockAbility.IMPORT_FLUIDS)); + this.outputInventory = new ItemHandlerList(getAbilities(MultiblockAbility.EXPORT_ITEMS)); + this.outputFluidInventory = new FluidTankList(allowSameFluidFillForOutputs(), getAbilities(MultiblockAbility.EXPORT_FLUIDS)); + this.energyContainer = new EnergyContainerList(getAbilities(MultiblockAbility.INPUT_ENERGY)); + } + + private void resetTileAbilities() { + this.inputInventory = new ArrayList<>(); + this.inputFluidInventory = new FluidTankList(true); + this.outputInventory = new ItemStackHandler(0); + this.outputFluidInventory = new FluidTankList(true); + this.energyContainer = new EnergyContainerList(Lists.newArrayList()); + } + + protected boolean allowSameFluidFillForOutputs() { + return true; + } + + // TODO Maybe do more in here? + @Override + protected void addDisplayText(List textList) { + super.addDisplayText(textList); + if (isStructureFormed()) { + IEnergyContainer energyContainer = recipeMapWorkable.getEnergyContainer(); + if (energyContainer != null && energyContainer.getEnergyCapacity() > 0) { + long maxVoltage = energyContainer.getInputVoltage(); + String voltageName = GTValues.VN[GTUtility.getTierByVoltage(maxVoltage)]; + textList.add(new TextComponentTranslation("gregtech.multiblock.max_energy_per_tick", maxVoltage, voltageName)); + } + + if (!recipeMapWorkable.isWorkingEnabled()) { + textList.add(new TextComponentTranslation("gregtech.multiblock.work_paused")); + + } else if (recipeMapWorkable.isActive()) { + textList.add(new TextComponentTranslation("gregtech.multiblock.running")); + int currentProgress = (int) (recipeMapWorkable.getProgressPercent() * 100); + textList.add(new TextComponentTranslation("gregtech.multiblock.progress", currentProgress)); + } else { + textList.add(new TextComponentTranslation("gregtech.multiblock.idling")); + } + + if (recipeMapWorkable.isHasNotEnoughEnergy()) { + textList.add(new TextComponentTranslation("gregtech.multiblock.not_enough_energy").setStyle(new Style().setColor(TextFormatting.RED))); + } + } + } + + @Override + protected boolean checkStructureComponents(List parts, Map, List> abilities) { + int itemInputsCount = abilities.getOrDefault(MultiblockAbility.IMPORT_ITEMS, Collections.emptyList()) + .stream().map(it -> (IItemHandler) it).mapToInt(IItemHandler::getSlots).sum(); + int fluidInputsCount = abilities.getOrDefault(MultiblockAbility.IMPORT_FLUIDS, Collections.emptyList()).size(); + return itemInputsCount >= recipeMap.getMinInputs() && + fluidInputsCount >= recipeMap.getMinFluidInputs() && + abilities.containsKey(MultiblockAbility.INPUT_ENERGY); + } + + @Override + public void renderMetaTileEntity(CCRenderState renderState, Matrix4 translation, IVertexOperation[] pipeline) { + super.renderMetaTileEntity(renderState, translation, pipeline); + this.getFrontOverlay().render(renderState, translation, pipeline, getFrontFacing(), recipeMapWorkable.isActive()); + } + + /** + * Override this method to change the Controller overlay + * @return The overlay to render on the Multiblock Controller + */ + @Nonnull + protected OrientedOverlayRenderer getFrontOverlay() { + return Textures.MULTIBLOCK_WORKABLE_OVERLAY; + } + + public static class DistinctBusesMultiblockRecipeLogic extends AbstractRecipeLogic { + + private int lastRecipeIndex = 0; + + public DistinctBusesMultiblockRecipeLogic(RecipeMapDistinctBusesMultiblockController tileEntity) { + super(tileEntity, tileEntity.recipeMap); + } + + @Override + public void update() { + } + + public void updateWorkable() { + super.update(); + } + + public IEnergyContainer getEnergyContainer() { + RecipeMapDistinctBusesMultiblockController controller = (RecipeMapDistinctBusesMultiblockController) metaTileEntity; + return controller.getEnergyContainer(); + } + + @Override + protected IItemHandlerModifiable getInputInventory() { + return null; // DO NOT USE THIS!!! + } + + public List getInputBuses() { + RecipeMapDistinctBusesMultiblockController controller = (RecipeMapDistinctBusesMultiblockController) metaTileEntity; + return controller.getInputInventory(); + } + + @Override + protected IItemHandlerModifiable getOutputInventory() { + RecipeMapDistinctBusesMultiblockController controller = (RecipeMapDistinctBusesMultiblockController) metaTileEntity; + return controller.getOutputInventory(); + } + + @Override + protected IMultipleTankHandler getInputTank() { + RecipeMapDistinctBusesMultiblockController controller = (RecipeMapDistinctBusesMultiblockController) metaTileEntity; + return controller.getInputFluidInventory(); + } + + @Override + protected IMultipleTankHandler getOutputTank() { + RecipeMapDistinctBusesMultiblockController controller = (RecipeMapDistinctBusesMultiblockController) metaTileEntity; + return controller.getOutputFluidInventory(); + } + + @Override + protected long getEnergyStored() { + return getEnergyContainer().getEnergyStored(); + } + + @Override + protected long getEnergyCapacity() { + return getEnergyContainer().getEnergyCapacity(); + } + + @Override + protected boolean drawEnergy(int recipeEUt) { + long resultEnergy = getEnergyStored() - recipeEUt; + if (resultEnergy >= 0L && resultEnergy <= getEnergyCapacity()) { + getEnergyContainer().changeEnergy(-recipeEUt); + return true; + } else return false; + } + + @Override + protected long getMaxVoltage() { + return Math.max(getEnergyContainer().getInputVoltage(), getEnergyContainer().getOutputVoltage()); + } + + @Override + protected void trySearchNewRecipe() { + long maxVoltage = getMaxVoltage(); + Recipe currentRecipe = null; + List importInventory = getInputBuses(); + IMultipleTankHandler importFluids = getInputTank(); + + // Our caching implementation + if (previousRecipe != null && previousRecipe.matches(false, importInventory.get(lastRecipeIndex), importFluids)) { + currentRecipe = previousRecipe; + if (setupAndConsumeRecipeInputs(currentRecipe, lastRecipeIndex)) { + setupRecipe(currentRecipe); + return; + } + } + for (int i = 0; i < importInventory.size(); i++) { + IItemHandlerModifiable bus = importInventory.get(i); + boolean dirty = checkRecipeInputsDirty(bus, importFluids); + if (dirty || forceRecipeRecheck) { + this.forceRecipeRecheck = false; + currentRecipe = findRecipe(maxVoltage, bus, importFluids); + if (currentRecipe != null) { + this.previousRecipe = currentRecipe; + } + } + if (currentRecipe != null && setupAndConsumeRecipeInputs(currentRecipe, i)) { + lastRecipeIndex = i; + setupRecipe(currentRecipe); + break; + } + } + } + + protected boolean setupAndConsumeRecipeInputs(Recipe recipe, int index) { + RecipeMapMultiblockController controller = (RecipeMapMultiblockController) metaTileEntity; + if (controller.checkRecipe(recipe, false)) { + + int[] resultOverclock = calculateOverclock(recipe.getEUt(), recipe.getDuration()); + int totalEUt = resultOverclock[0] * resultOverclock[1]; + IItemHandlerModifiable importInventory = getInputBuses().get(index); + IItemHandlerModifiable exportInventory = getOutputInventory(); + IMultipleTankHandler importFluids = getInputTank(); + IMultipleTankHandler exportFluids = getOutputTank(); + boolean setup = (totalEUt >= 0 ? getEnergyStored() >= (totalEUt > getEnergyCapacity() / 2 ? resultOverclock[0] : totalEUt) : + (getEnergyStored() - resultOverclock[0] <= getEnergyCapacity())) && + MetaTileEntity.addItemsToItemHandler(exportInventory, true, recipe.getAllItemOutputs(exportInventory.getSlots())) && + MetaTileEntity.addFluidsToFluidHandler(exportFluids, true, recipe.getFluidOutputs()) && + recipe.matches(true, importInventory, importFluids); + + if (setup) { + controller.checkRecipe(recipe, true); + return true; + } + } + return false; + } + + @Override + protected boolean setupAndConsumeRecipeInputs(Recipe recipe) { + return false; // DO NOT USE!!! + } + } +} From 4910e889410626806e5ecd4672665076082a32a1 Mon Sep 17 00:00:00 2001 From: DStrand1 Date: Wed, 14 Apr 2021 23:35:19 -0500 Subject: [PATCH 11/17] Fix casting error --- .../impl/RecipeMapDistinctBusesMultiblockController.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/java/gregicadditions/capabilities/impl/RecipeMapDistinctBusesMultiblockController.java b/src/main/java/gregicadditions/capabilities/impl/RecipeMapDistinctBusesMultiblockController.java index a63514afe..a68ee0723 100644 --- a/src/main/java/gregicadditions/capabilities/impl/RecipeMapDistinctBusesMultiblockController.java +++ b/src/main/java/gregicadditions/capabilities/impl/RecipeMapDistinctBusesMultiblockController.java @@ -4,7 +4,8 @@ import codechicken.lib.render.pipeline.IVertexOperation; import codechicken.lib.vec.Matrix4; import com.google.common.collect.Lists; -import gregtech.api.GTValues; +import gregicadditions.GAUtility; +import gregicadditions.GAValues; import gregtech.api.capability.IEnergyContainer; import gregtech.api.capability.IMultipleTankHandler; import gregtech.api.capability.impl.*; @@ -12,13 +13,11 @@ import gregtech.api.metatileentity.multiblock.IMultiblockPart; import gregtech.api.metatileentity.multiblock.MultiblockAbility; import gregtech.api.metatileentity.multiblock.MultiblockWithDisplayBase; -import gregtech.api.metatileentity.multiblock.RecipeMapMultiblockController; import gregtech.api.multiblock.PatternMatchContext; import gregtech.api.recipes.Recipe; import gregtech.api.recipes.RecipeMap; import gregtech.api.render.OrientedOverlayRenderer; import gregtech.api.render.Textures; -import gregtech.api.util.GTUtility; import net.minecraft.util.ResourceLocation; import net.minecraft.util.text.ITextComponent; import net.minecraft.util.text.Style; @@ -121,7 +120,7 @@ protected void addDisplayText(List textList) { IEnergyContainer energyContainer = recipeMapWorkable.getEnergyContainer(); if (energyContainer != null && energyContainer.getEnergyCapacity() > 0) { long maxVoltage = energyContainer.getInputVoltage(); - String voltageName = GTValues.VN[GTUtility.getTierByVoltage(maxVoltage)]; + String voltageName = GAValues.VN[GAUtility.getTierByVoltage(maxVoltage)]; textList.add(new TextComponentTranslation("gregtech.multiblock.max_energy_per_tick", maxVoltage, voltageName)); } @@ -274,7 +273,7 @@ protected void trySearchNewRecipe() { } protected boolean setupAndConsumeRecipeInputs(Recipe recipe, int index) { - RecipeMapMultiblockController controller = (RecipeMapMultiblockController) metaTileEntity; + RecipeMapDistinctBusesMultiblockController controller = (RecipeMapDistinctBusesMultiblockController) metaTileEntity; if (controller.checkRecipe(recipe, false)) { int[] resultOverclock = calculateOverclock(recipe.getEUt(), recipe.getDuration()); From dc705e1f3fd1ec40ca8b8dfb455500d91b3980c2 Mon Sep 17 00:00:00 2001 From: DStrand1 Date: Thu, 15 Apr 2021 00:04:22 -0500 Subject: [PATCH 12/17] Add itemstack caching override Offers much better efficiency than using stock one from AbstractRecipeLogic --- ...eMapDistinctBusesMultiblockController.java | 83 ++++++++++++++++--- 1 file changed, 72 insertions(+), 11 deletions(-) diff --git a/src/main/java/gregicadditions/capabilities/impl/RecipeMapDistinctBusesMultiblockController.java b/src/main/java/gregicadditions/capabilities/impl/RecipeMapDistinctBusesMultiblockController.java index a68ee0723..d356ecc55 100644 --- a/src/main/java/gregicadditions/capabilities/impl/RecipeMapDistinctBusesMultiblockController.java +++ b/src/main/java/gregicadditions/capabilities/impl/RecipeMapDistinctBusesMultiblockController.java @@ -6,6 +6,7 @@ import com.google.common.collect.Lists; import gregicadditions.GAUtility; import gregicadditions.GAValues; +import gregicadditions.utils.GALog; import gregtech.api.capability.IEnergyContainer; import gregtech.api.capability.IMultipleTankHandler; import gregtech.api.capability.impl.*; @@ -18,21 +19,24 @@ import gregtech.api.recipes.RecipeMap; import gregtech.api.render.OrientedOverlayRenderer; import gregtech.api.render.Textures; +import net.minecraft.item.ItemStack; import net.minecraft.util.ResourceLocation; import net.minecraft.util.text.ITextComponent; import net.minecraft.util.text.Style; import net.minecraft.util.text.TextComponentTranslation; import net.minecraft.util.text.TextFormatting; +import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.items.IItemHandler; import net.minecraftforge.items.IItemHandlerModifiable; import net.minecraftforge.items.ItemStackHandler; import javax.annotation.Nonnull; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; +import java.util.*; +/** + * This class serves as an alternative to {@link gregicadditions.capabilities.impl.GARecipeMapMultiblockController}, + * except it treats its input buses as separate and distinct inventories for recipes. + */ public abstract class RecipeMapDistinctBusesMultiblockController extends MultiblockWithDisplayBase { public final RecipeMap recipeMap; @@ -169,6 +173,7 @@ protected OrientedOverlayRenderer getFrontOverlay() { public static class DistinctBusesMultiblockRecipeLogic extends AbstractRecipeLogic { private int lastRecipeIndex = 0; + protected ItemStack[][] lastItemInputsMatrix; public DistinctBusesMultiblockRecipeLogic(RecipeMapDistinctBusesMultiblockController tileEntity) { super(tileEntity, tileEntity.recipeMap); @@ -187,12 +192,7 @@ public IEnergyContainer getEnergyContainer() { return controller.getEnergyContainer(); } - @Override - protected IItemHandlerModifiable getInputInventory() { - return null; // DO NOT USE THIS!!! - } - - public List getInputBuses() { + protected List getInputBuses() { RecipeMapDistinctBusesMultiblockController controller = (RecipeMapDistinctBusesMultiblockController) metaTileEntity; return controller.getInputInventory(); } @@ -247,6 +247,7 @@ protected void trySearchNewRecipe() { IMultipleTankHandler importFluids = getInputTank(); // Our caching implementation + // This guarantees that if we get a recipe cache hit, our efficiency is no different from other machines if (previousRecipe != null && previousRecipe.matches(false, importInventory.get(lastRecipeIndex), importFluids)) { currentRecipe = previousRecipe; if (setupAndConsumeRecipeInputs(currentRecipe, lastRecipeIndex)) { @@ -254,9 +255,12 @@ protected void trySearchNewRecipe() { return; } } + + // On a cache miss, our efficiency is much worse, as it will check + // each bus individually instead of the combined inventory all at once. for (int i = 0; i < importInventory.size(); i++) { IItemHandlerModifiable bus = importInventory.get(i); - boolean dirty = checkRecipeInputsDirty(bus, importFluids); + boolean dirty = checkRecipeInputsDirty(bus, importFluids, i); if (dirty || forceRecipeRecheck) { this.forceRecipeRecheck = false; currentRecipe = findRecipe(maxVoltage, bus, importFluids); @@ -296,9 +300,66 @@ protected boolean setupAndConsumeRecipeInputs(Recipe recipe, int index) { return false; } + // Replacing this for optimization reasons + protected boolean checkRecipeInputsDirty(IItemHandler inputs, IMultipleTankHandler fluidInputs, int index) { + boolean shouldRecheckRecipe = false; + RecipeMapDistinctBusesMultiblockController controller = (RecipeMapDistinctBusesMultiblockController) metaTileEntity; + if (lastItemInputsMatrix == null || lastItemInputsMatrix.length != controller.inputInventory.size()) { + lastItemInputsMatrix = new ItemStack[controller.inputInventory.size()][]; + GALog.logger.info("Num buses: " + controller.inputInventory.size()); + } + if (lastItemInputsMatrix[index] == null || lastItemInputsMatrix[index].length != inputs.getSlots()) { + this.lastItemInputsMatrix[index] = new ItemStack[inputs.getSlots()]; + Arrays.fill(lastItemInputsMatrix[index], ItemStack.EMPTY); + } + if (lastFluidInputs == null || lastFluidInputs.length != fluidInputs.getTanks()) { + this.lastFluidInputs = new FluidStack[fluidInputs.getTanks()]; + } + for (int i = 0; i < lastItemInputsMatrix[index].length; i++) { + ItemStack currentStack = inputs.getStackInSlot(i); + ItemStack lastStack = lastItemInputsMatrix[index][i]; + if (!areItemStacksEqual(currentStack, lastStack)) { + this.lastItemInputsMatrix[index][i] = currentStack.isEmpty() ? ItemStack.EMPTY : currentStack.copy(); + shouldRecheckRecipe = true; + } else if (currentStack.getCount() != lastStack.getCount()) { + lastStack.setCount(currentStack.getCount()); + shouldRecheckRecipe = true; + } + } + for (int i = 0; i < lastFluidInputs.length; i++) { + FluidStack currentStack = fluidInputs.getTankAt(i).getFluid(); + FluidStack lastStack = lastFluidInputs[i]; + if ((currentStack == null && lastStack != null) || + (currentStack != null && !currentStack.isFluidEqual(lastStack))) { + this.lastFluidInputs[i] = currentStack == null ? null : currentStack.copy(); + shouldRecheckRecipe = true; + } else if (currentStack != null && lastStack != null && + currentStack.amount != lastStack.amount) { + lastStack.amount = currentStack.amount; + shouldRecheckRecipe = true; + } + } + return shouldRecheckRecipe; + } + + // Dead methods + + @Override + protected IItemHandlerModifiable getInputInventory() { + GALog.logger.error("In old getInputInventory! Please report this error!"); + return null; // DO NOT USE!!! + } + @Override protected boolean setupAndConsumeRecipeInputs(Recipe recipe) { + GALog.logger.error("In old setupAndConsumeRecipeInputs! Please report this error!"); return false; // DO NOT USE!!! } + + @Override + protected boolean checkRecipeInputsDirty(IItemHandler inputs, IMultipleTankHandler fluidInputs) { + GALog.logger.error("In old checkRecipeInputsDirty! Please report this error!"); + return false; + } } } From 97d26f3c52def103af70dc42862c3bc291c4b3e9 Mon Sep 17 00:00:00 2001 From: DStrand1 Date: Thu, 15 Apr 2021 00:50:14 -0500 Subject: [PATCH 13/17] First working test done --- ...RecipeMapDistinctMultiblockController.java | 453 ++++++++++++++++++ ...ecipeMapDistinctMultiblockController.java} | 22 +- .../multi/simple/TileEntityLargeExtruder.java | 3 +- 3 files changed, 466 insertions(+), 12 deletions(-) create mode 100644 src/main/java/gregicadditions/capabilities/impl/LargeSimpleRecipeMapDistinctMultiblockController.java rename src/main/java/gregicadditions/capabilities/impl/{RecipeMapDistinctBusesMultiblockController.java => RecipeMapDistinctMultiblockController.java} (92%) diff --git a/src/main/java/gregicadditions/capabilities/impl/LargeSimpleRecipeMapDistinctMultiblockController.java b/src/main/java/gregicadditions/capabilities/impl/LargeSimpleRecipeMapDistinctMultiblockController.java new file mode 100644 index 000000000..0fa6b7bf3 --- /dev/null +++ b/src/main/java/gregicadditions/capabilities/impl/LargeSimpleRecipeMapDistinctMultiblockController.java @@ -0,0 +1,453 @@ +package gregicadditions.capabilities.impl; + +import gregicadditions.GAMaterials; +import gregicadditions.item.components.*; +import gregicadditions.machines.multi.simple.LargeSimpleRecipeMapMultiblockController; +import gregicadditions.utils.GALog; +import gregicadditions.utils.Tuple; +import gregtech.api.capability.IMultipleTankHandler; +import gregtech.api.metatileentity.multiblock.MultiblockAbility; +import gregtech.api.metatileentity.multiblock.RecipeMapMultiblockController; +import gregtech.api.multiblock.BlockWorldState; +import gregtech.api.recipes.CountableIngredient; +import gregtech.api.recipes.Recipe; +import gregtech.api.recipes.RecipeBuilder; +import gregtech.api.recipes.RecipeMap; +import gregtech.api.unification.material.type.Material; +import gregtech.api.util.GTUtility; +import gregtech.api.util.InventoryUtils; +import net.minecraft.block.state.IBlockState; +import net.minecraft.client.resources.I18n; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.world.World; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.IFluidTank; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; +import net.minecraftforge.items.IItemHandlerModifiable; + +import javax.annotation.Nullable; +import java.text.DecimalFormat; +import java.util.*; +import java.util.function.Predicate; +import java.util.stream.IntStream; + +public abstract class LargeSimpleRecipeMapDistinctMultiblockController extends RecipeMapDistinctMultiblockController { + + private int EUtPercentage = 100; + private int durationPercentage = 100; + private int chancePercentage = 100; + private int stack = 1; + public long maxVoltage = 0; + + DecimalFormat formatter = new DecimalFormat("#0.0"); + + /** + * Create large multiblock machine for simple machine. + *

+ * Percentage : 80 => 0.8 mean lower + * Percentage : 120 => 1.2 mean higher + * + * @param metaTileEntityId + * @param recipeMap + * @param EUtPercentage should be between 0 ~ Integer.MAX_VALUE, Default should be 100 + * @param durationPercentage should be between 0 ~ Integer.MAX_VALUE, Default should be 100 + * @param chancePercentage should be between 0 ~ Integer.MAX_VALUE, Default should be 100 + * @param stack should be between 0 ~ Integer.MAX_VALUE, Default should be 1 + */ + public LargeSimpleRecipeMapDistinctMultiblockController(ResourceLocation metaTileEntityId, RecipeMap recipeMap, int EUtPercentage, int durationPercentage, int chancePercentage, int stack) { + super(metaTileEntityId, recipeMap); + this.recipeMapWorkable = new LargeSimpleDistinctMultiblockRecipeLogic(this, EUtPercentage, durationPercentage, chancePercentage, stack); + + this.EUtPercentage = EUtPercentage; + this.durationPercentage = durationPercentage; + this.chancePercentage = chancePercentage; + this.stack = stack; + } + + @Override + @SideOnly(Side.CLIENT) + public void addInformation(ItemStack stack, @Nullable World player, List tooltip, boolean advanced) { + super.addInformation(stack, player, tooltip, advanced); + tooltip.add(I18n.format("gtadditions.multiblock.universal.tooltip.1", this.recipeMap.getLocalizedName())); + tooltip.add(I18n.format("gtadditions.multiblock.universal.tooltip.2", formatter.format(this.EUtPercentage / 100.0))); + tooltip.add(I18n.format("gtadditions.multiblock.universal.tooltip.3", formatter.format(this.durationPercentage / 100.0))); + tooltip.add(I18n.format("gtadditions.multiblock.universal.tooltip.4", this.stack)); + tooltip.add(I18n.format("gtadditions.multiblock.universal.tooltip.5", this.chancePercentage)); + } + + protected static Material getCasingMaterial(Material defaultMaterial, String materialString) { + Material mat = Material.MATERIAL_REGISTRY.getObject(materialString); + if (mat != null && mat.hasFlag(GAMaterials.GENERATE_METAL_CASING)) { + return mat; + } + return defaultMaterial; + } + + public static Predicate motorPredicate() { + return (blockWorldState) -> { + IBlockState blockState = blockWorldState.getBlockState(); + if (!(blockState.getBlock() instanceof MotorCasing)) { + return false; + } else { + MotorCasing motorCasing = (MotorCasing) blockState.getBlock(); + MotorCasing.CasingType tieredCasingType = motorCasing.getState(blockState); + MotorCasing.CasingType currentCasing = blockWorldState.getMatchContext().getOrPut("Motor", tieredCasingType); + return currentCasing.getName().equals(tieredCasingType.getName()); + } + }; + } + + public static Predicate emitterPredicate() { + return (blockWorldState) -> { + IBlockState blockState = blockWorldState.getBlockState(); + if (!(blockState.getBlock() instanceof EmitterCasing)) { + return false; + } else { + EmitterCasing motorCasing = (EmitterCasing) blockState.getBlock(); + EmitterCasing.CasingType tieredCasingType = motorCasing.getState(blockState); + EmitterCasing.CasingType currentCasing = blockWorldState.getMatchContext().getOrPut("Emitter", tieredCasingType); + return currentCasing.getName().equals(tieredCasingType.getName()); + } + }; + } + + public static Predicate conveyorPredicate() { + return (blockWorldState) -> { + IBlockState blockState = blockWorldState.getBlockState(); + if (!(blockState.getBlock() instanceof ConveyorCasing)) { + return false; + } else { + ConveyorCasing motorCasing = (ConveyorCasing) blockState.getBlock(); + ConveyorCasing.CasingType tieredCasingType = motorCasing.getState(blockState); + ConveyorCasing.CasingType currentCasing = blockWorldState.getMatchContext().getOrPut("Conveyor", tieredCasingType); + return currentCasing.getName().equals(tieredCasingType.getName()); + } + }; + } + + public static Predicate fieldGenPredicate() { + return (blockWorldState) -> { + IBlockState blockState = blockWorldState.getBlockState(); + if (!(blockState.getBlock() instanceof FieldGenCasing)) { + return false; + } else { + FieldGenCasing motorCasing = (FieldGenCasing) blockState.getBlock(); + FieldGenCasing.CasingType tieredCasingType = motorCasing.getState(blockState); + FieldGenCasing.CasingType currentCasing = blockWorldState.getMatchContext().getOrPut("FieldGen", tieredCasingType); + return currentCasing.getName().equals(tieredCasingType.getName()); + } + }; + } + + public static Predicate pistonPredicate() { + return (blockWorldState) -> { + IBlockState blockState = blockWorldState.getBlockState(); + if (!(blockState.getBlock() instanceof PistonCasing)) { + return false; + } else { + PistonCasing motorCasing = (PistonCasing) blockState.getBlock(); + PistonCasing.CasingType tieredCasingType = motorCasing.getState(blockState); + PistonCasing.CasingType currentCasing = blockWorldState.getMatchContext().getOrPut("Piston", tieredCasingType); + return currentCasing.getName().equals(tieredCasingType.getName()); + } + }; + } + + public static Predicate pumpPredicate() { + return (blockWorldState) -> { + IBlockState blockState = blockWorldState.getBlockState(); + if (!(blockState.getBlock() instanceof PumpCasing)) { + return false; + } else { + PumpCasing motorCasing = (PumpCasing) blockState.getBlock(); + PumpCasing.CasingType tieredCasingType = motorCasing.getState(blockState); + PumpCasing.CasingType currentCasing = blockWorldState.getMatchContext().getOrPut("Pump", tieredCasingType); + return currentCasing.getName().equals(tieredCasingType.getName()); + } + }; + } + + public static Predicate robotArmPredicate() { + return (blockWorldState) -> { + IBlockState blockState = blockWorldState.getBlockState(); + if (!(blockState.getBlock() instanceof RobotArmCasing)) { + return false; + } else { + RobotArmCasing motorCasing = (RobotArmCasing) blockState.getBlock(); + RobotArmCasing.CasingType tieredCasingType = motorCasing.getState(blockState); + RobotArmCasing.CasingType currentCasing = blockWorldState.getMatchContext().getOrPut("RobotArm", tieredCasingType); + return currentCasing.getName().equals(tieredCasingType.getName()); + } + }; + } + + public static Predicate sensorPredicate() { + return (blockWorldState) -> { + IBlockState blockState = blockWorldState.getBlockState(); + if (!(blockState.getBlock() instanceof SensorCasing)) { + return false; + } else { + SensorCasing motorCasing = (SensorCasing) blockState.getBlock(); + SensorCasing.CasingType tieredCasingType = motorCasing.getState(blockState); + SensorCasing.CasingType currentCasing = blockWorldState.getMatchContext().getOrPut("Sensor", tieredCasingType); + return currentCasing.getName().equals(tieredCasingType.getName()); + } + }; + } + + @Override + public void invalidateStructure() { + super.invalidateStructure(); + this.maxVoltage = 0; + } + + @Override + public boolean checkRecipe(Recipe recipe, boolean consumeIfSuccess) { + return recipe.getEUt() < maxVoltage; + } + + @Override + protected void addDisplayText(List textList) { + super.addDisplayText(textList); + textList.add(new TextComponentTranslation("gregtech.multiblock.universal.framework", this.maxVoltage)); + } + + public static class LargeSimpleDistinctMultiblockRecipeLogic extends DistinctBusesMultiblockRecipeLogic { + + private final int EUtPercentage; + private final int durationPercentage; + private final int chancePercentage; + private final int stack; + public RecipeMap recipeMap; + + public LargeSimpleDistinctMultiblockRecipeLogic(RecipeMapDistinctMultiblockController tileEntity, int EUtPercentage, int durationPercentage, int chancePercentage, int stack) { + super(tileEntity); + this.EUtPercentage = EUtPercentage; + this.durationPercentage = durationPercentage; + this.chancePercentage = chancePercentage; + this.stack = stack; + this.recipeMap = tileEntity.recipeMap; + } + + public int getEUtPercentage() { + return EUtPercentage; + } + + public int getDurationPercentage() { + return durationPercentage; + } + + public int getChancePercentage() { + return chancePercentage; + } + + public int getStack() { + return stack; + } + + // trySearchNewRecipe not overridden. Is this okay? Yes + + // TODO + @Override + protected Recipe findRecipe(long maxVoltage, IItemHandlerModifiable inputs, IMultipleTankHandler fluidInputs) { + Recipe recipe = super.findRecipe(maxVoltage, inputs, fluidInputs); + if (recipe != null) + return createRecipe(maxVoltage, inputs, fluidInputs, recipe); + return null; + } + + // TODO + protected Recipe createRecipe(long maxVoltage, IItemHandlerModifiable inputs, IMultipleTankHandler fluidInputs, Recipe matchingRecipe) { + int maxItemsLimit = this.stack; + int EUt; + int duration; + int currentTier = getOverclockingTier(maxVoltage); + int tierNeeded; + int minMultiplier = Integer.MAX_VALUE; + + tierNeeded = Math.max(1, getOverclockingTier(matchingRecipe.getEUt())); + maxItemsLimit *= currentTier - tierNeeded; + maxItemsLimit = Math.max(1, maxItemsLimit); + + + Set countIngredients = new HashSet<>(); + if (matchingRecipe.getInputs().size() != 0) { + this.findIngredients(countIngredients, inputs); + minMultiplier = Math.min(maxItemsLimit, this.getMinRatioItem(countIngredients, matchingRecipe, maxItemsLimit)); + } + + Map countFluid = new HashMap<>(); + if (matchingRecipe.getFluidInputs().size() != 0) { + + this.findFluid(countFluid, fluidInputs); + minMultiplier = Math.min(minMultiplier, this.getMinRatioFluid(countFluid, matchingRecipe, maxItemsLimit)); + } + + if (minMultiplier == Integer.MAX_VALUE) { + GALog.logger.error("Cannot calculate ratio of items for processing array"); + return null; + } + + EUt = matchingRecipe.getEUt(); + duration = matchingRecipe.getDuration(); + + List newRecipeInputs = new ArrayList<>(); + List newFluidInputs = new ArrayList<>(); + List outputI = new ArrayList<>(); + List outputF = new ArrayList<>(); + this.multiplyInputsAndOutputs(newRecipeInputs, newFluidInputs, outputI, outputF, matchingRecipe, minMultiplier); + + // determine if there is enough room in the output to fit all of this + boolean canFitOutputs = InventoryUtils.simulateItemStackMerge(outputI, this.getOutputInventory()); + // if there isn't, we can't process this recipe. + if (!canFitOutputs) + return null; + + + RecipeBuilder newRecipe = recipeMap.recipeBuilder() + .inputsIngredients(newRecipeInputs) + .fluidInputs(newFluidInputs) + .outputs(outputI) + .fluidOutputs(outputF) + .EUt((int) Math.max(1, EUt * this.EUtPercentage / 100)) + .duration((int) Math.max(3, duration * (this.durationPercentage / 100.0))); + + copyChancedItemOutputs(newRecipe, matchingRecipe, minMultiplier); + + return newRecipe.build().getResult(); + } + + // TODO + protected void copyChancedItemOutputs(RecipeBuilder newRecipe, Recipe oldRecipe, int multiplier) { + for (Recipe.ChanceEntry s : oldRecipe.getChancedOutputs()) { + int chance = Math.min(10000, s.getChance() * this.chancePercentage / 100); + int boost = s.getBoostPerTier() * this.chancePercentage / 100; + IntStream.range(0, multiplier).forEach(value -> { + ItemStack itemStack = s.getItemStack().copy(); + newRecipe.chancedOutput(itemStack, chance, boost); + }); + } + } + + // TODO + protected void findIngredients(Set countIngredients, IItemHandlerModifiable inputs) { + for (int slot = 0; slot < inputs.getSlots(); slot++) { + ItemStack wholeItemStack = inputs.getStackInSlot(slot); + String name = wholeItemStack.getItem().getUnlocalizedNameInefficiently(wholeItemStack); + // skip empty slots + if (name.equals("tile.air")) { + continue; + } + boolean found = false; + for (ItemStack i : countIngredients) { + if (ItemStack.areItemsEqual(i, wholeItemStack)) { + i.setCount(i.getCount() + wholeItemStack.getCount()); + found = true; + break; + } + } + if (!found) { + countIngredients.add(wholeItemStack.copy()); + } + } + } + + // TODO + protected int getMinRatioItem(Set countIngredients, Recipe r, int maxItemsLimit) { + int minMultiplier = Integer.MAX_VALUE; + for (CountableIngredient ci : r.getInputs()) { + if (ci.getCount() == 0) { + continue; + } + for (ItemStack wholeItemStack : countIngredients) { + if (ci.getIngredient().apply(wholeItemStack)) { + int ratio = Math.min(maxItemsLimit, wholeItemStack.getCount() / ci.getCount()); + if (ratio < minMultiplier) { + minMultiplier = ratio; + } + break; + } + } + } + return minMultiplier; + } + + // TODO + protected int getMinRatioFluid(Map countFluid, Recipe r, int maxItemsLimit) { + int minMultiplier = Integer.MAX_VALUE; + for (FluidStack fs : r.getFluidInputs()) { + if (fs.amount != 0) { // skip notConsumable fluids + String name = fs.getFluid().getUnlocalizedName(); + int ratio = Math.min(maxItemsLimit, countFluid.get(name) / fs.amount); + if (ratio < minMultiplier) { + minMultiplier = ratio; + } + } + } + return minMultiplier; + } + + // TODO + protected void findFluid(Map countFluid, IMultipleTankHandler fluidInputs) { + for (IFluidTank tank : fluidInputs) { + if (tank.getFluid() != null) { + String name = tank.getFluid().getUnlocalizedName(); + if (countFluid.containsKey(name)) { + int existingValue = countFluid.get(name); + countFluid.put(name, existingValue + tank.getFluidAmount()); + } else { + countFluid.put(name, tank.getFluidAmount()); + } + } + } + } + + // TODO + protected void multiplyInputsAndOutputs(List newRecipeInputs, List newFluidInputs, List outputI, List outputF, Recipe r, int multiplier) { + for (CountableIngredient ci : r.getInputs()) { + CountableIngredient newIngredient = new CountableIngredient(ci.getIngredient(), ci.getCount() * multiplier); + newRecipeInputs.add(newIngredient); + } + for (FluidStack fs : r.getFluidInputs()) { + FluidStack newFluid = new FluidStack(fs.getFluid(), fs.amount * multiplier); + newFluidInputs.add(newFluid); + } + for (ItemStack s : r.getOutputs()) { + int num = s.getCount() * multiplier; + ItemStack itemCopy = s.copy(); + itemCopy.setCount(num); + outputI.add(itemCopy); + } + for (FluidStack f : r.getFluidOutputs()) { + int fluidNum = f.amount * multiplier; + FluidStack fluidCopy = f.copy(); + fluidCopy.amount = fluidNum; + outputF.add(fluidCopy); + } + } + + // TODO + @Override + protected void setupRecipe(Recipe recipe) { + long maxVoltage = getMaxVoltage(); + if (metaTileEntity instanceof LargeSimpleRecipeMapMultiblockController) + maxVoltage = ((LargeSimpleRecipeMapMultiblockController) metaTileEntity).maxVoltage; + int[] resultOverclock = calculateOverclock(recipe.getEUt(), maxVoltage, recipe.getDuration()); + this.progressTime = 1; + setMaxProgress(resultOverclock[1]); + this.recipeEUt = resultOverclock[0]; + this.fluidOutputs = GTUtility.copyFluidList(recipe.getFluidOutputs()); + int tier = getMachineTierForRecipe(recipe); + this.itemOutputs = GTUtility.copyStackList(recipe.getResultItemOutputs(Integer.MAX_VALUE, random, tier)); + if (this.wasActiveAndNeedsUpdate) { + this.wasActiveAndNeedsUpdate = false; + } else { + this.setActive(true); + } + } + } +} diff --git a/src/main/java/gregicadditions/capabilities/impl/RecipeMapDistinctBusesMultiblockController.java b/src/main/java/gregicadditions/capabilities/impl/RecipeMapDistinctMultiblockController.java similarity index 92% rename from src/main/java/gregicadditions/capabilities/impl/RecipeMapDistinctBusesMultiblockController.java rename to src/main/java/gregicadditions/capabilities/impl/RecipeMapDistinctMultiblockController.java index d356ecc55..ac5311ab4 100644 --- a/src/main/java/gregicadditions/capabilities/impl/RecipeMapDistinctBusesMultiblockController.java +++ b/src/main/java/gregicadditions/capabilities/impl/RecipeMapDistinctMultiblockController.java @@ -37,7 +37,7 @@ * This class serves as an alternative to {@link gregicadditions.capabilities.impl.GARecipeMapMultiblockController}, * except it treats its input buses as separate and distinct inventories for recipes. */ -public abstract class RecipeMapDistinctBusesMultiblockController extends MultiblockWithDisplayBase { +public abstract class RecipeMapDistinctMultiblockController extends MultiblockWithDisplayBase { public final RecipeMap recipeMap; protected DistinctBusesMultiblockRecipeLogic recipeMapWorkable; @@ -48,7 +48,7 @@ public abstract class RecipeMapDistinctBusesMultiblockController extends Multibl protected IMultipleTankHandler outputFluidInventory; protected IEnergyContainer energyContainer; - public RecipeMapDistinctBusesMultiblockController(ResourceLocation metaTileEntityId, RecipeMap recipeMap) { + public RecipeMapDistinctMultiblockController(ResourceLocation metaTileEntityId, RecipeMap recipeMap) { super(metaTileEntityId); this.recipeMap = recipeMap; this.recipeMapWorkable = new DistinctBusesMultiblockRecipeLogic(this); // TODO @@ -172,10 +172,10 @@ protected OrientedOverlayRenderer getFrontOverlay() { public static class DistinctBusesMultiblockRecipeLogic extends AbstractRecipeLogic { - private int lastRecipeIndex = 0; + protected int lastRecipeIndex = 0; protected ItemStack[][] lastItemInputsMatrix; - public DistinctBusesMultiblockRecipeLogic(RecipeMapDistinctBusesMultiblockController tileEntity) { + public DistinctBusesMultiblockRecipeLogic(RecipeMapDistinctMultiblockController tileEntity) { super(tileEntity, tileEntity.recipeMap); } @@ -188,30 +188,30 @@ public void updateWorkable() { } public IEnergyContainer getEnergyContainer() { - RecipeMapDistinctBusesMultiblockController controller = (RecipeMapDistinctBusesMultiblockController) metaTileEntity; + RecipeMapDistinctMultiblockController controller = (RecipeMapDistinctMultiblockController) metaTileEntity; return controller.getEnergyContainer(); } protected List getInputBuses() { - RecipeMapDistinctBusesMultiblockController controller = (RecipeMapDistinctBusesMultiblockController) metaTileEntity; + RecipeMapDistinctMultiblockController controller = (RecipeMapDistinctMultiblockController) metaTileEntity; return controller.getInputInventory(); } @Override protected IItemHandlerModifiable getOutputInventory() { - RecipeMapDistinctBusesMultiblockController controller = (RecipeMapDistinctBusesMultiblockController) metaTileEntity; + RecipeMapDistinctMultiblockController controller = (RecipeMapDistinctMultiblockController) metaTileEntity; return controller.getOutputInventory(); } @Override protected IMultipleTankHandler getInputTank() { - RecipeMapDistinctBusesMultiblockController controller = (RecipeMapDistinctBusesMultiblockController) metaTileEntity; + RecipeMapDistinctMultiblockController controller = (RecipeMapDistinctMultiblockController) metaTileEntity; return controller.getInputFluidInventory(); } @Override protected IMultipleTankHandler getOutputTank() { - RecipeMapDistinctBusesMultiblockController controller = (RecipeMapDistinctBusesMultiblockController) metaTileEntity; + RecipeMapDistinctMultiblockController controller = (RecipeMapDistinctMultiblockController) metaTileEntity; return controller.getOutputFluidInventory(); } @@ -277,7 +277,7 @@ protected void trySearchNewRecipe() { } protected boolean setupAndConsumeRecipeInputs(Recipe recipe, int index) { - RecipeMapDistinctBusesMultiblockController controller = (RecipeMapDistinctBusesMultiblockController) metaTileEntity; + RecipeMapDistinctMultiblockController controller = (RecipeMapDistinctMultiblockController) metaTileEntity; if (controller.checkRecipe(recipe, false)) { int[] resultOverclock = calculateOverclock(recipe.getEUt(), recipe.getDuration()); @@ -303,7 +303,7 @@ protected boolean setupAndConsumeRecipeInputs(Recipe recipe, int index) { // Replacing this for optimization reasons protected boolean checkRecipeInputsDirty(IItemHandler inputs, IMultipleTankHandler fluidInputs, int index) { boolean shouldRecheckRecipe = false; - RecipeMapDistinctBusesMultiblockController controller = (RecipeMapDistinctBusesMultiblockController) metaTileEntity; + RecipeMapDistinctMultiblockController controller = (RecipeMapDistinctMultiblockController) metaTileEntity; if (lastItemInputsMatrix == null || lastItemInputsMatrix.length != controller.inputInventory.size()) { lastItemInputsMatrix = new ItemStack[controller.inputInventory.size()][]; GALog.logger.info("Num buses: " + controller.inputInventory.size()); diff --git a/src/main/java/gregicadditions/machines/multi/simple/TileEntityLargeExtruder.java b/src/main/java/gregicadditions/machines/multi/simple/TileEntityLargeExtruder.java index d86586cd8..1ae5bfa84 100644 --- a/src/main/java/gregicadditions/machines/multi/simple/TileEntityLargeExtruder.java +++ b/src/main/java/gregicadditions/machines/multi/simple/TileEntityLargeExtruder.java @@ -1,6 +1,7 @@ package gregicadditions.machines.multi.simple; import gregicadditions.GAConfig; +import gregicadditions.capabilities.impl.LargeSimpleRecipeMapDistinctMultiblockController; import gregicadditions.item.GAMetaBlocks; import gregicadditions.item.components.PistonCasing; import gregtech.api.metatileentity.MetaTileEntity; @@ -23,7 +24,7 @@ import static gregicadditions.GAMaterials.Inconel625; -public class TileEntityLargeExtruder extends LargeSimpleRecipeMapMultiblockController { +public class TileEntityLargeExtruder extends LargeSimpleRecipeMapDistinctMultiblockController { private static final MultiblockAbility[] ALLOWED_ABILITIES = {MultiblockAbility.IMPORT_ITEMS, MultiblockAbility.EXPORT_ITEMS, MultiblockAbility.IMPORT_FLUIDS, MultiblockAbility.EXPORT_FLUIDS, MultiblockAbility.INPUT_ENERGY}; From d1240c8861d439365b96babba24c854e2648a55e Mon Sep 17 00:00:00 2001 From: DStrand1 Date: Thu, 15 Apr 2021 01:30:25 -0500 Subject: [PATCH 14/17] Cleanup, combine into Large Simple Multi --- ...RecipeMapDistinctMultiblockController.java | 453 ------------------ ...RecipeMapDistinctMultiblockController.java | 365 -------------- ...geSimpleRecipeMapMultiblockController.java | 178 ++++++- .../multi/simple/TileEntityLargeExtruder.java | 3 +- .../assets/gtadditions/lang/en_us.lang | 4 + 5 files changed, 164 insertions(+), 839 deletions(-) delete mode 100644 src/main/java/gregicadditions/capabilities/impl/LargeSimpleRecipeMapDistinctMultiblockController.java delete mode 100644 src/main/java/gregicadditions/capabilities/impl/RecipeMapDistinctMultiblockController.java diff --git a/src/main/java/gregicadditions/capabilities/impl/LargeSimpleRecipeMapDistinctMultiblockController.java b/src/main/java/gregicadditions/capabilities/impl/LargeSimpleRecipeMapDistinctMultiblockController.java deleted file mode 100644 index 0fa6b7bf3..000000000 --- a/src/main/java/gregicadditions/capabilities/impl/LargeSimpleRecipeMapDistinctMultiblockController.java +++ /dev/null @@ -1,453 +0,0 @@ -package gregicadditions.capabilities.impl; - -import gregicadditions.GAMaterials; -import gregicadditions.item.components.*; -import gregicadditions.machines.multi.simple.LargeSimpleRecipeMapMultiblockController; -import gregicadditions.utils.GALog; -import gregicadditions.utils.Tuple; -import gregtech.api.capability.IMultipleTankHandler; -import gregtech.api.metatileentity.multiblock.MultiblockAbility; -import gregtech.api.metatileentity.multiblock.RecipeMapMultiblockController; -import gregtech.api.multiblock.BlockWorldState; -import gregtech.api.recipes.CountableIngredient; -import gregtech.api.recipes.Recipe; -import gregtech.api.recipes.RecipeBuilder; -import gregtech.api.recipes.RecipeMap; -import gregtech.api.unification.material.type.Material; -import gregtech.api.util.GTUtility; -import gregtech.api.util.InventoryUtils; -import net.minecraft.block.state.IBlockState; -import net.minecraft.client.resources.I18n; -import net.minecraft.item.ItemStack; -import net.minecraft.util.ResourceLocation; -import net.minecraft.util.text.ITextComponent; -import net.minecraft.util.text.TextComponentTranslation; -import net.minecraft.world.World; -import net.minecraftforge.fluids.FluidStack; -import net.minecraftforge.fluids.IFluidTank; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; -import net.minecraftforge.items.IItemHandlerModifiable; - -import javax.annotation.Nullable; -import java.text.DecimalFormat; -import java.util.*; -import java.util.function.Predicate; -import java.util.stream.IntStream; - -public abstract class LargeSimpleRecipeMapDistinctMultiblockController extends RecipeMapDistinctMultiblockController { - - private int EUtPercentage = 100; - private int durationPercentage = 100; - private int chancePercentage = 100; - private int stack = 1; - public long maxVoltage = 0; - - DecimalFormat formatter = new DecimalFormat("#0.0"); - - /** - * Create large multiblock machine for simple machine. - *

- * Percentage : 80 => 0.8 mean lower - * Percentage : 120 => 1.2 mean higher - * - * @param metaTileEntityId - * @param recipeMap - * @param EUtPercentage should be between 0 ~ Integer.MAX_VALUE, Default should be 100 - * @param durationPercentage should be between 0 ~ Integer.MAX_VALUE, Default should be 100 - * @param chancePercentage should be between 0 ~ Integer.MAX_VALUE, Default should be 100 - * @param stack should be between 0 ~ Integer.MAX_VALUE, Default should be 1 - */ - public LargeSimpleRecipeMapDistinctMultiblockController(ResourceLocation metaTileEntityId, RecipeMap recipeMap, int EUtPercentage, int durationPercentage, int chancePercentage, int stack) { - super(metaTileEntityId, recipeMap); - this.recipeMapWorkable = new LargeSimpleDistinctMultiblockRecipeLogic(this, EUtPercentage, durationPercentage, chancePercentage, stack); - - this.EUtPercentage = EUtPercentage; - this.durationPercentage = durationPercentage; - this.chancePercentage = chancePercentage; - this.stack = stack; - } - - @Override - @SideOnly(Side.CLIENT) - public void addInformation(ItemStack stack, @Nullable World player, List tooltip, boolean advanced) { - super.addInformation(stack, player, tooltip, advanced); - tooltip.add(I18n.format("gtadditions.multiblock.universal.tooltip.1", this.recipeMap.getLocalizedName())); - tooltip.add(I18n.format("gtadditions.multiblock.universal.tooltip.2", formatter.format(this.EUtPercentage / 100.0))); - tooltip.add(I18n.format("gtadditions.multiblock.universal.tooltip.3", formatter.format(this.durationPercentage / 100.0))); - tooltip.add(I18n.format("gtadditions.multiblock.universal.tooltip.4", this.stack)); - tooltip.add(I18n.format("gtadditions.multiblock.universal.tooltip.5", this.chancePercentage)); - } - - protected static Material getCasingMaterial(Material defaultMaterial, String materialString) { - Material mat = Material.MATERIAL_REGISTRY.getObject(materialString); - if (mat != null && mat.hasFlag(GAMaterials.GENERATE_METAL_CASING)) { - return mat; - } - return defaultMaterial; - } - - public static Predicate motorPredicate() { - return (blockWorldState) -> { - IBlockState blockState = blockWorldState.getBlockState(); - if (!(blockState.getBlock() instanceof MotorCasing)) { - return false; - } else { - MotorCasing motorCasing = (MotorCasing) blockState.getBlock(); - MotorCasing.CasingType tieredCasingType = motorCasing.getState(blockState); - MotorCasing.CasingType currentCasing = blockWorldState.getMatchContext().getOrPut("Motor", tieredCasingType); - return currentCasing.getName().equals(tieredCasingType.getName()); - } - }; - } - - public static Predicate emitterPredicate() { - return (blockWorldState) -> { - IBlockState blockState = blockWorldState.getBlockState(); - if (!(blockState.getBlock() instanceof EmitterCasing)) { - return false; - } else { - EmitterCasing motorCasing = (EmitterCasing) blockState.getBlock(); - EmitterCasing.CasingType tieredCasingType = motorCasing.getState(blockState); - EmitterCasing.CasingType currentCasing = blockWorldState.getMatchContext().getOrPut("Emitter", tieredCasingType); - return currentCasing.getName().equals(tieredCasingType.getName()); - } - }; - } - - public static Predicate conveyorPredicate() { - return (blockWorldState) -> { - IBlockState blockState = blockWorldState.getBlockState(); - if (!(blockState.getBlock() instanceof ConveyorCasing)) { - return false; - } else { - ConveyorCasing motorCasing = (ConveyorCasing) blockState.getBlock(); - ConveyorCasing.CasingType tieredCasingType = motorCasing.getState(blockState); - ConveyorCasing.CasingType currentCasing = blockWorldState.getMatchContext().getOrPut("Conveyor", tieredCasingType); - return currentCasing.getName().equals(tieredCasingType.getName()); - } - }; - } - - public static Predicate fieldGenPredicate() { - return (blockWorldState) -> { - IBlockState blockState = blockWorldState.getBlockState(); - if (!(blockState.getBlock() instanceof FieldGenCasing)) { - return false; - } else { - FieldGenCasing motorCasing = (FieldGenCasing) blockState.getBlock(); - FieldGenCasing.CasingType tieredCasingType = motorCasing.getState(blockState); - FieldGenCasing.CasingType currentCasing = blockWorldState.getMatchContext().getOrPut("FieldGen", tieredCasingType); - return currentCasing.getName().equals(tieredCasingType.getName()); - } - }; - } - - public static Predicate pistonPredicate() { - return (blockWorldState) -> { - IBlockState blockState = blockWorldState.getBlockState(); - if (!(blockState.getBlock() instanceof PistonCasing)) { - return false; - } else { - PistonCasing motorCasing = (PistonCasing) blockState.getBlock(); - PistonCasing.CasingType tieredCasingType = motorCasing.getState(blockState); - PistonCasing.CasingType currentCasing = blockWorldState.getMatchContext().getOrPut("Piston", tieredCasingType); - return currentCasing.getName().equals(tieredCasingType.getName()); - } - }; - } - - public static Predicate pumpPredicate() { - return (blockWorldState) -> { - IBlockState blockState = blockWorldState.getBlockState(); - if (!(blockState.getBlock() instanceof PumpCasing)) { - return false; - } else { - PumpCasing motorCasing = (PumpCasing) blockState.getBlock(); - PumpCasing.CasingType tieredCasingType = motorCasing.getState(blockState); - PumpCasing.CasingType currentCasing = blockWorldState.getMatchContext().getOrPut("Pump", tieredCasingType); - return currentCasing.getName().equals(tieredCasingType.getName()); - } - }; - } - - public static Predicate robotArmPredicate() { - return (blockWorldState) -> { - IBlockState blockState = blockWorldState.getBlockState(); - if (!(blockState.getBlock() instanceof RobotArmCasing)) { - return false; - } else { - RobotArmCasing motorCasing = (RobotArmCasing) blockState.getBlock(); - RobotArmCasing.CasingType tieredCasingType = motorCasing.getState(blockState); - RobotArmCasing.CasingType currentCasing = blockWorldState.getMatchContext().getOrPut("RobotArm", tieredCasingType); - return currentCasing.getName().equals(tieredCasingType.getName()); - } - }; - } - - public static Predicate sensorPredicate() { - return (blockWorldState) -> { - IBlockState blockState = blockWorldState.getBlockState(); - if (!(blockState.getBlock() instanceof SensorCasing)) { - return false; - } else { - SensorCasing motorCasing = (SensorCasing) blockState.getBlock(); - SensorCasing.CasingType tieredCasingType = motorCasing.getState(blockState); - SensorCasing.CasingType currentCasing = blockWorldState.getMatchContext().getOrPut("Sensor", tieredCasingType); - return currentCasing.getName().equals(tieredCasingType.getName()); - } - }; - } - - @Override - public void invalidateStructure() { - super.invalidateStructure(); - this.maxVoltage = 0; - } - - @Override - public boolean checkRecipe(Recipe recipe, boolean consumeIfSuccess) { - return recipe.getEUt() < maxVoltage; - } - - @Override - protected void addDisplayText(List textList) { - super.addDisplayText(textList); - textList.add(new TextComponentTranslation("gregtech.multiblock.universal.framework", this.maxVoltage)); - } - - public static class LargeSimpleDistinctMultiblockRecipeLogic extends DistinctBusesMultiblockRecipeLogic { - - private final int EUtPercentage; - private final int durationPercentage; - private final int chancePercentage; - private final int stack; - public RecipeMap recipeMap; - - public LargeSimpleDistinctMultiblockRecipeLogic(RecipeMapDistinctMultiblockController tileEntity, int EUtPercentage, int durationPercentage, int chancePercentage, int stack) { - super(tileEntity); - this.EUtPercentage = EUtPercentage; - this.durationPercentage = durationPercentage; - this.chancePercentage = chancePercentage; - this.stack = stack; - this.recipeMap = tileEntity.recipeMap; - } - - public int getEUtPercentage() { - return EUtPercentage; - } - - public int getDurationPercentage() { - return durationPercentage; - } - - public int getChancePercentage() { - return chancePercentage; - } - - public int getStack() { - return stack; - } - - // trySearchNewRecipe not overridden. Is this okay? Yes - - // TODO - @Override - protected Recipe findRecipe(long maxVoltage, IItemHandlerModifiable inputs, IMultipleTankHandler fluidInputs) { - Recipe recipe = super.findRecipe(maxVoltage, inputs, fluidInputs); - if (recipe != null) - return createRecipe(maxVoltage, inputs, fluidInputs, recipe); - return null; - } - - // TODO - protected Recipe createRecipe(long maxVoltage, IItemHandlerModifiable inputs, IMultipleTankHandler fluidInputs, Recipe matchingRecipe) { - int maxItemsLimit = this.stack; - int EUt; - int duration; - int currentTier = getOverclockingTier(maxVoltage); - int tierNeeded; - int minMultiplier = Integer.MAX_VALUE; - - tierNeeded = Math.max(1, getOverclockingTier(matchingRecipe.getEUt())); - maxItemsLimit *= currentTier - tierNeeded; - maxItemsLimit = Math.max(1, maxItemsLimit); - - - Set countIngredients = new HashSet<>(); - if (matchingRecipe.getInputs().size() != 0) { - this.findIngredients(countIngredients, inputs); - minMultiplier = Math.min(maxItemsLimit, this.getMinRatioItem(countIngredients, matchingRecipe, maxItemsLimit)); - } - - Map countFluid = new HashMap<>(); - if (matchingRecipe.getFluidInputs().size() != 0) { - - this.findFluid(countFluid, fluidInputs); - minMultiplier = Math.min(minMultiplier, this.getMinRatioFluid(countFluid, matchingRecipe, maxItemsLimit)); - } - - if (minMultiplier == Integer.MAX_VALUE) { - GALog.logger.error("Cannot calculate ratio of items for processing array"); - return null; - } - - EUt = matchingRecipe.getEUt(); - duration = matchingRecipe.getDuration(); - - List newRecipeInputs = new ArrayList<>(); - List newFluidInputs = new ArrayList<>(); - List outputI = new ArrayList<>(); - List outputF = new ArrayList<>(); - this.multiplyInputsAndOutputs(newRecipeInputs, newFluidInputs, outputI, outputF, matchingRecipe, minMultiplier); - - // determine if there is enough room in the output to fit all of this - boolean canFitOutputs = InventoryUtils.simulateItemStackMerge(outputI, this.getOutputInventory()); - // if there isn't, we can't process this recipe. - if (!canFitOutputs) - return null; - - - RecipeBuilder newRecipe = recipeMap.recipeBuilder() - .inputsIngredients(newRecipeInputs) - .fluidInputs(newFluidInputs) - .outputs(outputI) - .fluidOutputs(outputF) - .EUt((int) Math.max(1, EUt * this.EUtPercentage / 100)) - .duration((int) Math.max(3, duration * (this.durationPercentage / 100.0))); - - copyChancedItemOutputs(newRecipe, matchingRecipe, minMultiplier); - - return newRecipe.build().getResult(); - } - - // TODO - protected void copyChancedItemOutputs(RecipeBuilder newRecipe, Recipe oldRecipe, int multiplier) { - for (Recipe.ChanceEntry s : oldRecipe.getChancedOutputs()) { - int chance = Math.min(10000, s.getChance() * this.chancePercentage / 100); - int boost = s.getBoostPerTier() * this.chancePercentage / 100; - IntStream.range(0, multiplier).forEach(value -> { - ItemStack itemStack = s.getItemStack().copy(); - newRecipe.chancedOutput(itemStack, chance, boost); - }); - } - } - - // TODO - protected void findIngredients(Set countIngredients, IItemHandlerModifiable inputs) { - for (int slot = 0; slot < inputs.getSlots(); slot++) { - ItemStack wholeItemStack = inputs.getStackInSlot(slot); - String name = wholeItemStack.getItem().getUnlocalizedNameInefficiently(wholeItemStack); - // skip empty slots - if (name.equals("tile.air")) { - continue; - } - boolean found = false; - for (ItemStack i : countIngredients) { - if (ItemStack.areItemsEqual(i, wholeItemStack)) { - i.setCount(i.getCount() + wholeItemStack.getCount()); - found = true; - break; - } - } - if (!found) { - countIngredients.add(wholeItemStack.copy()); - } - } - } - - // TODO - protected int getMinRatioItem(Set countIngredients, Recipe r, int maxItemsLimit) { - int minMultiplier = Integer.MAX_VALUE; - for (CountableIngredient ci : r.getInputs()) { - if (ci.getCount() == 0) { - continue; - } - for (ItemStack wholeItemStack : countIngredients) { - if (ci.getIngredient().apply(wholeItemStack)) { - int ratio = Math.min(maxItemsLimit, wholeItemStack.getCount() / ci.getCount()); - if (ratio < minMultiplier) { - minMultiplier = ratio; - } - break; - } - } - } - return minMultiplier; - } - - // TODO - protected int getMinRatioFluid(Map countFluid, Recipe r, int maxItemsLimit) { - int minMultiplier = Integer.MAX_VALUE; - for (FluidStack fs : r.getFluidInputs()) { - if (fs.amount != 0) { // skip notConsumable fluids - String name = fs.getFluid().getUnlocalizedName(); - int ratio = Math.min(maxItemsLimit, countFluid.get(name) / fs.amount); - if (ratio < minMultiplier) { - minMultiplier = ratio; - } - } - } - return minMultiplier; - } - - // TODO - protected void findFluid(Map countFluid, IMultipleTankHandler fluidInputs) { - for (IFluidTank tank : fluidInputs) { - if (tank.getFluid() != null) { - String name = tank.getFluid().getUnlocalizedName(); - if (countFluid.containsKey(name)) { - int existingValue = countFluid.get(name); - countFluid.put(name, existingValue + tank.getFluidAmount()); - } else { - countFluid.put(name, tank.getFluidAmount()); - } - } - } - } - - // TODO - protected void multiplyInputsAndOutputs(List newRecipeInputs, List newFluidInputs, List outputI, List outputF, Recipe r, int multiplier) { - for (CountableIngredient ci : r.getInputs()) { - CountableIngredient newIngredient = new CountableIngredient(ci.getIngredient(), ci.getCount() * multiplier); - newRecipeInputs.add(newIngredient); - } - for (FluidStack fs : r.getFluidInputs()) { - FluidStack newFluid = new FluidStack(fs.getFluid(), fs.amount * multiplier); - newFluidInputs.add(newFluid); - } - for (ItemStack s : r.getOutputs()) { - int num = s.getCount() * multiplier; - ItemStack itemCopy = s.copy(); - itemCopy.setCount(num); - outputI.add(itemCopy); - } - for (FluidStack f : r.getFluidOutputs()) { - int fluidNum = f.amount * multiplier; - FluidStack fluidCopy = f.copy(); - fluidCopy.amount = fluidNum; - outputF.add(fluidCopy); - } - } - - // TODO - @Override - protected void setupRecipe(Recipe recipe) { - long maxVoltage = getMaxVoltage(); - if (metaTileEntity instanceof LargeSimpleRecipeMapMultiblockController) - maxVoltage = ((LargeSimpleRecipeMapMultiblockController) metaTileEntity).maxVoltage; - int[] resultOverclock = calculateOverclock(recipe.getEUt(), maxVoltage, recipe.getDuration()); - this.progressTime = 1; - setMaxProgress(resultOverclock[1]); - this.recipeEUt = resultOverclock[0]; - this.fluidOutputs = GTUtility.copyFluidList(recipe.getFluidOutputs()); - int tier = getMachineTierForRecipe(recipe); - this.itemOutputs = GTUtility.copyStackList(recipe.getResultItemOutputs(Integer.MAX_VALUE, random, tier)); - if (this.wasActiveAndNeedsUpdate) { - this.wasActiveAndNeedsUpdate = false; - } else { - this.setActive(true); - } - } - } -} diff --git a/src/main/java/gregicadditions/capabilities/impl/RecipeMapDistinctMultiblockController.java b/src/main/java/gregicadditions/capabilities/impl/RecipeMapDistinctMultiblockController.java deleted file mode 100644 index ac5311ab4..000000000 --- a/src/main/java/gregicadditions/capabilities/impl/RecipeMapDistinctMultiblockController.java +++ /dev/null @@ -1,365 +0,0 @@ -package gregicadditions.capabilities.impl; - -import codechicken.lib.render.CCRenderState; -import codechicken.lib.render.pipeline.IVertexOperation; -import codechicken.lib.vec.Matrix4; -import com.google.common.collect.Lists; -import gregicadditions.GAUtility; -import gregicadditions.GAValues; -import gregicadditions.utils.GALog; -import gregtech.api.capability.IEnergyContainer; -import gregtech.api.capability.IMultipleTankHandler; -import gregtech.api.capability.impl.*; -import gregtech.api.metatileentity.MetaTileEntity; -import gregtech.api.metatileentity.multiblock.IMultiblockPart; -import gregtech.api.metatileentity.multiblock.MultiblockAbility; -import gregtech.api.metatileentity.multiblock.MultiblockWithDisplayBase; -import gregtech.api.multiblock.PatternMatchContext; -import gregtech.api.recipes.Recipe; -import gregtech.api.recipes.RecipeMap; -import gregtech.api.render.OrientedOverlayRenderer; -import gregtech.api.render.Textures; -import net.minecraft.item.ItemStack; -import net.minecraft.util.ResourceLocation; -import net.minecraft.util.text.ITextComponent; -import net.minecraft.util.text.Style; -import net.minecraft.util.text.TextComponentTranslation; -import net.minecraft.util.text.TextFormatting; -import net.minecraftforge.fluids.FluidStack; -import net.minecraftforge.items.IItemHandler; -import net.minecraftforge.items.IItemHandlerModifiable; -import net.minecraftforge.items.ItemStackHandler; - -import javax.annotation.Nonnull; -import java.util.*; - -/** - * This class serves as an alternative to {@link gregicadditions.capabilities.impl.GARecipeMapMultiblockController}, - * except it treats its input buses as separate and distinct inventories for recipes. - */ -public abstract class RecipeMapDistinctMultiblockController extends MultiblockWithDisplayBase { - - public final RecipeMap recipeMap; - protected DistinctBusesMultiblockRecipeLogic recipeMapWorkable; - - protected List inputInventory; - protected IItemHandlerModifiable outputInventory; - protected IMultipleTankHandler inputFluidInventory; - protected IMultipleTankHandler outputFluidInventory; - protected IEnergyContainer energyContainer; - - public RecipeMapDistinctMultiblockController(ResourceLocation metaTileEntityId, RecipeMap recipeMap) { - super(metaTileEntityId); - this.recipeMap = recipeMap; - this.recipeMapWorkable = new DistinctBusesMultiblockRecipeLogic(this); // TODO - resetTileAbilities(); - } - - public IEnergyContainer getEnergyContainer() { - return energyContainer; - } - - public List getInputInventory() { - return inputInventory; - } - - public IItemHandlerModifiable getOutputInventory() { - return outputInventory; - } - - public IMultipleTankHandler getInputFluidInventory() { - return inputFluidInventory; - } - - public IMultipleTankHandler getOutputFluidInventory() { - return outputFluidInventory; - } - - public boolean checkRecipe(Recipe recipe, boolean consumeIfSuccess) { - return true; - } - - @Override - protected void formStructure(PatternMatchContext context) { - super.formStructure(context); - initializeAbilities(); - } - - @Override - public void invalidateStructure() { - super.invalidateStructure(); - resetTileAbilities(); - } - - @Override - protected void updateFormedValid() { - this.recipeMapWorkable.updateWorkable(); - } - - private void initializeAbilities() { - this.inputInventory = getAbilities(MultiblockAbility.IMPORT_ITEMS); - this.inputFluidInventory = new FluidTankList(allowSameFluidFillForOutputs(), getAbilities(MultiblockAbility.IMPORT_FLUIDS)); - this.outputInventory = new ItemHandlerList(getAbilities(MultiblockAbility.EXPORT_ITEMS)); - this.outputFluidInventory = new FluidTankList(allowSameFluidFillForOutputs(), getAbilities(MultiblockAbility.EXPORT_FLUIDS)); - this.energyContainer = new EnergyContainerList(getAbilities(MultiblockAbility.INPUT_ENERGY)); - } - - private void resetTileAbilities() { - this.inputInventory = new ArrayList<>(); - this.inputFluidInventory = new FluidTankList(true); - this.outputInventory = new ItemStackHandler(0); - this.outputFluidInventory = new FluidTankList(true); - this.energyContainer = new EnergyContainerList(Lists.newArrayList()); - } - - protected boolean allowSameFluidFillForOutputs() { - return true; - } - - // TODO Maybe do more in here? - @Override - protected void addDisplayText(List textList) { - super.addDisplayText(textList); - if (isStructureFormed()) { - IEnergyContainer energyContainer = recipeMapWorkable.getEnergyContainer(); - if (energyContainer != null && energyContainer.getEnergyCapacity() > 0) { - long maxVoltage = energyContainer.getInputVoltage(); - String voltageName = GAValues.VN[GAUtility.getTierByVoltage(maxVoltage)]; - textList.add(new TextComponentTranslation("gregtech.multiblock.max_energy_per_tick", maxVoltage, voltageName)); - } - - if (!recipeMapWorkable.isWorkingEnabled()) { - textList.add(new TextComponentTranslation("gregtech.multiblock.work_paused")); - - } else if (recipeMapWorkable.isActive()) { - textList.add(new TextComponentTranslation("gregtech.multiblock.running")); - int currentProgress = (int) (recipeMapWorkable.getProgressPercent() * 100); - textList.add(new TextComponentTranslation("gregtech.multiblock.progress", currentProgress)); - } else { - textList.add(new TextComponentTranslation("gregtech.multiblock.idling")); - } - - if (recipeMapWorkable.isHasNotEnoughEnergy()) { - textList.add(new TextComponentTranslation("gregtech.multiblock.not_enough_energy").setStyle(new Style().setColor(TextFormatting.RED))); - } - } - } - - @Override - protected boolean checkStructureComponents(List parts, Map, List> abilities) { - int itemInputsCount = abilities.getOrDefault(MultiblockAbility.IMPORT_ITEMS, Collections.emptyList()) - .stream().map(it -> (IItemHandler) it).mapToInt(IItemHandler::getSlots).sum(); - int fluidInputsCount = abilities.getOrDefault(MultiblockAbility.IMPORT_FLUIDS, Collections.emptyList()).size(); - return itemInputsCount >= recipeMap.getMinInputs() && - fluidInputsCount >= recipeMap.getMinFluidInputs() && - abilities.containsKey(MultiblockAbility.INPUT_ENERGY); - } - - @Override - public void renderMetaTileEntity(CCRenderState renderState, Matrix4 translation, IVertexOperation[] pipeline) { - super.renderMetaTileEntity(renderState, translation, pipeline); - this.getFrontOverlay().render(renderState, translation, pipeline, getFrontFacing(), recipeMapWorkable.isActive()); - } - - /** - * Override this method to change the Controller overlay - * @return The overlay to render on the Multiblock Controller - */ - @Nonnull - protected OrientedOverlayRenderer getFrontOverlay() { - return Textures.MULTIBLOCK_WORKABLE_OVERLAY; - } - - public static class DistinctBusesMultiblockRecipeLogic extends AbstractRecipeLogic { - - protected int lastRecipeIndex = 0; - protected ItemStack[][] lastItemInputsMatrix; - - public DistinctBusesMultiblockRecipeLogic(RecipeMapDistinctMultiblockController tileEntity) { - super(tileEntity, tileEntity.recipeMap); - } - - @Override - public void update() { - } - - public void updateWorkable() { - super.update(); - } - - public IEnergyContainer getEnergyContainer() { - RecipeMapDistinctMultiblockController controller = (RecipeMapDistinctMultiblockController) metaTileEntity; - return controller.getEnergyContainer(); - } - - protected List getInputBuses() { - RecipeMapDistinctMultiblockController controller = (RecipeMapDistinctMultiblockController) metaTileEntity; - return controller.getInputInventory(); - } - - @Override - protected IItemHandlerModifiable getOutputInventory() { - RecipeMapDistinctMultiblockController controller = (RecipeMapDistinctMultiblockController) metaTileEntity; - return controller.getOutputInventory(); - } - - @Override - protected IMultipleTankHandler getInputTank() { - RecipeMapDistinctMultiblockController controller = (RecipeMapDistinctMultiblockController) metaTileEntity; - return controller.getInputFluidInventory(); - } - - @Override - protected IMultipleTankHandler getOutputTank() { - RecipeMapDistinctMultiblockController controller = (RecipeMapDistinctMultiblockController) metaTileEntity; - return controller.getOutputFluidInventory(); - } - - @Override - protected long getEnergyStored() { - return getEnergyContainer().getEnergyStored(); - } - - @Override - protected long getEnergyCapacity() { - return getEnergyContainer().getEnergyCapacity(); - } - - @Override - protected boolean drawEnergy(int recipeEUt) { - long resultEnergy = getEnergyStored() - recipeEUt; - if (resultEnergy >= 0L && resultEnergy <= getEnergyCapacity()) { - getEnergyContainer().changeEnergy(-recipeEUt); - return true; - } else return false; - } - - @Override - protected long getMaxVoltage() { - return Math.max(getEnergyContainer().getInputVoltage(), getEnergyContainer().getOutputVoltage()); - } - - @Override - protected void trySearchNewRecipe() { - long maxVoltage = getMaxVoltage(); - Recipe currentRecipe = null; - List importInventory = getInputBuses(); - IMultipleTankHandler importFluids = getInputTank(); - - // Our caching implementation - // This guarantees that if we get a recipe cache hit, our efficiency is no different from other machines - if (previousRecipe != null && previousRecipe.matches(false, importInventory.get(lastRecipeIndex), importFluids)) { - currentRecipe = previousRecipe; - if (setupAndConsumeRecipeInputs(currentRecipe, lastRecipeIndex)) { - setupRecipe(currentRecipe); - return; - } - } - - // On a cache miss, our efficiency is much worse, as it will check - // each bus individually instead of the combined inventory all at once. - for (int i = 0; i < importInventory.size(); i++) { - IItemHandlerModifiable bus = importInventory.get(i); - boolean dirty = checkRecipeInputsDirty(bus, importFluids, i); - if (dirty || forceRecipeRecheck) { - this.forceRecipeRecheck = false; - currentRecipe = findRecipe(maxVoltage, bus, importFluids); - if (currentRecipe != null) { - this.previousRecipe = currentRecipe; - } - } - if (currentRecipe != null && setupAndConsumeRecipeInputs(currentRecipe, i)) { - lastRecipeIndex = i; - setupRecipe(currentRecipe); - break; - } - } - } - - protected boolean setupAndConsumeRecipeInputs(Recipe recipe, int index) { - RecipeMapDistinctMultiblockController controller = (RecipeMapDistinctMultiblockController) metaTileEntity; - if (controller.checkRecipe(recipe, false)) { - - int[] resultOverclock = calculateOverclock(recipe.getEUt(), recipe.getDuration()); - int totalEUt = resultOverclock[0] * resultOverclock[1]; - IItemHandlerModifiable importInventory = getInputBuses().get(index); - IItemHandlerModifiable exportInventory = getOutputInventory(); - IMultipleTankHandler importFluids = getInputTank(); - IMultipleTankHandler exportFluids = getOutputTank(); - boolean setup = (totalEUt >= 0 ? getEnergyStored() >= (totalEUt > getEnergyCapacity() / 2 ? resultOverclock[0] : totalEUt) : - (getEnergyStored() - resultOverclock[0] <= getEnergyCapacity())) && - MetaTileEntity.addItemsToItemHandler(exportInventory, true, recipe.getAllItemOutputs(exportInventory.getSlots())) && - MetaTileEntity.addFluidsToFluidHandler(exportFluids, true, recipe.getFluidOutputs()) && - recipe.matches(true, importInventory, importFluids); - - if (setup) { - controller.checkRecipe(recipe, true); - return true; - } - } - return false; - } - - // Replacing this for optimization reasons - protected boolean checkRecipeInputsDirty(IItemHandler inputs, IMultipleTankHandler fluidInputs, int index) { - boolean shouldRecheckRecipe = false; - RecipeMapDistinctMultiblockController controller = (RecipeMapDistinctMultiblockController) metaTileEntity; - if (lastItemInputsMatrix == null || lastItemInputsMatrix.length != controller.inputInventory.size()) { - lastItemInputsMatrix = new ItemStack[controller.inputInventory.size()][]; - GALog.logger.info("Num buses: " + controller.inputInventory.size()); - } - if (lastItemInputsMatrix[index] == null || lastItemInputsMatrix[index].length != inputs.getSlots()) { - this.lastItemInputsMatrix[index] = new ItemStack[inputs.getSlots()]; - Arrays.fill(lastItemInputsMatrix[index], ItemStack.EMPTY); - } - if (lastFluidInputs == null || lastFluidInputs.length != fluidInputs.getTanks()) { - this.lastFluidInputs = new FluidStack[fluidInputs.getTanks()]; - } - for (int i = 0; i < lastItemInputsMatrix[index].length; i++) { - ItemStack currentStack = inputs.getStackInSlot(i); - ItemStack lastStack = lastItemInputsMatrix[index][i]; - if (!areItemStacksEqual(currentStack, lastStack)) { - this.lastItemInputsMatrix[index][i] = currentStack.isEmpty() ? ItemStack.EMPTY : currentStack.copy(); - shouldRecheckRecipe = true; - } else if (currentStack.getCount() != lastStack.getCount()) { - lastStack.setCount(currentStack.getCount()); - shouldRecheckRecipe = true; - } - } - for (int i = 0; i < lastFluidInputs.length; i++) { - FluidStack currentStack = fluidInputs.getTankAt(i).getFluid(); - FluidStack lastStack = lastFluidInputs[i]; - if ((currentStack == null && lastStack != null) || - (currentStack != null && !currentStack.isFluidEqual(lastStack))) { - this.lastFluidInputs[i] = currentStack == null ? null : currentStack.copy(); - shouldRecheckRecipe = true; - } else if (currentStack != null && lastStack != null && - currentStack.amount != lastStack.amount) { - lastStack.amount = currentStack.amount; - shouldRecheckRecipe = true; - } - } - return shouldRecheckRecipe; - } - - // Dead methods - - @Override - protected IItemHandlerModifiable getInputInventory() { - GALog.logger.error("In old getInputInventory! Please report this error!"); - return null; // DO NOT USE!!! - } - - @Override - protected boolean setupAndConsumeRecipeInputs(Recipe recipe) { - GALog.logger.error("In old setupAndConsumeRecipeInputs! Please report this error!"); - return false; // DO NOT USE!!! - } - - @Override - protected boolean checkRecipeInputsDirty(IItemHandler inputs, IMultipleTankHandler fluidInputs) { - GALog.logger.error("In old checkRecipeInputsDirty! Please report this error!"); - return false; - } - } -} diff --git a/src/main/java/gregicadditions/machines/multi/simple/LargeSimpleRecipeMapMultiblockController.java b/src/main/java/gregicadditions/machines/multi/simple/LargeSimpleRecipeMapMultiblockController.java index 40ba30814..80bf307c3 100644 --- a/src/main/java/gregicadditions/machines/multi/simple/LargeSimpleRecipeMapMultiblockController.java +++ b/src/main/java/gregicadditions/machines/multi/simple/LargeSimpleRecipeMapMultiblockController.java @@ -5,8 +5,9 @@ import gregicadditions.capabilities.impl.GARecipeMapMultiblockController; import gregicadditions.item.components.*; import gregicadditions.utils.GALog; -import gregicadditions.utils.Tuple; import gregtech.api.capability.IMultipleTankHandler; +import gregtech.api.gui.Widget; +import gregtech.api.metatileentity.MetaTileEntity; import gregtech.api.metatileentity.multiblock.MultiblockAbility; import gregtech.api.metatileentity.multiblock.RecipeMapMultiblockController; import gregtech.api.multiblock.BlockWorldState; @@ -28,6 +29,7 @@ import net.minecraftforge.fluids.IFluidTank; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; +import net.minecraftforge.items.IItemHandler; import net.minecraftforge.items.IItemHandlerModifiable; import javax.annotation.Nullable; @@ -36,6 +38,9 @@ import java.util.function.Predicate; import java.util.stream.IntStream; +import static gregtech.api.gui.widgets.AdvancedTextWidget.withButton; +import static gregtech.api.gui.widgets.AdvancedTextWidget.withHoverTextTranslate; + abstract public class LargeSimpleRecipeMapMultiblockController extends GARecipeMapMultiblockController { private int EUtPercentage = 100; @@ -44,6 +49,15 @@ abstract public class LargeSimpleRecipeMapMultiblockController extends GARecipeM private int stack = 1; public long maxVoltage = 0; + /** + * When false, this multiblock will behave like any other. + * When true, this multiblock will treat each of its input buses as distinct, + * checking recipes for them independently. This is useful for many machines, for example the + * Large Extruder, where the player may want to put one extruder shape per bus, rather than + * one machine per extruder shape. + */ + protected boolean isDistinct = false; + DecimalFormat formatter = new DecimalFormat("#0.0"); /** @@ -216,16 +230,35 @@ public boolean checkRecipe(Recipe recipe, boolean consumeIfSuccess) { protected void addDisplayText(List textList) { super.addDisplayText(textList); textList.add(new TextComponentTranslation("gregtech.multiblock.universal.framework", this.maxVoltage)); + + ITextComponent buttonText = new TextComponentTranslation("gtadditions.multiblock.universal.distinct"); + buttonText.appendText(" "); + ITextComponent button = withButton((isDistinct ? + new TextComponentTranslation("gtadditions.multiblock.universal.distinct.yes") : + new TextComponentTranslation("gtadditions.multiblock.universal.distinct.no")), "distinct"); + withHoverTextTranslate(button, "gtadditions.multiblock.universal.distinct.info"); + buttonText.appendSibling(button); + textList.add(buttonText); + } + + @Override + protected void handleDisplayClick(String componentData, Widget.ClickData clickData) { + super.handleDisplayClick(componentData, clickData); + isDistinct = !isDistinct; } public static class LargeSimpleMultiblockRecipeLogic extends GAMultiblockRecipeLogic { - private int EUtPercentage = 100; - private int durationPercentage = 100; - private int chancePercentage = 100; - private int stack = 1; + private final int EUtPercentage; + private final int durationPercentage; + private final int chancePercentage; + private final int stack; public RecipeMap recipeMap; + // Fields used for distinct mode + protected int lastRecipeIndex = 0; + protected ItemStack[][] lastItemInputsMatrix; + public LargeSimpleMultiblockRecipeLogic(RecipeMapMultiblockController tileEntity, int EUtPercentage, int durationPercentage, int chancePercentage, int stack) { super(tileEntity); @@ -252,12 +285,24 @@ public int getStack() { return stack; } + protected List getInputBuses() { + RecipeMapMultiblockController controller = (RecipeMapMultiblockController) metaTileEntity; + return controller.getAbilities(MultiblockAbility.IMPORT_ITEMS); + } + @Override - /** - * From multi-smelter. - * - */ protected void trySearchNewRecipe() { + LargeSimpleRecipeMapMultiblockController controller = (LargeSimpleRecipeMapMultiblockController) metaTileEntity; + if (controller.isDistinct) { + trySearchNewRecipeDistinct(); + } else { + trySearchNewRecipeCombined(); + } + } + + // Combined buses code ========================================================================================= + + private void trySearchNewRecipeCombined() { long maxVoltage = getMaxVoltage(); if (metaTileEntity instanceof LargeSimpleRecipeMapMultiblockController) maxVoltage = ((LargeSimpleRecipeMapMultiblockController) metaTileEntity).maxVoltage; @@ -285,24 +330,119 @@ protected void trySearchNewRecipe() { } } - @Override - protected Recipe findRecipe(long maxVoltage, IItemHandlerModifiable inputs, IMultipleTankHandler fluidInputs) { - List itemInputs = ((RecipeMapMultiblockController) this.getMetaTileEntity()).getAbilities(MultiblockAbility.IMPORT_ITEMS); + // Distinct buses code ========================================================================================= - Tuple recipePerInput = itemInputs.stream() - .map(iItemHandlerModifiable -> new Tuple<>(recipeMap.findRecipe(maxVoltage, iItemHandlerModifiable, fluidInputs, 0), iItemHandlerModifiable)) - .filter(tuple -> tuple.getKey() != null) - .findFirst().orElse(new Tuple<>(recipeMap.findRecipe(maxVoltage, inputs, fluidInputs, 0), inputs)); + private void trySearchNewRecipeDistinct() { + long maxVoltage = getMaxVoltage(); + Recipe currentRecipe = null; + List importInventory = getInputBuses(); + IMultipleTankHandler importFluids = getInputTank(); - if (recipePerInput.getKey() == null) { - return null; + // Our caching implementation + // This guarantees that if we get a recipe cache hit, our efficiency is no different from other machines + if (previousRecipe != null && previousRecipe.matches(false, importInventory.get(lastRecipeIndex), importFluids)) { + currentRecipe = previousRecipe; + if (setupAndConsumeRecipeInputs(currentRecipe, lastRecipeIndex)) { + setupRecipe(currentRecipe); + return; + } } - return createRecipe(maxVoltage, recipePerInput.getValue(), fluidInputs, recipePerInput.getKey()); + // On a cache miss, our efficiency is much worse, as it will check + // each bus individually instead of the combined inventory all at once. + for (int i = 0; i < importInventory.size(); i++) { + IItemHandlerModifiable bus = importInventory.get(i); + boolean dirty = checkRecipeInputsDirty(bus, importFluids, i); + if (dirty || forceRecipeRecheck) { + this.forceRecipeRecheck = false; + currentRecipe = findRecipe(maxVoltage, bus, importFluids); + if (currentRecipe != null) { + this.previousRecipe = currentRecipe; + } + } + if (currentRecipe != null && setupAndConsumeRecipeInputs(currentRecipe, i)) { + lastRecipeIndex = i; + setupRecipe(currentRecipe); + break; + } + } + } + + // Replacing this for optimization reasons + protected boolean checkRecipeInputsDirty(IItemHandler inputs, IMultipleTankHandler fluidInputs, int index) { + boolean shouldRecheckRecipe = false; + if (lastItemInputsMatrix == null || lastItemInputsMatrix.length != getInputBuses().size()) { + lastItemInputsMatrix = new ItemStack[getInputBuses().size()][]; + GALog.logger.info("Num buses: " + getInputBuses().size()); + } + if (lastItemInputsMatrix[index] == null || lastItemInputsMatrix[index].length != inputs.getSlots()) { + this.lastItemInputsMatrix[index] = new ItemStack[inputs.getSlots()]; + Arrays.fill(lastItemInputsMatrix[index], ItemStack.EMPTY); + } + if (lastFluidInputs == null || lastFluidInputs.length != fluidInputs.getTanks()) { + this.lastFluidInputs = new FluidStack[fluidInputs.getTanks()]; + } + for (int i = 0; i < lastItemInputsMatrix[index].length; i++) { + ItemStack currentStack = inputs.getStackInSlot(i); + ItemStack lastStack = lastItemInputsMatrix[index][i]; + if (!areItemStacksEqual(currentStack, lastStack)) { + this.lastItemInputsMatrix[index][i] = currentStack.isEmpty() ? ItemStack.EMPTY : currentStack.copy(); + shouldRecheckRecipe = true; + } else if (currentStack.getCount() != lastStack.getCount()) { + lastStack.setCount(currentStack.getCount()); + shouldRecheckRecipe = true; + } + } + for (int i = 0; i < lastFluidInputs.length; i++) { + FluidStack currentStack = fluidInputs.getTankAt(i).getFluid(); + FluidStack lastStack = lastFluidInputs[i]; + if ((currentStack == null && lastStack != null) || + (currentStack != null && !currentStack.isFluidEqual(lastStack))) { + this.lastFluidInputs[i] = currentStack == null ? null : currentStack.copy(); + shouldRecheckRecipe = true; + } else if (currentStack != null && lastStack != null && + currentStack.amount != lastStack.amount) { + lastStack.amount = currentStack.amount; + shouldRecheckRecipe = true; + } + } + return shouldRecheckRecipe; + } + protected boolean setupAndConsumeRecipeInputs(Recipe recipe, int index) { + RecipeMapMultiblockController controller = (RecipeMapMultiblockController) metaTileEntity; + if (controller.checkRecipe(recipe, false)) { + + int[] resultOverclock = calculateOverclock(recipe.getEUt(), recipe.getDuration()); + int totalEUt = resultOverclock[0] * resultOverclock[1]; + IItemHandlerModifiable importInventory = getInputBuses().get(index); + IItemHandlerModifiable exportInventory = getOutputInventory(); + IMultipleTankHandler importFluids = getInputTank(); + IMultipleTankHandler exportFluids = getOutputTank(); + boolean setup = (totalEUt >= 0 ? getEnergyStored() >= (totalEUt > getEnergyCapacity() / 2 ? resultOverclock[0] : totalEUt) : + (getEnergyStored() - resultOverclock[0] <= getEnergyCapacity())) && + MetaTileEntity.addItemsToItemHandler(exportInventory, true, recipe.getAllItemOutputs(exportInventory.getSlots())) && + MetaTileEntity.addFluidsToFluidHandler(exportFluids, true, recipe.getFluidOutputs()) && + recipe.matches(true, importInventory, importFluids); + + if (setup) { + controller.checkRecipe(recipe, true); + return true; + } + } + return false; } + // Shared recipe generation code =============================================================================== + + @Override + protected Recipe findRecipe(long maxVoltage, IItemHandlerModifiable inputs, IMultipleTankHandler fluidInputs) { + Recipe recipe = super.findRecipe(maxVoltage, inputs, fluidInputs); + if (recipe != null) + return createRecipe(maxVoltage, inputs, fluidInputs, recipe); + return null; + } protected Recipe createRecipe(long maxVoltage, IItemHandlerModifiable inputs, IMultipleTankHandler fluidInputs, Recipe matchingRecipe) { int maxItemsLimit = this.stack; diff --git a/src/main/java/gregicadditions/machines/multi/simple/TileEntityLargeExtruder.java b/src/main/java/gregicadditions/machines/multi/simple/TileEntityLargeExtruder.java index 1ae5bfa84..d86586cd8 100644 --- a/src/main/java/gregicadditions/machines/multi/simple/TileEntityLargeExtruder.java +++ b/src/main/java/gregicadditions/machines/multi/simple/TileEntityLargeExtruder.java @@ -1,7 +1,6 @@ package gregicadditions.machines.multi.simple; import gregicadditions.GAConfig; -import gregicadditions.capabilities.impl.LargeSimpleRecipeMapDistinctMultiblockController; import gregicadditions.item.GAMetaBlocks; import gregicadditions.item.components.PistonCasing; import gregtech.api.metatileentity.MetaTileEntity; @@ -24,7 +23,7 @@ import static gregicadditions.GAMaterials.Inconel625; -public class TileEntityLargeExtruder extends LargeSimpleRecipeMapDistinctMultiblockController { +public class TileEntityLargeExtruder extends LargeSimpleRecipeMapMultiblockController { private static final MultiblockAbility[] ALLOWED_ABILITIES = {MultiblockAbility.IMPORT_ITEMS, MultiblockAbility.EXPORT_ITEMS, MultiblockAbility.IMPORT_FLUIDS, MultiblockAbility.EXPORT_FLUIDS, MultiblockAbility.INPUT_ENERGY}; diff --git a/src/main/resources/assets/gtadditions/lang/en_us.lang b/src/main/resources/assets/gtadditions/lang/en_us.lang index 5078419ca..04346de6a 100644 --- a/src/main/resources/assets/gtadditions/lang/en_us.lang +++ b/src/main/resources/assets/gtadditions/lang/en_us.lang @@ -2692,6 +2692,10 @@ gtadditions.multiblock.universal.tooltip.2=EUt Multiplier: §e%.1f§r gtadditions.multiblock.universal.tooltip.3=Duration Multiplier: §e%.1f§r gtadditions.multiblock.universal.tooltip.4=Max Parallel: §e%d§7 Per Overclocking Tier gtadditions.multiblock.universal.tooltip.5=Boost Chance: §e%d%%§r +gtadditions.multiblock.universal.distinct=Distinct Buses: +gtadditions.multiblock.universal.distinct.yes=§aYes +gtadditions.multiblock.universal.distinct.no=§cNo +gtadditions.multiblock.universal.distinct.info=If enabled, each bus will be treated as fully distinct from eachother for recipe lookup. Useful for example for Extruder Shapes, Laser Lenses, etc.. gtadditions.multiblock.fusion_reactor.heat=Heat: %d gtadditions.multiblock.fusion_reactor.tooltip.1=EU To Start: %s gtadditions.multiblock.central_monitor.height=Screen Height: %d From 6bb4a5f89575ab13201f4b882e806b8a13334339 Mon Sep 17 00:00:00 2001 From: DStrand1 Date: Thu, 15 Apr 2021 01:41:11 -0500 Subject: [PATCH 15/17] Save distinct to NBT to prevent clearing --- .../LargeSimpleRecipeMapMultiblockController.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/main/java/gregicadditions/machines/multi/simple/LargeSimpleRecipeMapMultiblockController.java b/src/main/java/gregicadditions/machines/multi/simple/LargeSimpleRecipeMapMultiblockController.java index 80bf307c3..42c2a2cad 100644 --- a/src/main/java/gregicadditions/machines/multi/simple/LargeSimpleRecipeMapMultiblockController.java +++ b/src/main/java/gregicadditions/machines/multi/simple/LargeSimpleRecipeMapMultiblockController.java @@ -21,6 +21,7 @@ import net.minecraft.block.state.IBlockState; import net.minecraft.client.resources.I18n; import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.ResourceLocation; import net.minecraft.util.text.ITextComponent; import net.minecraft.util.text.TextComponentTranslation; @@ -247,6 +248,19 @@ protected void handleDisplayClick(String componentData, Widget.ClickData clickDa isDistinct = !isDistinct; } + @Override + public NBTTagCompound writeToNBT(NBTTagCompound data) { + super.writeToNBT(data); + data.setBoolean("Distinct", isDistinct); + return data; + } + + @Override + public void readFromNBT(NBTTagCompound data) { + super.readFromNBT(data); + isDistinct = data.getBoolean("Distinct"); + } + public static class LargeSimpleMultiblockRecipeLogic extends GAMultiblockRecipeLogic { private final int EUtPercentage; From 949caf84841873e4e4a35dfc7568fecd00fdab73 Mon Sep 17 00:00:00 2001 From: DStrand1 Date: Mon, 19 Apr 2021 01:19:17 -0500 Subject: [PATCH 16/17] Fix crash with some GTCE multiblocks --- .../LargeSimpleRecipeMapMultiblockController.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/main/java/gregicadditions/machines/multi/simple/LargeSimpleRecipeMapMultiblockController.java b/src/main/java/gregicadditions/machines/multi/simple/LargeSimpleRecipeMapMultiblockController.java index 42c2a2cad..a133055c0 100644 --- a/src/main/java/gregicadditions/machines/multi/simple/LargeSimpleRecipeMapMultiblockController.java +++ b/src/main/java/gregicadditions/machines/multi/simple/LargeSimpleRecipeMapMultiblockController.java @@ -306,12 +306,11 @@ protected List getInputBuses() { @Override protected void trySearchNewRecipe() { - LargeSimpleRecipeMapMultiblockController controller = (LargeSimpleRecipeMapMultiblockController) metaTileEntity; - if (controller.isDistinct) { - trySearchNewRecipeDistinct(); - } else { - trySearchNewRecipeCombined(); - } + if (metaTileEntity instanceof LargeSimpleRecipeMapMultiblockController) { + LargeSimpleRecipeMapMultiblockController controller = (LargeSimpleRecipeMapMultiblockController) metaTileEntity; + if (controller.isDistinct) + trySearchNewRecipeDistinct(); + } else trySearchNewRecipeCombined(); } // Combined buses code ========================================================================================= From 2e699bdc2d72f296c02f9bd88cc77afe3b238891 Mon Sep 17 00:00:00 2001 From: DStrand1 Date: Mon, 19 Apr 2021 01:22:19 -0500 Subject: [PATCH 17/17] Small fix --- .../simple/LargeSimpleRecipeMapMultiblockController.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/java/gregicadditions/machines/multi/simple/LargeSimpleRecipeMapMultiblockController.java b/src/main/java/gregicadditions/machines/multi/simple/LargeSimpleRecipeMapMultiblockController.java index a133055c0..addd337a9 100644 --- a/src/main/java/gregicadditions/machines/multi/simple/LargeSimpleRecipeMapMultiblockController.java +++ b/src/main/java/gregicadditions/machines/multi/simple/LargeSimpleRecipeMapMultiblockController.java @@ -306,9 +306,7 @@ protected List getInputBuses() { @Override protected void trySearchNewRecipe() { - if (metaTileEntity instanceof LargeSimpleRecipeMapMultiblockController) { - LargeSimpleRecipeMapMultiblockController controller = (LargeSimpleRecipeMapMultiblockController) metaTileEntity; - if (controller.isDistinct) + if (metaTileEntity instanceof LargeSimpleRecipeMapMultiblockController && ((LargeSimpleRecipeMapMultiblockController) metaTileEntity).isDistinct) { trySearchNewRecipeDistinct(); } else trySearchNewRecipeCombined(); }