diff --git a/build.gradle b/build.gradle index f4c0964b..80c733a1 100644 --- a/build.gradle +++ b/build.gradle @@ -1,4 +1,4 @@ -//version: 1705076830 +//version: 1707604215 /* * DO NOT CHANGE THIS FILE! * Also, you may replace this file at any time if there is an update available. @@ -446,6 +446,10 @@ repositories { name 'GTNH Maven' url 'https://nexus.gtnewhorizons.com/repository/public/' } + maven { + name 'GTCEu Maven' + url 'https://maven.gtceu.com' + } } if (usesMixins.toBoolean() || forceEnableMixins.toBoolean()) { // need to add this here even if we did not above @@ -473,6 +477,22 @@ configurations { config.extendsFrom(shadowCompile) } } + + create("runtimeOnlyNonPublishable") { + description = "Runtime only dependencies that are not published alongside the jar" + canBeConsumed = false + canBeResolved = false + } + create("devOnlyNonPublishable") { + description = "Runtime and compiletime dependencies that are not published alongside the jar (compileOnly + runtimeOnlyNonPublishable)" + canBeConsumed = false + canBeResolved = false + } + + compileOnly.extendsFrom(devOnlyNonPublishable) + runtimeOnlyNonPublishable.extendsFrom(devOnlyNonPublishable) + runtimeClasspath.extendsFrom(runtimeOnlyNonPublishable) + testRuntimeClasspath.extendsFrom(runtimeOnlyNonPublishable) } String mixinProviderSpec = 'zone.rong:mixinbooter:8.9' @@ -493,7 +513,7 @@ dependencies { transitive = false } } else if (forceEnableMixins.toBoolean()) { - runtimeOnly(mixinProviderSpec) + runtimeOnlyNonPublishable(mixinProviderSpec) } if (enableJUnit.toBoolean()) { diff --git a/gradle.properties b/gradle.properties index b35962f0..96832ab1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -242,7 +242,7 @@ enable_rocketry = false # Whether to enable Ender Storage in runtime. Enables remappers to remap items in ender storage. # If this is set to false, the remappers will not apply there. -enable_ender_storage = false +enable_ender_storage = true # Whether to enable Thermal Expansion and its deps in runtime. These are used for the excitation coil textures. # If this is set to false, the top of the excitation coil will have a null texture. diff --git a/src/main/groovy-tests/recipeSearchTests.groovy b/src/main/groovy-tests/recipeSearchTests.groovy new file mode 100644 index 00000000..cf4a8c50 --- /dev/null +++ b/src/main/groovy-tests/recipeSearchTests.groovy @@ -0,0 +1,31 @@ +// Import Recipe Search Helpers, used for Chanced Item and Fluid Ingredients +import static com.nomiceu.nomilabs.groovy.GroovyHelpers.GTRecipeHelpers.* + +// Building Test Recipes +mods.gregtech.arc_furnace.recipeBuilder().inputs(metaitem('nomilabs:dustImpureOsmiridium8020')).outputs(item('minecraft:apple') * 64, item('minecraft:apple') * 64).EUt(50).duration(30).buildAndRegister() +mods.gregtech.arc_furnace.recipeBuilder().inputs(item('minecraft:stick')).outputs(item('minecraft:apple') * 64).EUt(50).duration(30).buildAndRegister() +mods.gregtech.arc_furnace.recipeBuilder().inputs(item('minecraft:yellow_flower')).outputs(item('minecraft:apple') * 64, item('minecraft:apple') * 64, item('minecraft:apple') * 64).chancedOutput(item('minecraft:apple') * 64, 50, 1).chancedFluidOutput(fluid('fluorine') * 2000, 50, 1).EUt(50).duration(30).buildAndRegister() +mods.gregtech.arc_furnace.recipeBuilder().inputs(metaitem('nomilabs:dustOsmiridium8020')).outputs(item('minecraft:apple') * 64, item('minecraft:apple') * 64, item('minecraft:apple') * 64).chancedOutput(item('minecraft:apple') * 64, 50, 1).chancedFluidOutput(fluid('fluorine') * 2000, 50, 1).EUt(50).duration(30).buildAndRegister() +mods.gregtech.arc_furnace.recipeBuilder().inputs(metaitem('nomilabs:dustPureOsmiridium8020')).outputs(item('minecraft:apple') * 64, item('minecraft:apple') * 64).EUt(50).duration(30).buildAndRegister() + +// Find/Remove By Input Extensions (Are Lists of: List itemInputs, List fluidInputs) +// mods.gregtech..removeByInput to remove, mods.gregtech..find to find (Returns null if no recipe found) +// Original: [long voltage, Inputs... (see above)] (Matches/Removes any recipe with that input, and that voltage or more, CHECKING AMOUNT) +// ALL FIND/REMOVE BY INPUT EXTENSIONS IGNORE THE AMOUNT! +// Three Extensions: +// [GTRecipeCategory category, Inputs... (see above)] (Matches/Removes any recipe with that input, and that category) +// [Inputs... (see above)] (Matches/Removes any recipe with that input) +// [Predicate predicate, Inputs... (see above)] (Matches/Removes any recipe with that input, and matching that predicate) +mods.gregtech.arc_furnace.removeByInput([item('minecraft:yellow_flower')], null) + +// Find/Remove By Output +// Outputs Specification: List itemOutputs, List fluidOutputs, List chancedItemOutputs, List chancedFluidOutputs +// Chanced Item/Fluid Outputs: chanced(item/fluid, chance, chanceBoost) +// mods.gregtech..removeByOutput to remove, mods.gregtech..findByOutput to find (Returns null if no recipes found) +// ALL FIND/REMOVE BY OUTPUT OPTIONS IGNORE THE AMOUNT! +// Four Options: +// [long voltage, Outputs... (see above)] (Matches/Removes any recipe with that output, and that voltage or more) +// [GTRecipeCategory category, Outputs... (see above)] (Matches/Removes any recipe with that output, and that category) +// [Outputs... (see above)] (Matches/Removes any recipe with that output) +// [Predicate predicate, Outputs... (see above)] (Matches/Removes any recipe with that output, and matching that predicate) +mods.gregtech.arc_furnace.removeByOutput(50, [item('minecraft:apple') * 64, item('minecraft:apple') * 64, item('minecraft:apple') * 64], null, [chanced(item('minecraft:apple') * 64, 50, 1)], [chanced(fluid('fluorine') * 2000, 50, 1)]) \ No newline at end of file diff --git a/src/main/groovy-tests/replaceCompositionTests.groovy b/src/main/groovy-tests/replaceCompositionTests.groovy new file mode 100644 index 00000000..4c06e938 --- /dev/null +++ b/src/main/groovy-tests/replaceCompositionTests.groovy @@ -0,0 +1,113 @@ +// Imports Helpers (Only Needed to use Groovy Helper Calls, +// useful when you only have an ItemStack or FluidStack of a Material +import static com.nomiceu.nomilabs.groovy.GroovyHelpers.ChangeCompositionHelpers.* + +// Imports Mixer Specification, needed to specify Mixer Specification if specifying how to change Mixer +import static com.nomiceu.nomilabs.groovy.CompositionBuilder.MixerSpecification + +// Replace Composition (changes specified) +// Decomp recipes are either centrifuge or electrolyzer recipes, and require the material not to have the DISABLE_DECOMPOSITION flag, and either have a dust or fluid. +// You cannot enable them, if the material already has them disabled. +// Chemical Formula Changes are not recursive. +// However, changes will apply if you first change the inner materials before the outer. +// ABS Recipe requires the material to have a molten fluid, and Alloy Blast and Blast Properties, and not have the Disable ABS Recipe Flag. +// Mixer Recipe requires the material to have an existing mixer recipe, and have a Dust Property. + +// You can get all material components of a material by holding an ItemStack of that material, then doing /gs hand! + +// Having the target material be a Material Stack changes nothing. +// Having the component list be an item stack will grab the material, then use the stack's amount as the material amount. +// Having the component list be an fluid stack will grab the material, then use the fluid amount / 1000. +// You cannot have fractional components. +// You can have materials with only liquid forms (fluorine, mercury, etc.), as both inputs and outputs. + +// Don't have recursive components! This will throw a Stack Overflow Error. + +// How to specify a Material Stack +println("Material Stack of 1 Pyrite | " + materialstack('pyrite')) // Material Stack of 1 Pyrite +println("Material Stack of 6 Pyrite | " + materialstack('pyrite') * 6) // Material Stack of 6 Pyrite + +println("Material Stack of 1 Pyrite | " + material('pyrite') * 1) // Material Stack of 1 Pyrite +println("Material Stack of 6 Pyrite | " + material('pyrite') * 6) // Material Stack of 6 Pyrite + +// How not to specify a Material Stack +println("Material: Pyrite | " + material('pyrite')) // MATERIAL, not Material Stack! This will throw an error! + +// Remove Decomposition by Material + +// Call Change Composition On a FluidStack or ItemStack or MaterialStack or Material, with Groovy Helper Call +// Change Composition Selectively +changeComposition(material('osmiridium')) + // Remove Components + .removeComponents() + // Remove Decomposition (No Components, so is removed) + .changeDecompositionRecipes() + // Remove Mixer (No Components, so is removed) + .changeMixer() + // Save and Apply Change **IMPORTANT** + .change() + +changeComposition(material('osmiridium')) + // Set Components, using a mixture of MaterialStacks, ItemStacks and FluidStacks + // This is 4 fluorine, not 4000, because fluids are divided by 1000 + .setComponents([fluid('fluorine') * 4000, materialstack('rhodium'), materialstack('nomilabs:naquadah_oxide') * 4, materialstack('magnesium') * 3, metaitem('dustCarbon') * 2]) + // Change ABS Recipe with new specified components + .changeABS() + // Change Chemical Formula (Also Saves the Change, so GS Hand shows new components instead of original) + .changeChemicalFormula() + // Save and Apply Change **IMPORTANT** + .change() + +// Call Change Composition on a Material Object +material('ruthenium_trinium_americium_neutronate') + // Start the builder + .changeComposition() + // Set Components, using a mixture of MaterialStacks, ItemStacks and FluidStacks + .setComponents([materialstack('pyrite') * 4, materialstack('rhodium') * 2, materialstack('nomilabs:naquadah_oxide'), materialstack('magnesium'), materialstack('carbon')]) + // Change ABS Recipes + .changeABS() + // Change Chemical Formula (Also Saves the Change, so GS Hand shows new components instead of original) + .changeChemicalFormula() + // Change Decomp Recipes + .changeDecompositionRecipes() + // Change Mixer, using all mixer defaults (see below for description) + .changeMixer() + // Save and Apply Change **IMPORTANT** + .change() + +material('rhodium_plated_palladium') + // Start the builder + .changeComposition() + // Set Components, using a mixture of MaterialStacks, ItemStacks and FluidStacks + .setComponents([materialstack('pyrite') * 4, materialstack('rhodium') * 2, materialstack('nomilabs:naquadah_oxide'), materialstack('magnesium'), materialstack('carbon')]) + // Change Chemical Formula (Also Saves the Change, so GS Hand shows new components instead of original) + .changeChemicalFormula() + // Change Decomp Recipes + .changeDecompositionRecipes() + // Override All, Some or None Mixer Specifications + // If none, can provide no params + // Call new MixerSpecification() to start the builder + // Builder Params and Defaults: + // EUt: How Much EU per Tick the Mixer Recipe uses. Default to EUt in original recipe. + // Output Amount: How much the Mixer Recipe Outputs. Defaults to Amount of Components (E.g. If 5 Pyrite and 2 Iron, outputs 7) + // Circuit: The circuit of the recipe, use 0 for none. Defaults to circuit in original mixer recipe. + .changeMixer(new MixerSpecification().overrideEUt(200).overrideOutputAmount(1).overrideDuration(250).overrideCircuit(0)) + // Change ABS Recipes + .changeABS() + .change() + +// Example of 'Recursive' Chemical Formula Change +// Redstone has Ruby as a Component +material('ruby') + .changeComposition() + // Originally 1 Chrome, 2 Aluminium, 3 Oxygen + .setComponents([materialstack('chrome') * 2, materialstack('aluminium') * 2, materialstack('oxygen') * 6]) + .changeChemicalFormula() // Must call this, so the chemical formula is reloaded, and so that changes are saved + .change() + +material('redstone') + .changeComposition() + // Marks this as a 'reload', meaning that components are the original ones from GT or the addon + .reloadComponents() + .changeChemicalFormula() // Must call this, so the chemical formula is reloaded + .change() \ No newline at end of file diff --git a/src/main/groovy-tests/replaceDecompositionTests.groovy b/src/main/groovy-tests/replaceDecompositionTests.groovy deleted file mode 100644 index 179f07c8..00000000 --- a/src/main/groovy-tests/replaceDecompositionTests.groovy +++ /dev/null @@ -1,52 +0,0 @@ -// Imports all Static Functions from the decomposition section of the groovy helpers - -import static com.nomiceu.nomilabs.groovy.GroovyHelpers.ReplaceDecompositionHelpers.* - -// Replace Decomposition (changes chemical formula and decomp recipe) -// This only changes DECOMPOSITION recipes, not the recipes that make it, such as ABS, Mixer, Blast Furnace, etc. -// Decomp recipes are either centrifuge or electrolyzer recipes. -// You cannot enable them, if the material already has them disabled. -// Chemical Formula Changes are not recursive. -// However, changes will apply if you first change the inner materials before the outer. - -// Having the target material be a Material Stack changes nothing. -// Having the component list be an item stack will grab the material, then use the stack's amount as the material amount. -// Having the component list be an fluid stack will grab the material, then use the fluid amount / 1000. -// You cannot have fractional components. -// You can have materials with only liquid forms (fluorine, mercury, etc.), as both inputs and outputs. - -// Don't have recursive components! This will throw a Stack Overflow Error. - -// How to specify a Material Stack -println("Material Stack of 1 Pyrite | " + materialstack('pyrite')) // Material Stack of 1 Pyrite -println("Material Stack of 6 Pyrite | " + materialstack('pyrite') * 6) // Material Stack of 6 Pyrite - -println("Material Stack of 1 Pyrite | " + material('pyrite') * 1) // Material Stack of 1 Pyrite -println("Material Stack of 6 Pyrite | " + material('pyrite') * 6) // Material Stack of 6 Pyrite - -// How not to specify a Material Stack -println("Material: Pyrite | " + material('pyrite')) // MATERIAL, not Material Stack! This will throw an error! - -// Replace/Add Decomposition by Material -replaceDecomposition(material('pyrite'), [materialstack('nomilabs:pulsating_iron') * 5, fluid('fluorine') * 2000, metaitem('ingotNaquadahAlloy') * 10]) - -// Replace/Add Decomposition by Material Stack -replaceDecomposition(materialstack('redstone'), [material('nomilabs:lead_metasilicate') * 2, materialstack('pyrite') * 6, materialstack('ruby') * 2, materialstack('mercury') * 4]) - -// Replace/Add Decomposition by Item Stack -replaceDecomposition(metaitem('ingotEnrichedNaquadahTriniumEuropiumDuranide'), [metaitem('ingotTrinaquadalloy')]) - -// Replace/Add Decomposition by Fluid Stack -replaceDecomposition(fluid('hexafluorosilicic_acid'), [metaitem('item_collector.hv'), item('minecraft:bucket')]) - -// Remove Decomposition by Material -removeDecomposition(material('osmiridium')) - -// Remove Decomposition by Material Stack -removeDecomposition(materialstack('red_alloy') * 10) - -// Remove Decomposition by Item Stack -removeDecomposition(metaitem('ingotTrinaquadalloy')) - -// Remove Decomposition by Fluid Stack -removeDecomposition(fluid('dioxygen_difluoride')) diff --git a/src/main/java/com/nomiceu/nomilabs/config/LabsConfig.java b/src/main/java/com/nomiceu/nomilabs/config/LabsConfig.java index dc5f54d6..dfc200a6 100644 --- a/src/main/java/com/nomiceu/nomilabs/config/LabsConfig.java +++ b/src/main/java/com/nomiceu/nomilabs/config/LabsConfig.java @@ -11,6 +11,11 @@ public class LabsConfig { @Config.Name("content") public static Content content = new Content(); + @Config.Comment({"GroovyScript Extensions and Script Helper Settings"}) + @Config.LangKey("config.nomilabs.groovy") + @Config.Name("groovyscript settings") + public static GroovyScriptSettings groovyScriptSettings = new GroovyScriptSettings(); + @Config.Comment("Mod Integration Settings") @Config.LangKey("config.nomilabs.mod_integration") @Config.Name("mod integration") @@ -122,6 +127,32 @@ public static class GTCustomContent { } } + public static class GroovyScriptSettings { + @Config.Comment({"Whether to enable GroovyScript Hand Additions.", + "[default: true]"}) + @Config.LangKey("config.nomilabs.groovy.hand") + public boolean enableGroovyHandAdditions = true; + + @Config.Comment({"Mode to Use for GT Recipe Output Searching.", + "'LINEAR_SEARCH' browses each recipe sequentially, 'FAST_TREE' navigates a tree structure and stops at the first match, while 'TREE' explores the entire tree structure before concluding.", + "Because of the extra generated tree, if no removals occur, TREE and FAST_TREE have a slightly longer launch time. They also have slightly higher memory usage (around 20-50MB in limited testing)", + "If a small amount of removals occur, game launching is around the same for all three, but TREE or FAST_TREE has the lowest script reload time.", + "With a moderate-high amount of removals, game launching and script reloading is much faster with FAST_TREE or TREE, and FAST_TREE does consistently out perform TREE in time.", + "TREE is a safer option if all recipes need to be grabbed, but FAST_TREE has not failed to grab any recipes in the limited testing.", + "If some recipes are left over, try using TREE mode.", + "Recipe Output Searching is used when replacing ABS recipes and Mixer Recipes in Composition Replacements, and in Recipe Output Searching or Removing.", + "[default: FAST_TREE]"}) + @Config.LangKey("config.nomilabs.groovy.recipe_search_mode") + @Config.RequiresMcRestart + public GTRecipeSearchMode gtRecipeSearchMode = GTRecipeSearchMode.FAST_TREE; + + public enum GTRecipeSearchMode { + LINEAR_SEARCH, + FAST_TREE, + TREE + } + } + public static class ModIntegration { @Config.Comment({"Whether to enable NuclearCraft Integration, which fixes its crash with GTCEu.", "[default: true]"}) @@ -165,11 +196,6 @@ public static class ModIntegration { @Config.RequiresMcRestart public boolean enableAdvancedRocketryIntegration = true; - @Config.Comment({"Whether to enable GroovyScript Hand Additions.", - "[default: true]"}) - @Config.LangKey("config.nomilabs.mod_integration.groovy_hand") - public boolean enableGroovyHandAdditions = true; - public static class DraconicEvolutionIntegration { @Config.Comment({"Whether to enable Draconic Evolution Integration, which adds many features, such as:", "Allowing GregTech Draconium and Awakened Draconium in the reactor and energy core.", diff --git a/src/main/java/com/nomiceu/nomilabs/gregtech/AccessibleMaterial.java b/src/main/java/com/nomiceu/nomilabs/gregtech/AccessibleMaterial.java deleted file mode 100644 index 5bb3d8c8..00000000 --- a/src/main/java/com/nomiceu/nomilabs/gregtech/AccessibleMaterial.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.nomiceu.nomilabs.gregtech; - -import gregtech.api.unification.stack.MaterialStack; - -import java.util.List; - -public interface AccessibleMaterial { - void setComponents(List components); -} diff --git a/src/main/java/com/nomiceu/nomilabs/gregtech/mixinhelper/AccessibleMaterial.java b/src/main/java/com/nomiceu/nomilabs/gregtech/mixinhelper/AccessibleMaterial.java new file mode 100644 index 00000000..5c088d42 --- /dev/null +++ b/src/main/java/com/nomiceu/nomilabs/gregtech/mixinhelper/AccessibleMaterial.java @@ -0,0 +1,22 @@ +package com.nomiceu.nomilabs.gregtech.mixinhelper; + +import com.google.common.collect.ImmutableList; +import gregtech.api.recipes.Recipe; +import gregtech.api.unification.stack.MaterialStack; + +import java.util.List; +import java.util.Map; + +public interface AccessibleMaterial { + void setComponents(ImmutableList components, boolean changeFormula); + + void setComponents(ImmutableList components); + + void recalculateDecompositionType(); + + void setOriginalRecipes(CompositionRecipeType type, List originals); + + Map> getOriginalRecipes(); + + ImmutableList getOriginalComponents(); +} diff --git a/src/main/java/com/nomiceu/nomilabs/gregtech/mixinhelper/AccessibleMaterialFlags.java b/src/main/java/com/nomiceu/nomilabs/gregtech/mixinhelper/AccessibleMaterialFlags.java new file mode 100644 index 00000000..f43d8dfc --- /dev/null +++ b/src/main/java/com/nomiceu/nomilabs/gregtech/mixinhelper/AccessibleMaterialFlags.java @@ -0,0 +1,7 @@ +package com.nomiceu.nomilabs.gregtech.mixinhelper; + +import gregtech.api.unification.material.info.MaterialFlag; + +public interface AccessibleMaterialFlags { + void removeFlags(MaterialFlag... toRemove); +} diff --git a/src/main/java/com/nomiceu/nomilabs/gregtech/mixinhelper/AccessibleRecipeMap.java b/src/main/java/com/nomiceu/nomilabs/gregtech/mixinhelper/AccessibleRecipeMap.java new file mode 100644 index 00000000..a920735a --- /dev/null +++ b/src/main/java/com/nomiceu/nomilabs/gregtech/mixinhelper/AccessibleRecipeMap.java @@ -0,0 +1,24 @@ +package com.nomiceu.nomilabs.gregtech.mixinhelper; + +import gregtech.api.recipes.Recipe; +import gregtech.api.recipes.chance.output.impl.ChancedFluidOutput; +import gregtech.api.recipes.chance.output.impl.ChancedItemOutput; +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.FluidStack; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.List; +import java.util.function.Predicate; + +public interface AccessibleRecipeMap { + List findByOutput(@NotNull Collection items, @NotNull Collection fluids, + @NotNull Collection chancedItems, @NotNull Collection chancedFluids, + @NotNull Predicate canHandle); + + List findRecipeByOutput(long voltage, List inputs, List fluidInputs, + List chancedItems, List chancedFluids); + + List findRecipeByOutput(long voltage, List inputs, List fluidInputs, + List chancedItems, List chancedFluids, boolean exactVoltage); +} diff --git a/src/main/java/com/nomiceu/nomilabs/gregtech/mixinhelper/CompositionRecipeType.java b/src/main/java/com/nomiceu/nomilabs/gregtech/mixinhelper/CompositionRecipeType.java new file mode 100644 index 00000000..5d354a6c --- /dev/null +++ b/src/main/java/com/nomiceu/nomilabs/gregtech/mixinhelper/CompositionRecipeType.java @@ -0,0 +1,45 @@ +package com.nomiceu.nomilabs.gregtech.mixinhelper; + +import com.nomiceu.nomilabs.groovy.ChangeComposition; +import gregicality.multiblocks.api.recipes.GCYMRecipeMaps; +import gregtech.api.recipes.RecipeMap; +import gregtech.api.recipes.RecipeMaps; +import gregtech.api.unification.material.Material; + +public enum CompositionRecipeType { + ELECTROLYZER(RecipeMaps.ELECTROLYZER_RECIPES) { + @Override + public void remove(Material input) { + // Have to check both, the decomp type could have changed! + ChangeComposition.removeDecompRecipe(this, input); + ChangeComposition.removeDecompRecipe(CENTRIFUGE, input); + } + }, + CENTRIFUGE(RecipeMaps.CENTRIFUGE_RECIPES) { + @Override + public void remove(Material input) { + // Have to check both, the decomp type could have changed! + ChangeComposition.removeDecompRecipe(ELECTROLYZER, input); + ChangeComposition.removeDecompRecipe(this, input); + } + }, + ALLOY_BLAST(GCYMRecipeMaps.ALLOY_BLAST_RECIPES) { + @Override + public void remove(Material input) { + ChangeComposition.removeABSRecipe(input); + } + }, + MIXER(RecipeMaps.MIXER_RECIPES) { + @Override + public void remove(Material input) { + ChangeComposition.removeMixerRecipe(input); + } + }; + + public final RecipeMap map; + CompositionRecipeType(RecipeMap map) { + this.map = map; + } + + public abstract void remove(Material input); +} diff --git a/src/main/java/com/nomiceu/nomilabs/gregtech/mixinhelper/EitherOrBoth.java b/src/main/java/com/nomiceu/nomilabs/gregtech/mixinhelper/EitherOrBoth.java new file mode 100644 index 00000000..18d657df --- /dev/null +++ b/src/main/java/com/nomiceu/nomilabs/gregtech/mixinhelper/EitherOrBoth.java @@ -0,0 +1,93 @@ +package com.nomiceu.nomilabs.gregtech.mixinhelper; + +import gregtech.api.recipes.map.Either; + +import javax.annotation.Nullable; +import java.util.Optional; +import java.util.function.Consumer; +import java.util.function.Function; + +@SuppressWarnings({"UnusedReturnValue", "unused"}) +public class EitherOrBoth { + @Nullable + private L valueL; + + @Nullable + private R valueR; + + public Optional getLeft() { + return Optional.ofNullable(valueL); + } + + public Optional getRight() { + return Optional.ofNullable(valueR); + } + + public EitherOrBoth setLeft(L value) { + if (hasLeft()) throw new IllegalArgumentException("Cannot set Either Or Both when value of that side already exists!"); + valueL = value; + return this; + } + + public EitherOrBoth setRight(R value) { + if (hasRight()) throw new IllegalArgumentException("Cannot set Either Or Both when value of that side already exists!"); + valueR = value; + return this; + } + + public EitherOrBoth setLeftIfNoValue(L value) { + if (hasLeft()) return this; + valueL = value; + return this; + } + + public EitherOrBoth setRightIfNoValue(R value) { + if (hasRight()) return this; + valueR = value; + return this; + } + + public EitherOrBoth removeLeft() { + if (!hasLeft()) return this; + valueL = null; + return this; + } + + public EitherOrBoth removeRight() { + if (!hasRight()) return this; + valueR = null; + return this; + } + + public boolean hasLeft() { + return valueL != null; + } + + public boolean hasRight() { + return valueR != null; + } + + public static EitherOrBoth left(L value) { + return new EitherOrBoth().setLeft(value); + } + + public static EitherOrBoth right(R value) { + return new EitherOrBoth().setRight(value); + } + + public static EitherOrBoth both(L valueL, R valueR) { + return new EitherOrBoth().setLeft(valueL).setRight(valueR); + } + + public EitherOrBoth map(Function var1, Function var2) { + return both(hasLeft() ? var1.apply(valueL) : null, hasRight() ? var2.apply(valueR) : null); + } + + public EitherOrBoth mapLeft(Function l) { + return map(l, (r) -> valueR); + } + + public EitherOrBoth mapRight(Function r) { + return map((l) -> valueL, r); + } +} diff --git a/src/main/java/com/nomiceu/nomilabs/gregtech/mixinhelper/MapChancedFluidIngredient.java b/src/main/java/com/nomiceu/nomilabs/gregtech/mixinhelper/MapChancedFluidIngredient.java new file mode 100644 index 00000000..2602e0e1 --- /dev/null +++ b/src/main/java/com/nomiceu/nomilabs/gregtech/mixinhelper/MapChancedFluidIngredient.java @@ -0,0 +1,51 @@ +package com.nomiceu.nomilabs.gregtech.mixinhelper; + +import gregtech.api.recipes.chance.output.impl.ChancedFluidOutput; +import gregtech.api.recipes.chance.output.impl.ChancedItemOutput; +import gregtech.api.recipes.map.MapFluidIngredient; +import gregtech.api.recipes.map.MapItemStackIngredient; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidStack; + +public class MapChancedFluidIngredient extends MapFluidIngredient { + protected int chance; + protected int chanceBoost; + + public MapChancedFluidIngredient(ChancedFluidOutput chancedFluidOutput) { + super(chancedFluidOutput.getIngredient()); + chance = chancedFluidOutput.getChance(); + chanceBoost = chancedFluidOutput.getChanceBoost(); + } + + @Override + protected int hash() { + int hash = super.hash(); + hash += 31 * chance; + hash += 31 * chanceBoost; + return hash; + } + + @Override + public boolean equals(Object o) { + if (!super.equals(o)) return false; + MapChancedFluidIngredient f = (MapChancedFluidIngredient) o; + return chance == f.chance && chanceBoost == f.chanceBoost; + } + + @Override + public String toString() { + return "MapChancedFluidIngredient{" + + "chance=" + chance + + ", chanceBoost=" + chanceBoost + + ", fluid=" + fluid.getName() + + ", tag=" + tag + + '}'; + } + + @Override + public boolean isSpecialIngredient() { + return true; + } +} diff --git a/src/main/java/com/nomiceu/nomilabs/gregtech/mixinhelper/MapChancedItemStackIngredient.java b/src/main/java/com/nomiceu/nomilabs/gregtech/mixinhelper/MapChancedItemStackIngredient.java new file mode 100644 index 00000000..b58d0b76 --- /dev/null +++ b/src/main/java/com/nomiceu/nomilabs/gregtech/mixinhelper/MapChancedItemStackIngredient.java @@ -0,0 +1,45 @@ +package com.nomiceu.nomilabs.gregtech.mixinhelper; + +import gregtech.api.recipes.chance.output.impl.ChancedItemOutput; + +public class MapChancedItemStackIngredient extends MapOutputItemStackIngredient { + protected int chance; + protected int chanceBoost; + + public MapChancedItemStackIngredient(ChancedItemOutput chancedItemOutput) { + super(chancedItemOutput.getIngredient(), chancedItemOutput.getIngredient().getMetadata(), chancedItemOutput.getIngredient().getTagCompound()); + chance = chancedItemOutput.getChance(); + chanceBoost = chancedItemOutput.getChanceBoost(); + } + + @Override + protected int hash() { + int hash = super.hash(); + hash += 31 * chance; + hash += 31 * chanceBoost; + return hash; + } + + @Override + public boolean equals(Object o) { + if (!super.equals(o)) return false; + MapChancedItemStackIngredient i = (MapChancedItemStackIngredient) o; + return chance == i.chance && chanceBoost == i.chanceBoost; + } + + @Override + public String toString() { + return "MapChancedItemStackIngredient{" + + "chance=" + chance + + ", chanceBoost=" + chanceBoost + + ", item=" + stack.getItem().getRegistryName() + + ", meta=" + meta + + ", tag=" + tag + + '}'; + } + + @Override + public boolean isSpecialIngredient() { + return true; + } +} diff --git a/src/main/java/com/nomiceu/nomilabs/gregtech/mixinhelper/MapOutputItemStackIngredient.java b/src/main/java/com/nomiceu/nomilabs/gregtech/mixinhelper/MapOutputItemStackIngredient.java new file mode 100644 index 00000000..aa3f3352 --- /dev/null +++ b/src/main/java/com/nomiceu/nomilabs/gregtech/mixinhelper/MapOutputItemStackIngredient.java @@ -0,0 +1,30 @@ +package com.nomiceu.nomilabs.gregtech.mixinhelper; + +import gregtech.api.recipes.map.MapItemStackIngredient; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; + +import java.util.Objects; + +/** + * Can't use MapItemStackIngredient as equals() always returns false, if gt recipe input is null. + */ +public class MapOutputItemStackIngredient extends MapItemStackIngredient { + public MapOutputItemStackIngredient(ItemStack stack, int meta, NBTTagCompound tag) { + super(stack, meta, tag); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null) return false; + if (getClass() != o.getClass()) return false; + MapOutputItemStackIngredient i = (MapOutputItemStackIngredient) o; + return stack.getItem() == i.stack.getItem() && meta == i.meta && Objects.equals(tag, i.tag); + } + + @Override + public String toString() { + return "MapOutputItemStackIngredient{item=" + this.stack.getItem().getRegistryName() + "} {meta=" + this.meta + "} {tag=" + this.tag + "}"; + } +} diff --git a/src/main/java/com/nomiceu/nomilabs/gregtech/mixinhelper/OutputBranch.java b/src/main/java/com/nomiceu/nomilabs/gregtech/mixinhelper/OutputBranch.java new file mode 100644 index 00000000..7c7032fc --- /dev/null +++ b/src/main/java/com/nomiceu/nomilabs/gregtech/mixinhelper/OutputBranch.java @@ -0,0 +1,42 @@ +package com.nomiceu.nomilabs.gregtech.mixinhelper; + +import gregtech.api.recipes.Recipe; +import gregtech.api.recipes.map.AbstractMapIngredient; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; +import java.util.Set; + +public class OutputBranch { + // Keys on this have *(should)* unique hashcodes, but perhaps not to specialNodes. + private Map, OutputBranch>> nodes; + + // Keys on this have *(should)* unique hashcodes, but perhaps not to nodes. + private Map, OutputBranch>> specialNodes; + + public boolean isEmpty() { + return (nodes == null || nodes.isEmpty()) && (specialNodes == null || specialNodes.isEmpty()); + } + + @NotNull + public Map, OutputBranch>> getNodes(AbstractMapIngredient ingredient) { + if (ingredient.isSpecialIngredient()) { + if (specialNodes == null) + specialNodes = new Object2ObjectOpenHashMap<>(1); + return specialNodes; + } + if (nodes == null) + nodes = new Object2ObjectOpenHashMap<>(1); + return nodes; + } + + @Nullable + public Map, OutputBranch>> getNodesIfExists(AbstractMapIngredient ingredient) { + if (ingredient.isSpecialIngredient()) + return specialNodes; + + return nodes; + } +} diff --git a/src/main/java/com/nomiceu/nomilabs/gregtech/mixinhelper/RecipeMapLogic.java b/src/main/java/com/nomiceu/nomilabs/gregtech/mixinhelper/RecipeMapLogic.java new file mode 100644 index 00000000..f1d731f3 --- /dev/null +++ b/src/main/java/com/nomiceu/nomilabs/gregtech/mixinhelper/RecipeMapLogic.java @@ -0,0 +1,238 @@ +package com.nomiceu.nomilabs.gregtech.mixinhelper; + +import com.nomiceu.nomilabs.config.LabsConfig; +import gregtech.api.recipes.Recipe; +import gregtech.api.recipes.RecipeMap; +import gregtech.api.recipes.chance.output.impl.ChancedFluidOutput; +import gregtech.api.recipes.chance.output.impl.ChancedItemOutput; +import gregtech.api.recipes.map.AbstractMapIngredient; +import gregtech.api.recipes.map.MapFluidIngredient; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.FluidStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.ref.WeakReference; +import java.util.*; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +public class RecipeMapLogic { + private static final WeakHashMap> outputRoot = new WeakHashMap<>(); + + private static final WeakHashMap> fluidOutputRoot = new WeakHashMap<>(); + + private static final WeakHashMap> chancedOutputRoot = new WeakHashMap<>(); + + private static final WeakHashMap> chancedFluidOutputRoot = new WeakHashMap<>(); + + public static void add(@NotNull Recipe recipe, @NotNull OutputBranch branch) { + if (LabsConfig.groovyScriptSettings.gtRecipeSearchMode == LabsConfig.GroovyScriptSettings.GTRecipeSearchMode.LINEAR_SEARCH) + return; + var list = getOutputFromRecipe(recipe); + recurseOutputTreeAdd(recipe, list, branch, 0, 0); + } + + private static void recurseOutputTreeAdd(@NotNull Recipe recipe, @NotNull List outputs, @NotNull OutputBranch branchMap, int index, int count) { + if (count >= outputs.size()) return; + if (index >= outputs.size()) + throw new RuntimeException("Index out of bounds for recurseOutputTreeAdd, should not happen"); + + var current = outputs.get(index); + var targetMap = branchMap.getNodes(current); + + EitherOrBoth, OutputBranch> r = targetMap.compute(current, (k, v) -> { + if (count == outputs.size() - 1) { + if (v == null) v = new EitherOrBoth<>(); + v.setLeftIfNoValue(new ObjectOpenHashSet<>(1)); + //noinspection OptionalGetWithoutIsPresent + v.getLeft().get().add(recipe); + return v; + } + return v == null ? EitherOrBoth.right(new OutputBranch()) : v.setRightIfNoValue(new OutputBranch()); + }); + if (r.getLeft().isPresent() && r.getLeft().get().contains(recipe)) return; + + // We do not need to check if right exists, because of the setRightIfNoValue call + //noinspection OptionalGetWithoutIsPresent + recurseOutputTreeAdd(recipe, outputs, r.getRight().get(), (index + 1) % outputs.size(), count + 1); + } + + public static void remove(@NotNull Recipe recipe, @NotNull OutputBranch branch) { + if (LabsConfig.groovyScriptSettings.gtRecipeSearchMode == LabsConfig.GroovyScriptSettings.GTRecipeSearchMode.LINEAR_SEARCH) + return; + var list = getOutputFromRecipe(recipe); + recurseOutputTreeRemove(recipe, list, branch); + } + + private static boolean recurseOutputTreeRemove(@NotNull Recipe recipe, @NotNull List outputs, @NotNull OutputBranch branchMap) { + for (var output : outputs) { + var targetMap = branchMap.getNodesIfExists(output); + if (targetMap == null) continue; + + EitherOrBoth, OutputBranch> result = targetMap.get(output); + + FoundType found = FoundType.NONE; + if (result == null) continue; + + if (result.getLeft().isPresent() && result.getLeft().get().contains(recipe)) { + found = FoundType.RECIPE; + } + if (result.getRight().isPresent()) { + if (recurseOutputTreeRemove(recipe, outputs.subList(1, outputs.size()), result.getRight().get())) found = FoundType.BRANCH; + } + + if (found != FoundType.NONE) { + if (outputs.size() == 1 && found == FoundType.RECIPE) { + // a recipe was found, and this is the only ingredient, so remove it directly + if (result.getLeft().isPresent() && result.getLeft().get().size() <= 1) result.removeLeft(); + else result.getLeft().get().remove(recipe); + } + if (result.getRight().isPresent() && found == FoundType.BRANCH) { + OutputBranch branch = result.getRight().get(); + if (branch.isEmpty()) result.removeRight(); + } + //noinspection SimplifyOptionalCallChains + if (!result.getLeft().isPresent() && !result.getRight().isPresent()) targetMap.remove(output); + return true; + } + } + return false; + } + + private static List getOutputFromRecipe(Recipe r) { + List list = new ObjectArrayList<>( + r.getOutputs().size() + r.getChancedOutputs().getChancedEntries().size() + + r.getFluidOutputs().size() + r.getChancedFluidOutputs().getChancedEntries().size()); + for (var output : r.getOutputs()) { + list.add(getCachedIngredient(new MapOutputItemStackIngredient(output, output.getMetadata(), output.getTagCompound()), outputRoot)); + } + for (var output : r.getChancedOutputs().getChancedEntries()) { + list.add(getCachedIngredient(new MapChancedItemStackIngredient(output), chancedOutputRoot)); + } + for (var output : r.getFluidOutputs()) { + list.add(getCachedIngredient(new MapFluidIngredient(output), fluidOutputRoot)); + } + for (var output : r.getChancedFluidOutputs().getChancedEntries()) { + list.add(getCachedIngredient(new MapChancedFluidIngredient(output), chancedFluidOutputRoot)); + } + list.sort(Comparator.comparingInt(AbstractMapIngredient::hashCode)); // Help improve speed of lookups + return list; + } + + private static AbstractMapIngredient getCachedIngredient(AbstractMapIngredient ingredient, WeakHashMap> cache) { + WeakReference cached = cache.get(ingredient); + if (cached != null && cached.get() != null) + return cached.get(); + + cache.put(ingredient, new WeakReference<>(ingredient)); + return ingredient; + } + + private enum FoundType { + NONE, + RECIPE, + BRANCH + } + + @Nullable + public static List find(@NotNull OutputBranch branch, @NotNull RecipeMap map, + @NotNull Collection items, @NotNull Collection fluids, + @NotNull Collection chancedItems, @NotNull Collection chancedFluids, + @NotNull Predicate predicate) { + var list = prepareOutputFind(items, fluids, chancedItems, chancedFluids); + if (list == null) return null; + if (LabsConfig.groovyScriptSettings.gtRecipeSearchMode == LabsConfig.GroovyScriptSettings.GTRecipeSearchMode.LINEAR_SEARCH) + return linearFind(map, list, predicate); + return recurseOutputTreeFindRecipe(list, branch, predicate); + } + + @Nullable + private static List recurseOutputTreeFindRecipe(@NotNull List outputs, @NotNull OutputBranch branchRoot, @NotNull Predicate canHandle) { + List result = new ObjectArrayList<>(); + for (int i = 0; i < outputs.size(); ++i) { + recurseOutputTreeFindRecipe(outputs, branchRoot, canHandle, i, 0, 1L << i, result); + } + + return result.isEmpty() ? null : result; + } + + private static boolean recurseOutputTreeFindRecipe(@NotNull List outputs, @NotNull OutputBranch branchRoot, @NotNull Predicate canHandle, int index, int count, long skip, List foundRecipes) { + if (count == outputs.size()) return false; + var current = outputs.get(index); + var targetMap = branchRoot.getNodesIfExists(current); + if (targetMap == null) return false; + + EitherOrBoth, OutputBranch> result = targetMap.get(current); + if (result != null) { + if (result.getLeft().isPresent() && count == outputs.size() - 1) { + //noinspection SimplifyStreamApiCallChains + var found = result.getLeft().get().stream().filter(canHandle).collect(Collectors.toList()); + if (!found.isEmpty()) { + foundRecipes.addAll(found); + if (LabsConfig.groovyScriptSettings.gtRecipeSearchMode == + LabsConfig.GroovyScriptSettings.GTRecipeSearchMode.FAST_TREE) + return true; + } + } + if (result.getRight().isPresent()) { + return diveIngredientTreeFindRecipe(outputs, result.getRight().get(), canHandle, index, count, skip, foundRecipes); + } + } + return false; + } + + private static boolean diveIngredientTreeFindRecipe(@NotNull List outputs, @NotNull OutputBranch map, @NotNull Predicate canHandle, int currentIndex, int count, long skip, List foundRecipes) { + for (int i = (currentIndex + 1) % outputs.size(); i != currentIndex; i = (i + 1) % outputs.size()) { + if ((skip & 1L << i) == 0L) { + var found = recurseOutputTreeFindRecipe(outputs, map, canHandle, i, count + 1, skip | 1L << i, foundRecipes); + if (found) return true; + } + } + return false; + } + + @Nullable + private static List prepareOutputFind(@NotNull Collection items, @NotNull Collection fluids, @NotNull Collection chancedItems, @NotNull Collection chancedFluids) { + if (items.size() != Integer.MAX_VALUE && fluids.size() != Integer.MAX_VALUE) { + if (items.isEmpty() && fluids.isEmpty() && chancedItems.isEmpty() && chancedFluids.isEmpty()) { + return null; + } else { + List list = new ObjectArrayList<>(items.size() + fluids.size()); + for (var output : items) { + list.add(getCachedIngredient(new MapOutputItemStackIngredient(output, output.getMetadata(), output.getTagCompound()), outputRoot)); + } + for (var output : chancedItems) { + list.add(getCachedIngredient(new MapChancedItemStackIngredient(output), chancedOutputRoot)); + } + for (var output : fluids) { + list.add(getCachedIngredient(new MapFluidIngredient(output), fluidOutputRoot)); + } + for (var output : chancedFluids) { + list.add(getCachedIngredient(new MapChancedFluidIngredient(output), chancedFluidOutputRoot)); + } + + list.sort(Comparator.comparingInt(AbstractMapIngredient::hashCode)); // Help improve speed of lookups + + return list.isEmpty() ? null : list; + } + } else { + return null; + } + } + + @Nullable + private static List linearFind(@NotNull RecipeMap map, + @NotNull List list, + @NotNull Predicate predicate) { + List result = new ArrayList<>(); + for (var recipe : map.getRecipeList()) { + var recipeOutputs = getOutputFromRecipe(recipe); + // This compares lists with order in mind, but since they are sorted by hashcode already, should be fine + if (recipeOutputs.equals(list) && predicate.test(recipe)) result.add(recipe); + } + return result.isEmpty() ? null : result; + } +} diff --git a/src/main/java/com/nomiceu/nomilabs/groovy/ABSRecipeReplacer.java b/src/main/java/com/nomiceu/nomilabs/groovy/ABSRecipeReplacer.java new file mode 100644 index 00000000..2eb7cdea --- /dev/null +++ b/src/main/java/com/nomiceu/nomilabs/groovy/ABSRecipeReplacer.java @@ -0,0 +1,36 @@ +package com.nomiceu.nomilabs.groovy; + +import gregicality.multiblocks.api.fluids.GCYMFluidStorageKeys; +import gregicality.multiblocks.api.recipes.alloyblast.AlloyBlastRecipeProducer; +import gregtech.api.recipes.RecipeBuilder; +import gregtech.api.recipes.builders.BlastRecipeBuilder; +import gregtech.api.unification.material.Material; +import gregtech.api.unification.material.properties.BlastProperty; +import net.minecraftforge.fluids.Fluid; +import org.jetbrains.annotations.NotNull; + +public class ABSRecipeReplacer extends AlloyBlastRecipeProducer { + public static final AlloyBlastRecipeProducer REPLACE_PRODUCER = new ABSRecipeReplacer(); + + /** + * Same as super, but doesn't make a vac freezer recipe. + */ + @Override + public void produce(@NotNull Material material, @NotNull BlastProperty blastProperty) { + int compAmount = material.getMaterialComponents().size(); + + // ignore non-alloys + if (compAmount < 2) return; + + // get the output fluid + Fluid molten = material.getFluid(GCYMFluidStorageKeys.MOLTEN); + + + RecipeBuilder builder = createBuilder(blastProperty, material); + + int outputAmount = addInputs(material, builder); + if (outputAmount <= 0) return; + + buildRecipes(blastProperty, molten, outputAmount, compAmount, builder); + } +} diff --git a/src/main/java/com/nomiceu/nomilabs/groovy/ChangeComposition.java b/src/main/java/com/nomiceu/nomilabs/groovy/ChangeComposition.java new file mode 100644 index 00000000..9713b8fd --- /dev/null +++ b/src/main/java/com/nomiceu/nomilabs/groovy/ChangeComposition.java @@ -0,0 +1,253 @@ +package com.nomiceu.nomilabs.groovy; + +import com.cleanroommc.groovyscript.api.GroovyBlacklist; +import com.nomiceu.nomilabs.NomiLabs; +import com.nomiceu.nomilabs.gregtech.mixinhelper.AccessibleMaterial; +import com.nomiceu.nomilabs.gregtech.mixinhelper.AccessibleRecipeMap; +import com.nomiceu.nomilabs.gregtech.mixinhelper.CompositionRecipeType; +import com.nomiceu.nomilabs.mixin.gregtech.AccessibleDecompositionRecipeHandler; +import gregicality.multiblocks.api.fluids.GCYMFluidStorageKeys; +import gregicality.multiblocks.api.recipes.GCYMRecipeMaps; +import gregtech.api.recipes.Recipe; +import gregtech.api.recipes.RecipeMaps; +import gregtech.api.recipes.ingredients.GTRecipeFluidInput; +import gregtech.api.recipes.ingredients.GTRecipeInput; +import gregtech.api.recipes.ingredients.GTRecipeItemInput; +import gregtech.api.recipes.ingredients.IntCircuitIngredient; +import gregtech.api.unification.OreDictUnifier; +import gregtech.api.unification.material.Material; +import gregtech.api.unification.material.properties.PropertyKey; +import gregtech.api.unification.ore.OrePrefix; +import gregtech.api.unification.stack.MaterialStack; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.FluidStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collections; +import java.util.Deque; +import java.util.List; + +@GroovyBlacklist +public class ChangeComposition { + public static void reloadCompositionRecipes() { + var specs = LabsVirtualizedRegistries.REPLACE_DECOMP_MANAGER.needReloading; + if (specs.isEmpty()) return; + + var time = System.currentTimeMillis(); + + changeDecomp(specs); + changeABS(specs); + changeMixer(specs); + finalize(specs); + + NomiLabs.LOGGER.info("Reloading Composition Recipes took {}ms", System.currentTimeMillis() - time); + } + + public static void restore(CompositionBuilder.CompositionSpecification spec) { + var mat = (AccessibleMaterial) spec.material; + + // Replace Chemical Formula and components + mat.setComponents(mat.getOriginalComponents(), true); + + // Remove New Generated Recipes & Restore Old Recipes + for (var entry : mat.getOriginalRecipes().entrySet()) { + entry.getKey().remove(spec.material); + if (entry.getValue() == null) continue; + for (var recipe : entry.getValue()) { + entry.getKey().map.compileRecipe(recipe); + } + } + } + + private static void changeDecomp(Deque specs) { + NomiLabs.LOGGER.debug("Replacing Decomp Recipes..."); + specs.stream().filter((spec) -> spec.changeDecomposition) + .distinct() // Should take the newest version, since isDistinct preserves first dupe, and we are using the Deque as a LIFO structure + .forEach((spec) -> { + NomiLabs.LOGGER.debug("---------------------------"); + NomiLabs.LOGGER.debug("Processing Spec For Decomp:"); + NomiLabs.LOGGER.debug(spec); + NomiLabs.LOGGER.debug("---------------------------"); + + var material = spec.material; + NomiLabs.LOGGER.debug("Removing Decomp Recipes for {}...", material.getRegistryName()); + + removeDecompRecipe(CompositionRecipeType.ELECTROLYZER, material); + removeDecompRecipe(CompositionRecipeType.CENTRIFUGE, material); + + if (spec.components.isEmpty()) return; + + var mat = (AccessibleMaterial) material; + // Temp Set Components so the handler makes correct recipe + mat.setComponents(spec.components); + mat.recalculateDecompositionType(); + + NomiLabs.LOGGER.debug("Adding Decomp Recipes for {}...", material.getRegistryName()); + OrePrefix prefix = material.hasProperty(PropertyKey.DUST) ? OrePrefix.dust : null; + AccessibleDecompositionRecipeHandler.processDecomposition(prefix, material); + }); + + NomiLabs.LOGGER.debug(""); + } + + private static void changeABS(Deque specs) { + NomiLabs.LOGGER.debug("Replacing ABS Recipes..."); + + // Since we already checked to see if the ABS flags are valid during the builder check phase, it should be fine now + specs.stream().filter((spec) -> spec.changeABS) + .distinct() // Should take the newest version, since isDistinct preserves first dupe, and we are using the Deque as a LIFO structure + .forEach((spec) -> { + NomiLabs.LOGGER.debug("------------------------"); + NomiLabs.LOGGER.debug("Processing Spec For ABS:"); + NomiLabs.LOGGER.debug(spec); + NomiLabs.LOGGER.debug("------------------------"); + + var material = spec.material; + NomiLabs.LOGGER.debug("Removing ABS Recipes for {}...", material.getRegistryName()); + + removeABSRecipe(material); + + if (spec.components.isEmpty()) return; + + var mat = (AccessibleMaterial) material; + // Temp Set Components so the handler makes correct recipe + mat.setComponents(spec.components); + + NomiLabs.LOGGER.debug("Adding ABS Recipes for {}...", material.getRegistryName()); + ABSRecipeReplacer.REPLACE_PRODUCER.produce(material, material.getProperty(PropertyKey.BLAST)); + }); + + NomiLabs.LOGGER.debug(""); + } + + private static void changeMixer(Deque specs) { + NomiLabs.LOGGER.debug("Replacing Mixer Recipes..."); + + // Since we already checked to see if the material has a dust property during the builder check phase, it should be fine now + specs.stream().filter((spec) -> spec.changeMixer) + .distinct() // Should take the newest version, since isDistinct preserves first dupe, and we are using the Deque as a LIFO structure + .forEach((spec) -> { + NomiLabs.LOGGER.debug("--------------------------"); + NomiLabs.LOGGER.debug("Processing Spec For Mixer:"); + NomiLabs.LOGGER.debug(spec); + NomiLabs.LOGGER.debug("--------------------------"); + + var material = spec.material; + NomiLabs.LOGGER.debug("Removing Mixer Recipes for {}...", material.getRegistryName()); + + var originalRecipe = removeMixerRecipe(material); + if (originalRecipe == null) return; + + var EUt = spec.mixerEUt == -1 ? originalRecipe.getEUt() : spec.mixerEUt; + var duration = spec.mixerDuration == -1 ? originalRecipe.getDuration() : spec.mixerDuration; + var circuit = spec.mixerCircuit == -1 ? getCircuit(originalRecipe.getInputs()) : spec.mixerCircuit; + var outputAmount = spec.mixerOutputAmount == -1 ? spec.components.stream() + .mapToInt((mat) -> (int) mat.amount).sum() : spec.mixerOutputAmount; + + if (spec.components.isEmpty()) return; + + NomiLabs.LOGGER.debug("Adding Mixer Recipes for {}...", material.getRegistryName()); + var builder = RecipeMaps.MIXER_RECIPES.recipeBuilder() + .inputs(getItemInputsFromComponents(spec.components).toArray(new GTRecipeInput[0])) + .fluidInputs(getFluidInputsFromComponents(spec.components)) + .outputs(OreDictUnifier.get(OrePrefix.dust, material, outputAmount)) + .EUt(EUt).duration(duration); + + if (circuit != 0) builder.circuitMeta(circuit); + builder.buildAndRegister(); + }); + + NomiLabs.LOGGER.debug(""); + } + + private static void finalize(Deque specs) { + // Replace Chemical Formula for those that need it, else revert the change to the material's components + var iter = specs.descendingIterator(); + while (iter.hasNext()) { + var spec = iter.next(); + var mat = (AccessibleMaterial) spec.material; + if (spec.changeChemicalFormula) + mat.setComponents(spec.components, true); + else + mat.setComponents(mat.getOriginalComponents()); + } + } + + public static void removeDecompRecipe(CompositionRecipeType type, Material input) { + ItemStack itemInput = ItemStack.EMPTY; + FluidStack fluidInput = null; + if (input.hasProperty(PropertyKey.DUST)) + itemInput = OreDictUnifier.get(OrePrefix.dust, input); + else + fluidInput = input.getFluid(1); + var map = type.map; + var recipe = map.find(itemInput.isEmpty() ? Collections.emptyList() : Collections.singletonList(itemInput), + fluidInput == null ? Collections.emptyList() : Collections.singletonList(fluidInput), + (recipe1) -> true); + ((AccessibleMaterial) input).setOriginalRecipes(type, recipe == null ? Collections.emptyList() : Collections.singletonList(recipe)); + if (recipe == null) return; + NomiLabs.LOGGER.debug("Removing Decomp Recipe for {} @ {} in recipe map {}.", + itemInput.getItem().getRegistryName(), itemInput.getMetadata(), + map.getUnlocalizedName()); + map.removeRecipe(recipe); + } + + public static void removeABSRecipe(Material input) { + var recipes = ((AccessibleRecipeMap) GCYMRecipeMaps.ALLOY_BLAST_RECIPES) + .findByOutput(Collections.emptyList(), Collections.singletonList(input.getFluid(GCYMFluidStorageKeys.MOLTEN, 1)), + Collections.emptyList(), Collections.emptyList(), (r) -> true); + ((AccessibleMaterial) input).setOriginalRecipes(CompositionRecipeType.ALLOY_BLAST, recipes == null ? Collections.emptyList() : recipes); + if (recipes == null) return; + for (var recipe : recipes) { + NomiLabs.LOGGER.debug("Removing ABS Recipe with inputs {} and fluid inputs {}.", recipe.getInputs(), recipe.getFluidInputs()); + GCYMRecipeMaps.ALLOY_BLAST_RECIPES.removeRecipe(recipe); + } + } + + @Nullable + public static Recipe removeMixerRecipe(Material input) { + var recipes = ((AccessibleRecipeMap) RecipeMaps.MIXER_RECIPES) + .findByOutput(Collections.singletonList(OreDictUnifier.get(OrePrefix.dust, input)), Collections.emptyList(), + Collections.emptyList(), Collections.emptyList(), (r) -> true); + ((AccessibleMaterial) input).setOriginalRecipes(CompositionRecipeType.MIXER, recipes == null ? Collections.emptyList() : recipes); + if (recipes == null) return null; + for (var recipe : recipes) { + NomiLabs.LOGGER.debug("Removing Mixer Recipe with inputs {} and fluid inputs {}.", recipe.getInputs(), recipe.getFluidInputs()); + RecipeMaps.MIXER_RECIPES.removeRecipe(recipe); + } + return recipes.get(0); + } + + private static int getCircuit(List inputs) { + for (var input : inputs) { + if (!input.isNonConsumable()) continue; + for (var stack : input.getInputStacks()) { + var circuit = IntCircuitIngredient.getCircuitConfiguration(stack); + if (circuit != 0) return circuit; + } + } + return 0; + } + + @NotNull + private static List getItemInputsFromComponents(List components) { + List result = new ObjectArrayList<>(); + for (var mat : components) { + if (!mat.material.hasProperty(PropertyKey.DUST)) continue; + result.add(new GTRecipeItemInput(OreDictUnifier.get(OrePrefix.dust, mat.material, (int) mat.amount))); + } + return result; + } + + @NotNull + private static List getFluidInputsFromComponents(List components) { + List result = new ObjectArrayList<>(); + for (var mat : components) { + if (mat.material.hasProperty(PropertyKey.DUST) || !mat.material.hasFluid()) continue; + result.add(new GTRecipeFluidInput(mat.material.getFluid((int) (mat.amount * 1000)))); + } + return result; + } +} diff --git a/src/main/java/com/nomiceu/nomilabs/groovy/CompositionBuilder.java b/src/main/java/com/nomiceu/nomilabs/groovy/CompositionBuilder.java new file mode 100644 index 00000000..92f7dd39 --- /dev/null +++ b/src/main/java/com/nomiceu/nomilabs/groovy/CompositionBuilder.java @@ -0,0 +1,308 @@ +package com.nomiceu.nomilabs.groovy; + +import com.cleanroommc.groovyscript.api.GroovyLog; +import com.cleanroommc.groovyscript.api.IIngredient; +import com.google.common.collect.ImmutableList; +import com.nomiceu.nomilabs.gregtech.mixinhelper.AccessibleMaterial; +import com.nomiceu.nomilabs.util.LabsGroovyHelper; +import gregicality.multiblocks.api.fluids.GCYMFluidStorageKeys; +import gregicality.multiblocks.api.unification.GCYMMaterialFlags; +import gregicality.multiblocks.api.unification.properties.GCYMPropertyKey; +import gregtech.api.GregTechAPI; +import gregtech.api.unification.OreDictUnifier; +import gregtech.api.unification.material.Material; +import gregtech.api.unification.material.info.MaterialFlags; +import gregtech.api.unification.material.properties.PropertyKey; +import gregtech.api.unification.stack.MaterialStack; +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.FluidStack; +import org.jetbrains.annotations.NotNull; + +import javax.annotation.Nullable; +import java.util.*; + +@SuppressWarnings("unused") +public class CompositionBuilder { + private final Material material; + + private List components = null; + private boolean changeChemicalFormula = false; + private boolean changeDecomposition = false; + private boolean changeABS = false; + private MixerSpecification changeMixer = null; + + public CompositionBuilder(@NotNull Material material) { + this.material = material; + } + + public CompositionBuilder setComponents(List components) { + this.components = getMatFromIIngredient(components); + return this; + } + + public CompositionBuilder removeComponents() { + components = Collections.emptyList(); + return this; + } + + public CompositionBuilder reloadComponents() { + components = ((AccessibleMaterial) material).getOriginalComponents(); + return this; + } + + public CompositionBuilder changeChemicalFormula() { + changeChemicalFormula = true; + return this; + } + + public CompositionBuilder changeDecompositionRecipes() { + changeDecomposition = true; + return this; + } + + public CompositionBuilder changeABS() { + changeABS = true; + return this; + } + + public CompositionBuilder changeMixer(){ + changeMixer = new MixerSpecification(); + return this; + } + + public CompositionBuilder changeMixer(MixerSpecification builder) { + changeMixer = builder; + return this; + } + + public void change() { + /* Checks */ + if (components == null) { + throwIfShould(new IllegalArgumentException("Cannot change when components, or removal, has not been specified!")); + return; + } + if (!changeChemicalFormula && !changeDecomposition && !changeABS && changeMixer == null) { + throwIfShould(new IllegalArgumentException("Cannot change when change method(s) have not been specified!")); + return; + } + if (changeDecomposition && material.hasFlag(MaterialFlags.DISABLE_DECOMPOSITION)) { + throwIfShould(new IllegalArgumentException("Cannot change when decomposition changing is specified, but the material has DISABLE_DECOMPOSITION flag!")); + return; + } + if (changeMixer != null && !material.hasProperty(PropertyKey.DUST)){ + throwIfShould(new IllegalArgumentException("Cannot change when mixer changing is specified, but the material does not have a Dust Property!")); + return; + } + if (changeABS && + (!material.hasProperty(GCYMPropertyKey.ALLOY_BLAST) || !material.hasProperty(PropertyKey.BLAST) + || !material.hasFluid() || material.getFluid(GCYMFluidStorageKeys.MOLTEN) == null + || material.hasFlag(GCYMMaterialFlags.NO_ALLOY_BLAST_RECIPES))) { + throwIfShould(new IllegalArgumentException("Cannot change when ABS changing is specified, but the material cannot generate ABS recipes to a Molten Fluid!")); + return; + } + + LabsVirtualizedRegistries.REPLACE_DECOMP_MANAGER.changeMaterialDecomp(new CompositionSpecification(this)); + } + + public static void throwIfShould(Exception e) { + if (LabsGroovyHelper.isRunningGroovyScripts()) { + GroovyLog.get().exception(e); + } + } + + /* Helpers */ + public static List getMatFromIIngredient(List ingredients) { + List result = new ArrayList<>(); + for (var ingredient : ingredients) { + //noinspection ConstantValue + if ((Object) ingredient instanceof ItemStack stack) { + var mat = getMatFromStack(stack); + result.add(mat); + continue; + } + if ((Object) ingredient instanceof MaterialStack stack) { + result.add(stack); + continue; + } + if ((Object) ingredient instanceof FluidStack stack) { + result.add(getMatFromFluid(stack)); + continue; + } + IllegalArgumentException e = new IllegalArgumentException("Component Specification must be an Item Stack, a Material Stack, or a Fluid Stack!"); + + if (!LabsGroovyHelper.isRunningGroovyScripts()) throw e; + + GroovyLog.get().exception(e); + result.add(null); + } + if (result.stream().anyMatch(Objects::isNull)) return null; + return result; + } + + @Nullable + public static MaterialStack getMatFromStack(ItemStack stack) { + MaterialStack material = OreDictUnifier.getMaterial(stack); + if (material == null) { + IllegalArgumentException e = new IllegalArgumentException( + String.format("Could not find Material for Stack %s @ %s, with %s.", + stack.getItem().getRegistryName(), stack.getMetadata(), + stack.hasTagCompound() ? "Tag " + stack.getTagCompound() : "No Tag")); + + if (!LabsGroovyHelper.isRunningGroovyScripts()) throw e; + + GroovyLog.get().exception(e); + return null; + } + return material.copy(stack.getCount()); + } + + @Nullable + public static MaterialStack getMatFromFluid(FluidStack fluid) { + // Material Getting needs modid, which we don't have + // Recursive through all registries to find it + Material mat = getMaterialFromFluid(fluid); + if (mat == null) return null; + + if (fluid.amount % 1000 != 0 || fluid.amount < 1000) { + IllegalArgumentException e = new IllegalArgumentException("Fluid Amount must be divisible by 1000, and be at least 1000!"); + + if (!LabsGroovyHelper.isRunningGroovyScripts()) throw e; + + GroovyLog.get().exception(e); + return null; + } + return new MaterialStack(mat, fluid.amount / 1000); + } + + @Nullable + public static Material getMaterialFromFluid(FluidStack fluid) { + // Material Getting needs modid, which we don't have + // Recursive through all registries to find it + var name = fluid.getFluid().getName(); + Material mat = null; + for (var registry : GregTechAPI.materialManager.getRegistries()) { + if (!registry.containsKey(name)) continue; + + var foundMat = registry.getObject(name); + if (foundMat == null || !foundMat.hasProperty(PropertyKey.FLUID)) continue; + + mat = foundMat; + break; + } + if (mat == null) { + IllegalArgumentException e = new IllegalArgumentException( + String.format("Could not find Material for Fluid %s!", fluid.getFluid().getName())); + + if (!LabsGroovyHelper.isRunningGroovyScripts()) throw e; + + GroovyLog.get().exception(e); + return null; + } + return mat; + } + + public static class MixerSpecification { + private int EUt = -1; + private int duration = -1; + private int circuit = -1; + private int outputAmount = -1; + + public MixerSpecification overrideEUt(int EUt) { + if (EUt <= 0) { + CompositionBuilder.throwIfShould(new IllegalArgumentException("EUt override must be larger than 0!")); + return this; + } + this.EUt = EUt; + return this; + } + + public MixerSpecification overrideDuration(int duration) { + if (duration <= 0) { + CompositionBuilder.throwIfShould(new IllegalArgumentException("Duration override must be larger than 0!")); + return this; + } + this.duration = duration; + return this; + } + + public MixerSpecification overrideCircuit(int circuit) { + if (circuit < 0) { + CompositionBuilder.throwIfShould(new IllegalArgumentException("Circuit override must be larger or equal to 0!")); + return this; + } + this.circuit = circuit; + return this; + } + + public MixerSpecification overrideOutputAmount(int outputAmount) { + if (outputAmount <= 0) { + CompositionBuilder.throwIfShould(new IllegalArgumentException("Output amount override must be larger or equal to 0!")); + return this; + } + this.outputAmount = outputAmount; + return this; + } + } + + public static class CompositionSpecification { + public final Material material; + public final ImmutableList components; + public final boolean changeChemicalFormula; + public final boolean changeDecomposition; + public final boolean changeABS; + public final boolean changeMixer; + public final int mixerEUt; + public final int mixerDuration; + public final int mixerCircuit; + public final int mixerOutputAmount; + + private CompositionSpecification(CompositionBuilder builder) { + material = builder.material; + components = ImmutableList.copyOf(builder.components); + changeChemicalFormula = builder.changeChemicalFormula; + changeDecomposition = builder.changeDecomposition; + changeABS = builder.changeABS; + changeMixer = builder.changeMixer != null; + if (changeMixer) { + mixerEUt = builder.changeMixer.EUt; + mixerDuration = builder.changeMixer.duration; + mixerCircuit = builder.changeMixer.circuit; + mixerOutputAmount = builder.changeMixer.outputAmount; + } else { + mixerEUt = -1; + mixerDuration = -1; + mixerCircuit = -1; + mixerOutputAmount = -1; + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + CompositionSpecification that = (CompositionSpecification) o; + return Objects.equals(material, that.material); + } + + @Override + public int hashCode() { + return Objects.hash(material); + } + + @Override + public String toString() { + return "DecompositionSpecification{" + + "material=" + material + + ", components=" + components + + ", changeChemicalFormula=" + changeChemicalFormula + + ", changeDecomposition=" + changeDecomposition + + ", changeABS=" + changeABS + + ", changeMixer=" + changeMixer + + ", mixerEUt=" + mixerEUt + + ", mixerDuration=" + mixerDuration + + ", mixerCircuit=" + mixerCircuit + + ", mixerOutputAmount=" + mixerOutputAmount + + '}'; + } + } +} diff --git a/src/main/java/com/nomiceu/nomilabs/groovy/GroovyHelpers.java b/src/main/java/com/nomiceu/nomilabs/groovy/GroovyHelpers.java index 095b10af..42f37df2 100644 --- a/src/main/java/com/nomiceu/nomilabs/groovy/GroovyHelpers.java +++ b/src/main/java/com/nomiceu/nomilabs/groovy/GroovyHelpers.java @@ -1,7 +1,5 @@ package com.nomiceu.nomilabs.groovy; -import com.cleanroommc.groovyscript.GroovyScript; -import com.cleanroommc.groovyscript.api.GroovyLog; import com.cleanroommc.groovyscript.api.IIngredient; import com.cleanroommc.groovyscript.compat.mods.ModSupport; import com.cleanroommc.groovyscript.compat.mods.jei.JeiPlugin; @@ -9,10 +7,11 @@ import com.cleanroommc.groovyscript.sandbox.ClosureHelper; import com.nomiceu.nomilabs.integration.jei.JEIPlugin; import com.nomiceu.nomilabs.util.LabsTranslate; +import gregtech.api.GTValues; import gregtech.api.GregTechAPI; -import gregtech.api.unification.OreDictUnifier; +import gregtech.api.recipes.chance.output.impl.ChancedFluidOutput; +import gregtech.api.recipes.chance.output.impl.ChancedItemOutput; import gregtech.api.unification.material.Material; -import gregtech.api.unification.material.properties.PropertyKey; import gregtech.api.unification.stack.MaterialStack; import gregtech.api.util.GTUtility; import gregtech.client.utils.TooltipHelper; @@ -23,11 +22,8 @@ import net.minecraftforge.fluids.Fluid; import net.minecraftforge.fluids.FluidStack; -import javax.annotation.Nullable; -import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Objects; /** * The interface for groovy to interact with. @@ -162,140 +158,39 @@ public static void removeStackRecycling(ItemStack output) { ReplaceRecipe.changeStackRecycling(output, Collections.emptyList()); } } - public static class ReplaceDecompositionHelpers { - public static void replaceDecomposition(Material material, List components) { - ReplaceDecomposition.replaceDecomposition(material, getMatFromIIngredient(components)); + public static class ChangeCompositionHelpers { + public static CompositionBuilder changeComposition(Material material) { + return new CompositionBuilder(material); } - public static void replaceDecomposition(MaterialStack material, List components) { - ReplaceDecomposition.replaceDecomposition(material.material, getMatFromIIngredient(components)); + public static CompositionBuilder replaceDeomposition(MaterialStack material) { + return new CompositionBuilder(material.material); } - public static void replaceDecomposition(ItemStack stack, List components) { - MaterialStack mat = getMatFromStack(stack); - if (mat == null) return; + public static CompositionBuilder changeComposition(ItemStack stack) { + MaterialStack mat = CompositionBuilder.getMatFromStack(stack); + if (mat == null) return new CompositionBuilder( + GregTechAPI.materialManager.getRegistry(GTValues.MODID).getFallbackMaterial()); - ReplaceDecomposition.replaceDecomposition(mat.material, getMatFromIIngredient(components)); + return new CompositionBuilder(mat.material); } - public static void replaceDecomposition(FluidStack stack, List components) { - Material mat = getMaterialFromFluid(stack); - if (mat == null) return; + public static CompositionBuilder changeComposition(FluidStack fluid) { + MaterialStack mat = CompositionBuilder.getMatFromFluid(fluid); + if (mat == null) return new CompositionBuilder( + GregTechAPI.materialManager.getRegistry(GTValues.MODID).getFallbackMaterial()); - ReplaceDecomposition.replaceDecomposition(mat, getMatFromIIngredient(components)); + return new CompositionBuilder(mat.material); } - - public static void removeDecomposition(Material material) { - ReplaceDecomposition.replaceDecomposition(material, Collections.emptyList()); - } - - public static void removeDecomposition(MaterialStack material) { - ReplaceDecomposition.replaceDecomposition(material.material, Collections.emptyList()); - } - - public static void removeDecomposition(ItemStack stack) { - MaterialStack mat = getMatFromStack(stack); - if (mat == null) return; - - ReplaceDecomposition.replaceDecomposition(mat.material, Collections.emptyList()); - } - - public static void removeDecomposition(FluidStack stack) { - Material mat = getMaterialFromFluid(stack); - if (mat == null) return; - - ReplaceDecomposition.replaceDecomposition(mat, Collections.emptyList()); - } - - /* Helpers */ - public static List getMatFromIIngredient(List ingredients) { - List result = new ArrayList<>(); - for (var ingredient : ingredients) { - //noinspection ConstantValue - if ((Object) ingredient instanceof ItemStack stack) { - var mat = getMatFromStack(stack); - result.add(mat); - continue; - } - if ((Object) ingredient instanceof MaterialStack stack) { - result.add(stack); - continue; - } - if ((Object) ingredient instanceof FluidStack stack) { - result.add(getMatFromFluid(stack)); - continue; - } - IllegalArgumentException e = new IllegalArgumentException("Component Specification must be an Item Stack, a Material Stack, or a Fluid Stack!"); - - if (!GroovyScript.getSandbox().isRunning()) throw e; - - GroovyLog.get().exception(e); - result.add(null); - } - if (result.stream().anyMatch(Objects::isNull)) return null; - return result; - } - - @Nullable - private static MaterialStack getMatFromStack(ItemStack stack) { - MaterialStack material = OreDictUnifier.getMaterial(stack); - if (material == null) { - IllegalArgumentException e = new IllegalArgumentException( - String.format("Could not find Material for Stack %s @ %s, with %s.", - stack.getItem().getRegistryName(), stack.getMetadata(), - stack.hasTagCompound() ? "Tag " + stack.getTagCompound() : "No Tag")); - - if (!GroovyScript.getSandbox().isRunning()) throw e; - - GroovyLog.get().exception(e); - return null; - } - return material.copy(stack.getCount()); - } - - @Nullable - private static MaterialStack getMatFromFluid(FluidStack fluid) { - // Material Getting needs modid, which we don't have - // Recursive through all registries to find it - Material mat = getMaterialFromFluid(fluid); - if (mat == null) return null; - - if (fluid.amount % 1000 != 0 || fluid.amount < 1000) { - IllegalArgumentException e = new IllegalArgumentException("Fluid Amount must be divisible by 1000, and be at least 1000!"); - - if (!GroovyScript.getSandbox().isRunning()) throw e; - - GroovyLog.get().exception(e); - return null; - } - return new MaterialStack(mat, fluid.amount / 1000); + } + public static class GTRecipeHelpers { + public static ChancedItemOutput chanced(ItemStack stack, int chance, int chanceBoost) { + return new ChancedItemOutput(stack, chance, chanceBoost); } - @Nullable - private static Material getMaterialFromFluid(FluidStack fluid) { - // Material Getting needs modid, which we don't have - // Recursive through all registries to find it - var name = fluid.getFluid().getName(); - Material mat = null; - for (var registry : GregTechAPI.materialManager.getRegistries()) { - if (!registry.containsKey(name)) continue; - - var foundMat = registry.getObject(name); - if (foundMat == null || !foundMat.hasProperty(PropertyKey.FLUID)) continue; - - mat = foundMat; - break; - } - if (mat == null) { - IllegalArgumentException e = new IllegalArgumentException( - String.format("Could not find Material for Fluid %s!", fluid.getFluid().getName())); - - if (!GroovyScript.getSandbox().isRunning()) throw e; - - GroovyLog.get().exception(e); - return null; - } - return mat; + public static ChancedFluidOutput chanced(FluidStack fluid, int chance, int chanceBoost) { + return new ChancedFluidOutput(fluid, chance, chanceBoost); } } + } diff --git a/src/main/java/com/nomiceu/nomilabs/groovy/LabsVirtualizedRegistries.java b/src/main/java/com/nomiceu/nomilabs/groovy/LabsVirtualizedRegistries.java index 2df5514c..4b1122a3 100644 --- a/src/main/java/com/nomiceu/nomilabs/groovy/LabsVirtualizedRegistries.java +++ b/src/main/java/com/nomiceu/nomilabs/groovy/LabsVirtualizedRegistries.java @@ -2,53 +2,42 @@ import com.cleanroommc.groovyscript.api.GroovyBlacklist; import com.cleanroommc.groovyscript.registry.VirtualizedRegistry; -import com.nomiceu.nomilabs.gregtech.AccessibleMaterial; import com.nomiceu.nomilabs.integration.jei.JEIPlugin; import com.nomiceu.nomilabs.util.ItemTagMeta; import gregtech.api.unification.OreDictUnifier; -import gregtech.api.unification.material.Material; import gregtech.api.unification.stack.ItemMaterialInfo; -import gregtech.api.unification.stack.MaterialStack; import net.minecraft.item.ItemStack; import org.apache.commons.lang3.tuple.Pair; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; + +import static com.nomiceu.nomilabs.groovy.CompositionBuilder.CompositionSpecification; @SuppressWarnings("unused") @GroovyBlacklist public class LabsVirtualizedRegistries { public static final ReplaceRecyclingManager REPLACE_RECYCLING_MANAGER = new ReplaceRecyclingManager(); - public static final ReplaceDecompositionManager REPLACE_DECOMP_MANAGER = new ReplaceDecompositionManager(); + public static final ReplaceCompositionManager REPLACE_DECOMP_MANAGER = new ReplaceCompositionManager(); public static final JEIManager JEI_MANAGER = new JEIManager(); - public static class ReplaceDecompositionManager extends VirtualizedRegistry>> { - public final Map> needReloading = new HashMap<>(); + public static class ReplaceCompositionManager extends VirtualizedRegistry { + public final Deque needReloading = new ArrayDeque<>(); @Override public void onReload() { - restoreFromBackup().forEach((pair) -> { - needReloading.put(pair.getLeft(), pair.getRight()); - ((AccessibleMaterial) pair.getKey()).setComponents(pair.getRight()); - }); + restoreFromBackup().forEach(ChangeComposition::restore); } @Override public void afterScriptLoad() { - ReplaceDecomposition.reloadDecompositionRecipes(); + ChangeComposition.reloadCompositionRecipes(); needReloading.clear(); } - @Override - protected boolean compareRecipe(Pair> a, Pair> b) { - return a.getKey().getRegistryName().equals(b.getKey().getRegistryName()); - } - - public void changeMaterialDecomp(Material material, List components) { - addBackup(Pair.of(material, material.getMaterialComponents())); - needReloading.put(material, components); - ((AccessibleMaterial) material).setComponents(components); + public void changeMaterialDecomp(CompositionSpecification spec) { + addBackup(spec); + // Add Items to the 'back' of the array deque, so that it's a LIFO structure + needReloading.addFirst(spec); } } diff --git a/src/main/java/com/nomiceu/nomilabs/groovy/ReplaceDecomposition.java b/src/main/java/com/nomiceu/nomilabs/groovy/ReplaceDecomposition.java deleted file mode 100644 index 48f922d2..00000000 --- a/src/main/java/com/nomiceu/nomilabs/groovy/ReplaceDecomposition.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.nomiceu.nomilabs.groovy; - -import com.cleanroommc.groovyscript.api.GroovyBlacklist; -import com.nomiceu.nomilabs.NomiLabs; -import com.nomiceu.nomilabs.mixin.gregtech.AccessibleDecompositionRecipeHandler; -import gregtech.api.recipes.RecipeMap; -import gregtech.api.recipes.RecipeMaps; -import gregtech.api.unification.OreDictUnifier; -import gregtech.api.unification.material.Material; -import gregtech.api.unification.material.properties.PropertyKey; -import gregtech.api.unification.ore.OrePrefix; -import gregtech.api.unification.stack.MaterialStack; -import net.minecraft.item.ItemStack; -import net.minecraftforge.fluids.FluidStack; - -import java.util.Collections; -import java.util.List; - -@GroovyBlacklist -public class ReplaceDecomposition { - public static void replaceDecomposition(Material material, List components) { - LabsVirtualizedRegistries.REPLACE_DECOMP_MANAGER.changeMaterialDecomp(material, components); - } - - public static void reloadDecompositionRecipes() { - if (LabsVirtualizedRegistries.REPLACE_DECOMP_MANAGER.needReloading.isEmpty()) return; - var time = System.currentTimeMillis(); - for (var modified : LabsVirtualizedRegistries.REPLACE_DECOMP_MANAGER.needReloading.entrySet()) { - var material = modified.getKey(); - NomiLabs.LOGGER.debug("Replacing Decomp Recipes for {}...", material.getRegistryName()); - removeDecompRecipe(RecipeMaps.ELECTROLYZER_RECIPES, material); - removeDecompRecipe(RecipeMaps.CENTRIFUGE_RECIPES, material); - if (modified.getValue().isEmpty()) continue; - OrePrefix prefix = material.hasProperty(PropertyKey.DUST) ? OrePrefix.dust : null; - NomiLabs.LOGGER.debug("Adding Decomp Recipes for {}...", material.getRegistryName()); - AccessibleDecompositionRecipeHandler.processDecomposition(prefix,material); - } - NomiLabs.LOGGER.info("Reloading Decomp Recipes took {}ms", System.currentTimeMillis() - time); - } - - private static void removeDecompRecipe(RecipeMap map, Material input) { - ItemStack itemInput = ItemStack.EMPTY; - FluidStack fluidInput = null; - if (input.hasProperty(PropertyKey.DUST)) - itemInput = OreDictUnifier.get(OrePrefix.dust, input); - else - fluidInput = input.getFluid(1); - var recipe = map.find(itemInput.isEmpty() ? Collections.emptyList() : Collections.singletonList(itemInput), - fluidInput == null ? Collections.emptyList() : Collections.singletonList(fluidInput), - (recipe1) -> true); - if (recipe == null) return; - NomiLabs.LOGGER.debug("Removing Decomp Recipe for {} @ {} in recipe map {}.", - itemInput.getItem().getRegistryName(), itemInput.getMetadata(), - map.getUnlocalizedName()); - map.removeRecipe(recipe); - } -} diff --git a/src/main/java/com/nomiceu/nomilabs/groovy/ReplaceRecipe.java b/src/main/java/com/nomiceu/nomilabs/groovy/ReplaceRecipe.java index 2877c1af..226dfab2 100644 --- a/src/main/java/com/nomiceu/nomilabs/groovy/ReplaceRecipe.java +++ b/src/main/java/com/nomiceu/nomilabs/groovy/ReplaceRecipe.java @@ -53,7 +53,7 @@ public static void reloadRecyclingRecipes() { var time = System.currentTimeMillis(); for (var modified : LabsVirtualizedRegistries.REPLACE_RECYCLING_MANAGER.needReloading.entrySet()) { var stack = modified.getKey(); - NomiLabs.LOGGER.debug("Replacing Recycling Recipes for {} @ {}...", stack.getItem().getRegistryName(), stack.getMetadata()); + NomiLabs.LOGGER.debug("Removing Recycling Recipes for {} @ {}...", stack.getItem().getRegistryName(), stack.getMetadata()); removeRecyclingRecipe(RecipeMaps.ARC_FURNACE_RECIPES, RecipeCategories.ARC_FURNACE_RECYCLING, stack, Materials.Oxygen.getFluid()); removeRecyclingRecipe(RecipeMaps.MACERATOR_RECIPES, RecipeCategories.MACERATOR_RECYCLING, stack, null); removeRecyclingRecipe(RecipeMaps.EXTRACTOR_RECIPES, RecipeCategories.EXTRACTOR_RECYCLING, stack, null); @@ -130,7 +130,6 @@ public static void createRecipe(ItemStack output, List> input) registerRecycling(output, input); } - public static void changeStackRecycling(ItemStack output, List ingredients) { registerRecycling(output, Collections.singletonList(ingredients)); } @@ -168,6 +167,10 @@ private static boolean isRecipeValid(ResourceLocation name) { } private static void registerRecycling(ItemStack output, List> inputs) { + if (inputs.isEmpty() || inputs.stream().allMatch(List::isEmpty)) { + LabsVirtualizedRegistries.REPLACE_RECYCLING_MANAGER.registerOre(output, null); + return; + } List gtInputs = new ArrayList<>(); for (var inputList : inputs) { for (var input : inputList) { diff --git a/src/main/java/com/nomiceu/nomilabs/mixin/gregtech/AccessibleMaterialInfo.java b/src/main/java/com/nomiceu/nomilabs/mixin/gregtech/AccessibleMaterialInfo.java index bd9a00ac..4767b934 100644 --- a/src/main/java/com/nomiceu/nomilabs/mixin/gregtech/AccessibleMaterialInfo.java +++ b/src/main/java/com/nomiceu/nomilabs/mixin/gregtech/AccessibleMaterialInfo.java @@ -5,6 +5,9 @@ import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; +/** + * Allows setting the component list in Material Infos. + */ @Mixin(targets = "gregtech.api.unification.material.Material$MaterialInfo", remap = false) public interface AccessibleMaterialInfo { @Accessor(value = "componentList") diff --git a/src/main/java/com/nomiceu/nomilabs/mixin/gregtech/FluidStorageKeyMixin.java b/src/main/java/com/nomiceu/nomilabs/mixin/gregtech/FluidStorageKeyMixin.java index b182b977..fd983e10 100644 --- a/src/main/java/com/nomiceu/nomilabs/mixin/gregtech/FluidStorageKeyMixin.java +++ b/src/main/java/com/nomiceu/nomilabs/mixin/gregtech/FluidStorageKeyMixin.java @@ -8,6 +8,9 @@ import java.util.Map; +/** + * Allows getting all possible FluidStorageKey options. + */ @Mixin(value = FluidStorageKey.class, remap = false) public interface FluidStorageKeyMixin { @Accessor(value = "keys") diff --git a/src/main/java/com/nomiceu/nomilabs/mixin/gregtech/GTRecipeWrapperMixin.java b/src/main/java/com/nomiceu/nomilabs/mixin/gregtech/GTRecipeWrapperMixin.java new file mode 100644 index 00000000..fcb465a6 --- /dev/null +++ b/src/main/java/com/nomiceu/nomilabs/mixin/gregtech/GTRecipeWrapperMixin.java @@ -0,0 +1,51 @@ +package com.nomiceu.nomilabs.mixin.gregtech; + +import com.nomiceu.nomilabs.util.LabsTranslate; +import gregtech.api.gui.GuiTextures; +import gregtech.api.recipes.Recipe; +import gregtech.integration.RecipeCompatUtil; +import gregtech.integration.jei.recipe.GTRecipeWrapper; +import gregtech.integration.jei.utils.AdvancedRecipeWrapper; +import gregtech.integration.jei.utils.JeiButton; +import net.minecraft.client.Minecraft; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.function.BooleanSupplier; + +/** + * If the recipe is from CT or GS, shows an info logo, saying that the recipe is from CT/GS, + * instead of showing the button to copy a script to remove the recipe. + */ +@Mixin(value = GTRecipeWrapper.class, remap = false) +public abstract class GTRecipeWrapperMixin extends AdvancedRecipeWrapper { + @Shadow + @Final + private Recipe recipe; + @Inject(method = "initExtras", at = @At("TAIL")) + public void showIsCustomRecipe(CallbackInfo ci) { + if (!RecipeCompatUtil.isTweakerLoaded()) return; + + BooleanSupplier creativePlayerPredicate = () -> Minecraft.getMinecraft().player != null && + Minecraft.getMinecraft().player.isCreative(); + BooleanSupplier creativeTweaker = () -> creativePlayerPredicate.getAsBoolean() && (recipe.getIsCTRecipe() || recipe.isGroovyRecipe()); + BooleanSupplier creativeDefault = () -> creativePlayerPredicate.getAsBoolean() && !recipe.getIsCTRecipe() && !recipe.isGroovyRecipe(); + + if (buttons.isEmpty()) return; + // Get Newest Button, which should be the default button + buttons.get(buttons.size() - 1).setActiveSupplier(creativeDefault); + + buttons.add(new JeiButton(166, 2, 10, 10) + .setTextures(GuiTextures.INFO_ICON) + .setTooltipBuilder(lines -> + lines.add(recipe.isGroovyRecipe() ? + LabsTranslate.translate("gui.nomilabs.recipes.tooltip.gs_recipe") : + LabsTranslate.translate("gui.nomilabs.recipes.tooltip.ct_recipe"))) + .setClickAction((mc, x, y, button) -> false) + .setActiveSupplier(creativeTweaker)); + } +} diff --git a/src/main/java/com/nomiceu/nomilabs/mixin/gregtech/MaterialFlagsMixin.java b/src/main/java/com/nomiceu/nomilabs/mixin/gregtech/MaterialFlagsMixin.java new file mode 100644 index 00000000..58eee179 --- /dev/null +++ b/src/main/java/com/nomiceu/nomilabs/mixin/gregtech/MaterialFlagsMixin.java @@ -0,0 +1,30 @@ +package com.nomiceu.nomilabs.mixin.gregtech; + +import com.nomiceu.nomilabs.gregtech.mixinhelper.AccessibleMaterialFlags; +import gregtech.api.unification.material.info.MaterialFlag; +import gregtech.api.unification.material.info.MaterialFlags; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; + +import java.util.Set; + +/** + * Allows removing flags. + */ +@Mixin(value = MaterialFlags.class, remap = false) +public class MaterialFlagsMixin implements AccessibleMaterialFlags { + + @Shadow + @Final + private Set flags; + + @Override + @Unique + public void removeFlags(MaterialFlag... toRemove) { + for (var flag : toRemove) { + flags.remove(flag); + } + } +} diff --git a/src/main/java/com/nomiceu/nomilabs/mixin/gregtech/MaterialMixin.java b/src/main/java/com/nomiceu/nomilabs/mixin/gregtech/MaterialMixin.java index c614cf2c..09ac1b56 100644 --- a/src/main/java/com/nomiceu/nomilabs/mixin/gregtech/MaterialMixin.java +++ b/src/main/java/com/nomiceu/nomilabs/mixin/gregtech/MaterialMixin.java @@ -1,22 +1,34 @@ package com.nomiceu.nomilabs.mixin.gregtech; import com.google.common.collect.ImmutableList; -import com.nomiceu.nomilabs.gregtech.AccessibleMaterial; +import com.google.common.collect.ImmutableMap; +import com.nomiceu.nomilabs.gregtech.mixinhelper.AccessibleMaterial; +import com.nomiceu.nomilabs.gregtech.mixinhelper.AccessibleMaterialFlags; +import com.nomiceu.nomilabs.gregtech.mixinhelper.CompositionRecipeType; +import com.nomiceu.nomilabs.groovy.CompositionBuilder; +import gregtech.api.recipes.Recipe; import gregtech.api.unification.material.Material; +import gregtech.api.unification.material.info.MaterialFlag; +import gregtech.api.unification.material.info.MaterialFlags; import gregtech.api.unification.stack.MaterialStack; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import org.apache.commons.lang3.ArrayUtils; import org.jetbrains.annotations.NotNull; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import java.lang.reflect.Field; import java.util.List; +import java.util.Map; /** - * Allows setting material components. + * Allows setting material components, and saving original components. */ @Mixin(value = Material.class, remap = false) public abstract class MaterialMixin implements AccessibleMaterial { @@ -26,6 +38,24 @@ public abstract class MaterialMixin implements AccessibleMaterial { @Shadow private String chemicalFormula; + @Unique + private ImmutableList originalComponents = null; + + @Unique + private boolean hasSetFlags = false; + + @Unique + private boolean calculatedDecomp = false; + + @Unique + private final Map> originalRecipes = new Object2ObjectOpenHashMap<>(); + + @Unique + private final MaterialFlag[] decompFlags = new MaterialFlag[] { + MaterialFlags.DISABLE_DECOMPOSITION, + MaterialFlags.DECOMPOSITION_BY_CENTRIFUGING, + MaterialFlags.DECOMPOSITION_BY_ELECTROLYZING}; + @Shadow @NotNull protected abstract String calculateChemicalFormula(); @@ -33,23 +63,102 @@ public abstract class MaterialMixin implements AccessibleMaterial { @Shadow protected abstract void calculateDecompositionType(); + @Shadow + public abstract ImmutableList getMaterialComponents(); + + @Shadow + @Final + @NotNull + private MaterialFlags flags; + + @Unique @Override - public void setComponents(List components) { - try { - // Java reflection because mixin dies at shadowing fields with private types - Field f = Material.class.getDeclaredField("materialInfo"); - ((AccessibleMaterialInfo) f.get(this)).setComponentList(ImmutableList.copyOf(components)); + public void setComponents(ImmutableList components, boolean changeFormula) { + setComponents(components); + if (changeFormula) { // Recalculate Chemical Formula and Decomposition Type // First set chemical formula to CT/Addon Set Formula (If Exists) chemicalFormula = setChemicalFormula; // Then Recalculate (Returns the set formula if it exists) chemicalFormula = calculateChemicalFormula(); - calculateDecompositionType(); + } + if (chemicalFormula == null) chemicalFormula = ""; + recalculateDecompositionType(); + } + + @Unique + @Override + public void setComponents(ImmutableList components) { + if (originalComponents == null) originalComponents = getMaterialComponents(); + try { + // Java reflection because mixin dies at shadowing fields with private types + Field f = Material.class.getDeclaredField("materialInfo"); + ((AccessibleMaterialInfo) f.get(this)).setComponentList(components); } catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException ignored) {} } @Inject(method = "setFormula(Ljava/lang/String;Z)Lgregtech/api/unification/material/Material;", at = @At("RETURN")) - public void setChemicalFormula(String formula, boolean withFormatting, CallbackInfoReturnable cir) { + private void setChemicalFormula(String formula, boolean withFormatting, CallbackInfoReturnable cir) { setChemicalFormula = chemicalFormula; } + + @Inject(method = "calculateDecompositionType", at = @At("HEAD")) + private void saveSetFlags(CallbackInfo ci) { + boolean hasDecompFlags = false; + for (var flag : decompFlags) { + if (!flags.hasFlag(flag)) continue; + hasDecompFlags = true; + break; + } + if (hasDecompFlags && !calculatedDecomp) + hasSetFlags = true; + + calculatedDecomp = true; + } + + @Inject(method = "addFlags([Lgregtech/api/unification/material/info/MaterialFlag;)V", at = @At("TAIL")) + private void checkFlags(MaterialFlag[] flags, CallbackInfo ci) { + if (hasSetFlags) return; + + for (var flag : decompFlags) { + if (!ArrayUtils.contains(flags, flag)) continue; + hasSetFlags = true; + break; + } + } + + @Override + @Unique + public ImmutableList getOriginalComponents() { + return originalComponents == null ? getMaterialComponents() : originalComponents; + } + + @Override + @Unique + public void recalculateDecompositionType() { + if (!hasSetFlags && calculatedDecomp) { + ((AccessibleMaterialFlags) flags).removeFlags(MaterialFlags.DISABLE_DECOMPOSITION, + MaterialFlags.DECOMPOSITION_BY_CENTRIFUGING, + MaterialFlags.DECOMPOSITION_BY_ELECTROLYZING); + } + + calculateDecompositionType(); + } + + @Unique + @SuppressWarnings("unused") + public CompositionBuilder changeComposition() { + return new CompositionBuilder((Material) (Object) this); + } + + @Override + public void setOriginalRecipes(CompositionRecipeType type, List originals) { + if (originalRecipes.containsKey(type)) return; + originalRecipes.put(type, originals); + } + + @Override + public Map> getOriginalRecipes() { + return ImmutableMap.copyOf(originalRecipes); + } } diff --git a/src/main/java/com/nomiceu/nomilabs/mixin/gregtech/MaterialStackMixin.java b/src/main/java/com/nomiceu/nomilabs/mixin/gregtech/MaterialStackMixin.java index 2f7bfcaa..d4dc99d3 100644 --- a/src/main/java/com/nomiceu/nomilabs/mixin/gregtech/MaterialStackMixin.java +++ b/src/main/java/com/nomiceu/nomilabs/mixin/gregtech/MaterialStackMixin.java @@ -3,6 +3,9 @@ import com.cleanroommc.groovyscript.api.IIngredient; import com.cleanroommc.groovyscript.api.IResourceStack; import gregtech.api.unification.stack.MaterialStack; +import net.minecraft.item.ItemStack; +import net.minecraft.item.crafting.Ingredient; +import org.apache.commons.lang3.NotImplementedException; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -21,7 +24,44 @@ public abstract class MaterialStackMixin implements IIngredient { @Shadow public abstract MaterialStack copy(long amount); + @Shadow + public abstract MaterialStack copy(); + + @Unique + @Override + public IIngredient exactCopy() { + return (IIngredient) copy(); + } + + @Unique + @Override + public int getAmount() { + return (int) amount; + } + + @Unique + @Override + public void setAmount(int amount) { + throw new NotImplementedException("Material Stack cannot be modified!"); + } + + @Override + public boolean test(ItemStack stack) { + return false; + } + + @Override + public ItemStack[] getMatchingStacks() { + return new ItemStack[0]; + } + + @Override + public Ingredient toMcIngredient() { + return Ingredient.EMPTY; + } + @Unique + @Override public IResourceStack multiply(Number num) { return (IResourceStack) copy(num.longValue()); } diff --git a/src/main/java/com/nomiceu/nomilabs/mixin/gregtech/MetaItemsMixin.java b/src/main/java/com/nomiceu/nomilabs/mixin/gregtech/MetaItemsMixin.java index 41373796..ff4432f1 100644 --- a/src/main/java/com/nomiceu/nomilabs/mixin/gregtech/MetaItemsMixin.java +++ b/src/main/java/com/nomiceu/nomilabs/mixin/gregtech/MetaItemsMixin.java @@ -8,6 +8,9 @@ import java.util.List; +/** + * Allows getting the Ore Prefixes used in Generating Meta Items for Materials. + */ @Mixin(value = MetaItems.class, remap = false) public interface MetaItemsMixin { @Accessor(value = "orePrefixes") diff --git a/src/main/java/com/nomiceu/nomilabs/mixin/gregtech/RecipeMapMixin.java b/src/main/java/com/nomiceu/nomilabs/mixin/gregtech/RecipeMapMixin.java index bcad4ccc..cd55934f 100644 --- a/src/main/java/com/nomiceu/nomilabs/mixin/gregtech/RecipeMapMixin.java +++ b/src/main/java/com/nomiceu/nomilabs/mixin/gregtech/RecipeMapMixin.java @@ -1,23 +1,41 @@ package com.nomiceu.nomilabs.mixin.gregtech; +import com.nomiceu.nomilabs.gregtech.mixinhelper.AccessibleRecipeMap; +import com.nomiceu.nomilabs.gregtech.mixinhelper.OutputBranch; +import com.nomiceu.nomilabs.gregtech.mixinhelper.RecipeMapLogic; import com.nomiceu.nomilabs.groovy.ReplaceRecipe; import gregtech.api.recipes.Recipe; import gregtech.api.recipes.RecipeMap; +import gregtech.api.recipes.chance.output.impl.ChancedFluidOutput; +import gregtech.api.recipes.chance.output.impl.ChancedItemOutput; import gregtech.api.util.ValidationResult; +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.FluidStack; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import java.util.Collection; +import java.util.List; import java.util.Objects; +import java.util.function.Predicate; +import java.util.stream.Collectors; /** + * Allows for lookup with outputs. + *

* Precaution to make sure only Recycling Recipes are added during recycling recipe reloading.
* This is because Arc Smelting sometimes generates non-recycling recipes. */ @Mixin(value = RecipeMap.class, remap = false) -public class RecipeMapMixin { +public abstract class RecipeMapMixin implements AccessibleRecipeMap { + @Unique + private final OutputBranch outputLookup = new OutputBranch(); + @Inject(method = "addRecipe", at = @At("HEAD"), cancellable = true) public void addRecipeInRecycling(@NotNull ValidationResult validationResult, CallbackInfoReturnable cir) { if (!ReplaceRecipe.isReloadingRecycling()) return; @@ -26,4 +44,55 @@ public void addRecipeInRecycling(@NotNull ValidationResult validationRes validationResult.getResult().getRecipeCategory())) cir.setReturnValue(false); } + + @Inject(method = "compileRecipe", + at = @At(value = "INVOKE", + target = "Ljava/util/Map;compute(Ljava/lang/Object;Ljava/util/function/BiFunction;)Ljava/lang/Object;")) + private void updateOutputLookupAdd(Recipe recipe, CallbackInfoReturnable cir) { + RecipeMapLogic.add(recipe, outputLookup); + } + + @Inject(method = "removeRecipe", + at = @At(value = "INVOKE", + target = "Lgregtech/integration/groovy/GroovyScriptModule;isCurrentlyRunning()Z")) + private void updateOutputLookupRemove(Recipe recipe, CallbackInfoReturnable cir) { + RecipeMapLogic.remove(recipe, outputLookup); + } + + /* Public Interface-Visible Methods */ + @Unique + @Nullable + @Override + public List findByOutput(@NotNull Collection items, @NotNull Collection fluids, + @NotNull Collection chancedItems, @NotNull Collection chancedFluids, + @NotNull Predicate canHandle) { + return RecipeMapLogic.find(outputLookup, (RecipeMap) (Object) this, items, fluids, chancedItems, chancedFluids, canHandle); + } + + @Unique + @Nullable + @Override + public List findRecipeByOutput(long voltage, List inputs, List fluidInputs, + List chancedItems, List chancedFluids) { + return findRecipeByOutput(voltage, inputs, fluidInputs, chancedItems, chancedFluids, false); + } + + @Unique + @Nullable + @Override + public List findRecipeByOutput(long voltage, List inputs, List fluidInputs, + List chancedItems, List chancedFluids, boolean exactVoltage) { + List items = inputs.stream().filter(s -> !s.isEmpty()).collect(Collectors.toList()); + List fluids = fluidInputs.stream().filter(f -> f != null && f.amount != 0) + .collect(Collectors.toList()); + + return findByOutput(items, fluids, chancedItems, chancedFluids, (recipe) -> { + if (exactVoltage && recipe.getEUt() != voltage) { + // if exact voltage is required, the recipe is not considered valid + return false; + } + // there is not enough voltage to consider the recipe valid + return recipe.getEUt() <= voltage; + }); + } } diff --git a/src/main/java/com/nomiceu/nomilabs/mixin/gregtech/VirtualizedRecipeMapMixin.java b/src/main/java/com/nomiceu/nomilabs/mixin/gregtech/VirtualizedRecipeMapMixin.java new file mode 100644 index 00000000..28c3c5c6 --- /dev/null +++ b/src/main/java/com/nomiceu/nomilabs/mixin/gregtech/VirtualizedRecipeMapMixin.java @@ -0,0 +1,203 @@ +package com.nomiceu.nomilabs.mixin.gregtech; + +import com.cleanroommc.groovyscript.api.GroovyLog; +import com.nomiceu.nomilabs.gregtech.mixinhelper.AccessibleRecipeMap; +import com.nomiceu.nomilabs.util.LabsGroovyHelper; +import gregtech.api.recipes.Recipe; +import gregtech.api.recipes.RecipeMap; +import gregtech.api.recipes.category.GTRecipeCategory; +import gregtech.api.recipes.chance.output.impl.ChancedFluidOutput; +import gregtech.api.recipes.chance.output.impl.ChancedItemOutput; +import gregtech.integration.groovy.VirtualizedRecipeMap; +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.FluidStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; + +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +/** + * Allows calling of `removeByOutput` via `mods.gregtech.<RECIPE_MAP_NAME>` calls in Groovy. + */ +@Mixin(value = VirtualizedRecipeMap.class, remap = false) +@SuppressWarnings("unused") +public abstract class VirtualizedRecipeMapMixin { + @Shadow + @Final + private RecipeMap recipeMap; + + @Shadow + public abstract String getName(); + + @Unique + @Nullable + public Recipe find(List inputs, List fluidInputs) { + return find((r) -> true, inputs, fluidInputs); + } + + @Unique + @Nullable + public Recipe find(GTRecipeCategory category, List inputs, List fluidInputs) { + return find((r) -> Objects.equals(category, r.getRecipeCategory()), inputs, fluidInputs); + } + + @SuppressWarnings("DuplicatedCode") + @Unique + public Recipe find(Predicate condition, List inputs, List fluidInputs) { + inputs = validateList(inputs); + fluidInputs = validateList(fluidInputs); + List items = inputs.stream().filter((s) -> !s.isEmpty()).collect(Collectors.toList()); + List fluids = fluidInputs.stream().filter((f) -> f != null && f.amount != 0).collect(Collectors.toList()); + return recipeMap.find(items, fluids, condition); + } + + @Unique + public boolean removeByInput(List items, List fluids) { + return removeByInput((r) -> true, items, fluids, String.format("items: %s, fluids: %s", items, fluids)); + } + + @Unique + public boolean removeByInput(GTRecipeCategory category, List items, List fluids) { + return removeByInput((r) -> Objects.equals(r.getRecipeCategory(), category), items, fluids, + String.format("category: %s, items %s, fluids %s", category, items, fluids)); + } + + @Unique + public boolean removeByInput(Predicate condition, List items, List fluids) { + return removeByInput(condition, items, fluids, String.format("items: %s, fluids: %s", items, fluids)); + } + + @Unique + private boolean removeByInput(Predicate condition, List items, List fluids, String components) { + Recipe recipe = find(condition, items, fluids); + if (recipe == null) { + if (LabsGroovyHelper.isRunningGroovyScripts()) { + GroovyLog.msg("Error removing GregTech " + getName() + " recipe") + .add("could not find recipe for: " + components) + .error() + .post(); + } + return false; + } + recipeMap.removeRecipe(recipe); + return true; + } + + @Unique + @Nullable + public List findByOutput(long voltage, List inputs, List fluidInputs, + List chancedItems, List chancedFluids) { + inputs = validateList(inputs); + fluidInputs = validateList(fluidInputs); + chancedItems = validateList(chancedItems); + chancedFluids = validateList(chancedFluids); + return getAccessibleRecipeMap().findRecipeByOutput(voltage, inputs, fluidInputs, chancedItems, chancedFluids); + } + + @Unique + @Nullable + public List findByOutput(List inputs, List fluidInputs, + List chancedItems, List chancedFluids) { + return findByOutput((r) -> true, inputs, fluidInputs, chancedItems, chancedFluids); + } + + @Unique + @Nullable + public List findRecipeByOutput(GTRecipeCategory category, List inputs, List fluidInputs, + List chancedItems, List chancedFluids) { + return findByOutput((r) -> Objects.equals(r.getRecipeCategory(), category), inputs, fluidInputs, chancedItems, chancedFluids); + } + + @Unique + public List findByOutput(Predicate condition, List inputs, List fluidInputs, + List chancedItems, List chancedFluids) { + inputs = validateList(inputs); + fluidInputs = validateList(fluidInputs); + chancedItems = validateList(chancedItems); + chancedFluids = validateList(chancedFluids); + List items = inputs.stream().filter((s) -> !s.isEmpty()).collect(Collectors.toList()); + List fluids = fluidInputs.stream().filter((f) -> f != null && f.amount != 0).collect(Collectors.toList()); + return getAccessibleRecipeMap().findByOutput(items, fluids, chancedItems, chancedFluids, condition); + } + + @Unique + public boolean removeByOutput(long voltage, List items, List fluids, + List chancedItems, List chancedFluids) { + List recipes = findByOutput(voltage, items, fluids, chancedItems, chancedFluids); + if (recipes == null) { + if (LabsGroovyHelper.isRunningGroovyScripts()) { + GroovyLog.msg("Error removing GregTech " + getName() + " recipe") + .add("could not find recipe for: voltage {}, items: {}, fluids: {}, chanced items: {}, chanced fluids: {}", voltage, items, fluids, chancedItems, chancedFluids) + .error() + .post(); + } + return false; + } + for (var recipe : recipes) { + recipeMap.removeRecipe(recipe); + } + return true; + } + + @Unique + public boolean removeByOutput(List items, List fluids, + List chancedItems, List chancedFluids) { + return removeByOutput((r) -> true, items, fluids, chancedItems, chancedFluids, + String.format("items: %s, fluids: %s, chanced items: %s, chanced fluids: %s", items, fluids, chancedItems, chancedFluids)); + } + + @Unique + public boolean removeByOutput(GTRecipeCategory category, List items, List fluids, + List chancedItems, List chancedFluids) { + return removeByOutput((r) -> Objects.equals(r.getRecipeCategory(), category), items, fluids, + chancedItems, chancedFluids, + String.format("category: %s, items: %s, fluids: %s, chanced items: %s, chanced fluids: %s", category, items, fluids, chancedItems, chancedFluids)); + } + + @Unique + public boolean removeByOutput(Predicate condition, List items, List fluids, + List chancedItems, List chancedFluids) { + return removeByOutput(condition, items, fluids, chancedItems, chancedFluids, + String.format("items: %s, fluids: %s, chanced items: %s, chanced fluids: %s", items, fluids, chancedItems, chancedFluids)); + } + + @Unique + private boolean removeByOutput(Predicate condition, List items, List fluids, + List chancedItems, List chancedFluids, + String components) { + List recipes = findByOutput(condition, items, fluids, chancedItems, chancedFluids); + if (recipes == null) { + if (LabsGroovyHelper.isRunningGroovyScripts()) { + GroovyLog.msg("Error removing GregTech " + getName() + " recipes by output") + .add("could not find recipe for: " + components) + .error() + .post(); + } + return false; + } + for (var recipe : recipes) { + recipeMap.removeRecipe(recipe); + } + return true; + } + + @Unique + private AccessibleRecipeMap getAccessibleRecipeMap() { + return (AccessibleRecipeMap) recipeMap; + } + + @Unique + @NotNull + private List validateList(@Nullable List list) { + if (list == null || list.isEmpty()) return Collections.emptyList(); + return list; + } +} diff --git a/src/main/java/com/nomiceu/nomilabs/remap/datafixer/DataFixerHandler.java b/src/main/java/com/nomiceu/nomilabs/remap/datafixer/DataFixerHandler.java index 9669b535..7bfbf716 100644 --- a/src/main/java/com/nomiceu/nomilabs/remap/datafixer/DataFixerHandler.java +++ b/src/main/java/com/nomiceu/nomilabs/remap/datafixer/DataFixerHandler.java @@ -176,6 +176,8 @@ private static void determineNeededFixesAndLog(SaveHandler save) { if (mods.containsKey(LabsValues.LABS_MODID) && mods.get(LabsValues.LABS_MODID).equals(LabsValues.LABS_VERSION)) return; + DataFixerHandler.worldSavedData.processModList(mods); + NomiLabs.LOGGER.info("NEEDED DATA FIXES: ----------------------------------------"); for (var fixType : LabsFixes.fixes.keySet()) { NomiLabs.LOGGER.info("SECTION: {} -------------------------------------------", fixType); diff --git a/src/main/java/com/nomiceu/nomilabs/remap/datafixer/LabsFixes.java b/src/main/java/com/nomiceu/nomilabs/remap/datafixer/LabsFixes.java index f8b85508..23dab43a 100644 --- a/src/main/java/com/nomiceu/nomilabs/remap/datafixer/LabsFixes.java +++ b/src/main/java/com/nomiceu/nomilabs/remap/datafixer/LabsFixes.java @@ -1,5 +1,6 @@ package com.nomiceu.nomilabs.remap.datafixer; +import com.google.common.collect.ImmutableMap; import com.nomiceu.nomilabs.LabsValues; import com.nomiceu.nomilabs.config.LabsConfig; import com.nomiceu.nomilabs.remap.LabsRemapHelper; @@ -14,14 +15,18 @@ import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import it.unimi.dsi.fastutil.shorts.Short2ShortLinkedOpenHashMap; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagString; import net.minecraft.util.ResourceLocation; import net.minecraft.util.datafix.IFixType; import net.minecraftforge.common.util.Constants; +import net.minecraftforge.fml.common.Loader; +import org.jetbrains.annotations.Nullable; import java.util.*; import static com.nomiceu.nomilabs.LabsValues.*; -import static com.nomiceu.nomilabs.util.LabsNames.*; +import static com.nomiceu.nomilabs.util.LabsNames.makeLabsName; /** * Definitions for all values, and all data fixes. @@ -42,16 +47,32 @@ public class LabsFixes { */ public static final String DATA_KEY = "LabsFixer"; + /** + * The Data Fixer Version that occurs when a world has not been loaded with Nomi Labs before, and Nomi-CEu Specific Fixes are not enabled. + *

+ * This is not for new worlds; new worlds must never have Data Fixes. + *

+ * When comparing against this version, check for equality rather than less than! + */ + public static final int NEW = Integer.MAX_VALUE; + /** * The current data format version. Increment this when breaking changes are made and the * data mixer must be applied. If this is not incremented, nothing will be applied. */ - public static final int CURRENT = 2; + public static final int CURRENT = 3; + + /** + * Version before Capacitor Remapping. + *

+ * Versions before this need nbt of custom capacitors removed. + */ + public static final int PRE_CAPACITOR_REMAPPING = 2; /** * Version before Material Registry Rework. *

- * Versions before this need to be remapped. Meta Blocks are remapped based on missing registry event, as they have 'baseID's + * Versions before this need to have their material items be remapped. Meta Blocks are remapped based on missing registry event, as they have 'baseID's * (thus, any new GT Addons must be checked to make sure they do not have IDs in GregTech's Registry above 32000) *

* Meta Items are mapped below. @@ -59,7 +80,7 @@ public class LabsFixes { public static final int PRE_MATERIAL_REWORK = 1; /** - * Default version, used if save doesn't have a fix version, or if something goes wrong. + * Default version, used if save doesn't have a fix version and nomi-labs was loaded in the world before, or if something goes wrong. */ public static final int DEFAULT = 0; @@ -80,6 +101,7 @@ public class LabsFixes { public static Map multiblockMetaRemap; public static Set specialMetaItemsRemap; public static Set materialNames; + public static Map capacitorSpecificationRemap; public static void init() { helperMapsInit(); @@ -135,8 +157,8 @@ public static void init() { new DataFix.ItemFix("XU2 Frequency Removal", "Removes Frequency from XU2 Ingredients.", false, - (version) -> version <= DEFAULT, - (modList) -> true, + (version) -> version <= DEFAULT || version == NEW, + (modList) -> modList.containsKey(XU2_MODID), (stack) -> stack.rl.equals(new ResourceLocation(XU2_MODID, "ingredients")) && stack.tag != null && stack.tag.hasKey("Freq"), (stack) -> { @@ -182,6 +204,20 @@ public static void init() { .setRl(LabsNames.makeLabsName(stack.rl.getPath()))) ); + if (Loader.isModLoaded(LabsValues.ENDER_IO_MODID)) + itemFixes.add( + new DataFix.ItemFix("Custom Capacitor NBT Removal", + "Removes NBT from Custom Capacitors.", + false, + (version) -> version <= PRE_CAPACITOR_REMAPPING, + (modList) -> true, + (stack) -> (stack.rl.getNamespace().equals(LABS_MODID) || + stack.rl.getNamespace().equals(CONTENTTWEAKER_MODID)) && + capacitorSpecificationRemap.containsKey(stack.rl.getPath()) && + capacitorSpecificationRemap.get(stack.rl.getPath()).needChange(stack.tag), + (stack) -> stack.setTag(capacitorSpecificationRemap.get(stack.rl.getPath()).remove(stack.tag))) + ); + /* * Block Fixes. * Note that this is not applied on any new blocks, placed or generated. @@ -373,5 +409,85 @@ private static void helperMapsInit() { multiblockMetaIdRemap.put( new ResourceLocation(MULTIBLOCK_TWEAKER_MODID, "dml_sim_chamber"), makeLabsName("dme_sim_chamber") ); + + capacitorSpecificationRemap = ImmutableMap.of( + "compressedoctadiccapacitor", + new OldCapacitorSpecification(4f, "Compressed Octadic RF Capacitor", + "This is what is known as a Compressed Octadic Capacitor.", + "Or, you could just call this an Octadic Capacitor Two.", + "Can be inserted into EnderIO machines.", + "Level: 4"), + + "doublecompressedoctadiccapacitor", + new OldCapacitorSpecification(5f, "Double Compressed Octadic RF Capacitor", + "AND THIS IS TO GO EVEN FURTHER BEYOND!", + "Can be inserted into EnderIO machines.", + "Level: 9.001", + "Just kidding, it's only 5.") + ); + } + + public static class OldCapacitorSpecification { + private static final String EIO_KEY = "eiocap"; + private static final String DISPLAY_KEY = "display"; + private static final String NAME_KEY = "Name"; + private static final String LORE_KEY = "Lore"; + private final float level; + private final String name; + private final String[] lore; + + private OldCapacitorSpecification(float level, String name, String... lore) { + this.level = level; + this.name = name; + this.lore = lore; + } + + public boolean needChange(@Nullable NBTTagCompound compound) { + if (compound == null || compound.isEmpty()) return false; + return testEIO(compound) || testName(compound) || testLore(compound); + } + + @Nullable + public NBTTagCompound remove(@Nullable NBTTagCompound compound) { + if (compound == null || compound.isEmpty()) return null; + if (testEIO(compound)) compound.removeTag(EIO_KEY); + removeDisplay(compound); + return compound.isEmpty() ? null : compound; + } + + private boolean testEIO(NBTTagCompound compound) { + if (!compound.hasKey(EIO_KEY, Constants.NBT.TAG_FLOAT)) return false; + return compound.getFloat(EIO_KEY) == level; + } + + private void removeDisplay(NBTTagCompound compound) { + if (!compound.hasKey(DISPLAY_KEY, Constants.NBT.TAG_COMPOUND)) return; + var display = compound.getCompoundTag(DISPLAY_KEY); + if (testName(compound)) display.removeTag(NAME_KEY); + if (testLore(compound)) display.removeTag(LORE_KEY); + + if (display.isEmpty()) compound.removeTag(DISPLAY_KEY); + else compound.setTag(DISPLAY_KEY, display); + } + + private boolean testName(NBTTagCompound compound) { + if (!compound.hasKey(DISPLAY_KEY, Constants.NBT.TAG_COMPOUND)) return false; + var display = compound.getCompoundTag(DISPLAY_KEY); + if (!display.hasKey(NAME_KEY, Constants.NBT.TAG_STRING)) return false; + return display.getString(NAME_KEY).equals(name); + } + + private boolean testLore(NBTTagCompound compound) { + if (!compound.hasKey(DISPLAY_KEY, Constants.NBT.TAG_COMPOUND)) return false; + var display = compound.getCompoundTag(DISPLAY_KEY); + if (!display.hasKey(LORE_KEY, Constants.NBT.TAG_LIST)) return false; + var lore = display.getTagList(LORE_KEY, Constants.NBT.TAG_STRING); + for (int i = 0; i < lore.tagList.size(); i++) { + var tagStr = (NBTTagString) lore.tagList.get(i); + var str = tagStr.getString(); + if (!str.equals(this.lore[i])) return false; + } + return true; + } } } diff --git a/src/main/java/com/nomiceu/nomilabs/remap/datafixer/LabsWorldFixData.java b/src/main/java/com/nomiceu/nomilabs/remap/datafixer/LabsWorldFixData.java index 9ea55b7a..506df5d2 100644 --- a/src/main/java/com/nomiceu/nomilabs/remap/datafixer/LabsWorldFixData.java +++ b/src/main/java/com/nomiceu/nomilabs/remap/datafixer/LabsWorldFixData.java @@ -1,5 +1,6 @@ package com.nomiceu.nomilabs.remap.datafixer; +import com.nomiceu.nomilabs.LabsValues; import com.nomiceu.nomilabs.NomiLabs; import com.nomiceu.nomilabs.config.LabsConfig; import net.minecraft.nbt.CompressedStreamTools; @@ -12,6 +13,7 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; +import java.util.Map; public class LabsWorldFixData extends WorldSavedData { // Fix Version stored in this world. @@ -29,6 +31,11 @@ public LabsWorldFixData() { else savedVersion = LabsFixes.DEFAULT; } + public void processModList(Map mods) { + if (!mods.containsKey(LabsValues.LABS_MODID) && !LabsConfig.advanced.enableNomiCEuDataFixes) + savedVersion = LabsFixes.NEW; + } + @Override public void readFromNBT(NBTTagCompound nbt) { if (nbt.hasKey(VERSION_KEY, Constants.NBT.TAG_ANY_NUMERIC)) { diff --git a/src/main/java/com/nomiceu/nomilabs/util/LabsGroovyHelper.java b/src/main/java/com/nomiceu/nomilabs/util/LabsGroovyHelper.java new file mode 100644 index 00000000..e431c8e1 --- /dev/null +++ b/src/main/java/com/nomiceu/nomilabs/util/LabsGroovyHelper.java @@ -0,0 +1,9 @@ +package com.nomiceu.nomilabs.util; + +import com.cleanroommc.groovyscript.GroovyScript; + +public class LabsGroovyHelper { + public static boolean isRunningGroovyScripts() { + return GroovyScript.getSandbox().isRunning(); + } +} diff --git a/src/main/resources/assets/nomilabs/lang/en_us.lang b/src/main/resources/assets/nomilabs/lang/en_us.lang index 542ddf10..fb4672b5 100644 --- a/src/main/resources/assets/nomilabs/lang/en_us.lang +++ b/src/main/resources/assets/nomilabs/lang/en_us.lang @@ -10,8 +10,10 @@ nomilabs.subtitle.tick.microverse=Projecting Microverse # Config config.nomilabs.content=Content Settings +config.nomilabs.content.tooltip=Content Settings config.nomilabs.content.custom_content=Custom Content Settings +config.nomilabs.content.custom_content.tooltip=Custom Content Settings config.nomilabs.content.custom_content.items=Enable Custom Items config.nomilabs.content.custom_content.blocks=Enable Custom Blocks config.nomilabs.content.custom_content.fluids=Enable Custom Fluids @@ -19,6 +21,7 @@ config.nomilabs.content.custom_content.complex_recipes=Enable Complex Recipes config.nomilabs.content.custom_content.remap=Remap Old Content Tweaker Content config.nomilabs.content.gt_content=Custom GT Content Settings +config.nomilabs.content.gt_content.tooltip=Custom GT Content Settings config.nomilabs.content.gt_content.materials=Enable Custom GT Materials config.nomilabs.content.gt_content.perfect_gems=Enable Perfect Gems config.nomilabs.content.gt_content.remap_perfect_gems=Remap Old Perfect Gems @@ -27,20 +30,27 @@ config.nomilabs.content.gt_content.old_multiblocks=Enable Old Multiblocks config.nomilabs.content.gt_content.new_multiblocks=Enable New Multiblocks config.nomilabs.mod_integration=Mod Integration Settings +config.nomilabs.mod_integration.tooltip=Mod Integration Settings config.nomilabs.mod_integration.nuclearcraft=Enable NuclearCraft Integration config.nomilabs.mod_integration.xu2=Enable Extra Utilities 2 Integration config.nomilabs.mod_integration.top=Enable The One Probe Integration config.nomilabs.mod_integration.advanced_rocketry=Enable Advanced Rocketry Integration config.nomilabs.mod_integration.ender_storage=Enable Ender Storage Integration config.nomilabs.mod_integration.ender_io=Enable Ender IO Integration -config.nomilabs.mod_integration.groovy_hand=Enable GroovyScript Hand Additions + +config.nomilabs.groovy=GroovyScript Extensions and Script Helper Settings +config.nomilabs.groovy.tooltip=GroovyScript Extensions and Script Helper Settings +config.nomilabs.groovy.hand=Enable GroovyScript Hand Additions +config.nomilabs.groovy.recipe_search_mode=GregTech Recipe Output Search Mode config.nomilabs.mod_integration.draconicevolution=Draconic Evolution Integration Settings +config.nomilabs.mod_integration.draconicevolution.tooltip=Draconic Evolution Integration Settings config.nomilabs.mod_integration.draconicevolution.enable=Enable Draconic Evolution Integration config.nomilabs.mod_integration.draconicevolution.auto_builder_speed=Auto Builder Speed config.nomilabs.mod_integration.draconicevolution.auto_destructor_speed=Auto Destructor Speed config.nomilabs.advanced=Advanced Settings +config.nomilabs.advanced.tooltip=Advanced Settings config.nomilabs.advanced.allow_other_modes=Allow Other Modes config.nomilabs.advanced.disable_xp_scaling=Disable Anvil XP Scaling config.nomilabs.advanced.disable_advancements=Disable Advancements @@ -52,9 +62,14 @@ config.nomilabs.advanced.ignore_entities=ENTITY Missing Registry Ignore List config.nomilabs.advanced.ignore_biomes=BIOME Missing Registry Ignore List config.nomilabs.advanced.fluid_registry=Fluid Registry Settings +config.nomilabs.advanced.fluid_registry.tooltip=Fluid Registry Settings config.nomilabs.advanced.fluid_registry.default_fluids=Default Fluids config.nomilabs.advanced.fluid_registry.log_conflicting_fluids=Log Conflicting Fluids +# GUI +gui.nomilabs.recipes.tooltip.ct_recipe=Recipe is from CraftTweaker. +gui.nomilabs.recipes.tooltip.gs_recipe=Recipe is from GroovyScript. + # Custom Fluids fluid.uranium233=Uranium 233 fluid.plutonium2=Plutonium diff --git a/src/main/resources/mixins.nomilabs.gregtech.json b/src/main/resources/mixins.nomilabs.gregtech.json index 1a3dd876..45706f42 100644 --- a/src/main/resources/mixins.nomilabs.gregtech.json +++ b/src/main/resources/mixins.nomilabs.gregtech.json @@ -8,10 +8,13 @@ "AccessibleDecompositionRecipeHandler", "AccessibleMaterialInfo", "FluidStorageKeyMixin", + "GTRecipeWrapperMixin", + "MaterialFlagsMixin", "MaterialMixin", "MaterialStackMixin", "MetaItemsMixin", - "RecipeMapMixin" + "RecipeMapMixin", + "VirtualizedRecipeMapMixin" ], "client": [], "server": []