aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/gregtech/api/util/GT_RecipeBuilder.java
diff options
context:
space:
mode:
authormiozune <miozune@gmail.com>2023-12-04 05:34:27 +0900
committerGitHub <noreply@github.com>2023-12-03 21:34:27 +0100
commitf74c7cc297d1d19d38a19683cd277ad9ce605d3a (patch)
treeb2a5d66ec5a959099240fb1db239ffc0f9531839 /src/main/java/gregtech/api/util/GT_RecipeBuilder.java
parentb08cde7de4ec93cba05fb070991ad1dffb800ce1 (diff)
downloadGT5-Unofficial-f74c7cc297d1d19d38a19683cd277ad9ce605d3a.tar.gz
GT5-Unofficial-f74c7cc297d1d19d38a19683cd277ad9ce605d3a.tar.bz2
GT5-Unofficial-f74c7cc297d1d19d38a19683cd277ad9ce605d3a.zip
Refactor RecipeMap (#2345)
* Remove deprecated and unused things * Move recipemap subclasses * Move GT_Recipe_Map to outside and rename to RecipeMap * Move recipemap instances to separated class & remove prepending s * Remove useless GT_Recipe constructors * Always use ModularUI * Rename IGT_RecipeMap -> IRecipeMap * Add RecipeMapBuilder * Remove more deprecated and unused things * Fix RecipeMap type parameters * Use multimap for recipe index * Fix bending recipe error in dev env * Remove mUniqueIdentifier * Update AE2FC * Less edgy texture for NEI recipe background * Add replicator fluid output slot for NEI and machine GUI * Fix fluid fuels not having fuel value in large boilers * Remove GT_RectHandler and NEI_TransferRectHost * Remove RecipeMapHandler * Move NEI energy description from RecipeMapFrontend to Power * Refactor the way to filter fusion recipes * Check restriction for some properties * Remove showVoltageAmperage * Make Power accept GT_Recipe * Fix NPE * Move NEI duration description to Power from Frontend * Directly implement IRecipeProcessingAwareHatch for GT_MetaTileEntity_Hatch_InputBus_ME * Make Power integrated with GT_OverclockCalculator * Rename Power -> OverclockDescriber * Don't modify recipe find logic until postload finishes * Reformat reserved MTE ids * Fix check for too few inputs on recipe addition * Move replicator logic to backend * Stop un-hiding assline recipes * Allow setting custom recipe comparator & implement for fusion * Update AE2FC * Rename getRecipeList and getRecipes -> getRecipeMap * Automatically register recipe catalysts * Cleanup the way to detect recipe collision * Make use of BasicUIProperties for basic machines * Make use of BasicUIProperties for UIHelper * Rename specialHandler -> recipeTransformer * Add way to automatically register handler info * Add recipe category * Add some APIs for addons * Rename blastRecipes -> blastFurnaceRecipes * Remove GT_MetaTileEntity_BasicMachine_GT_Recipe#mSharedTank and #mRequiresFluidForFiltering * Don't require setting duration and EU/t for fuel recipes * Don't require setting EU/t for primitive blast furnace recipes * Revert change to addMultiblockChemicalRecipe * Fix large boiler general desc recipe not being added * Hide duration and EU/t from large boiler * Cleanup recipe stacktrace draw * Extend metadata usage of recipe builder to recipe itself * Implement metadata handling & NEI comparator for PCB factory * Some rename around NEIRecipeInfo * Some toString implementations * Add more APIs for addons & some rename * Infer handler icon from recipe catalyst if one is not set * Also shrink recipe title when OC is not used * Remove rare earth centrifuge recipe * Use metadata for replicator backend * Adjust geothermal generator output slot * Allow having multiple transferrects * Store recipemap reference in backend * Rename vacuumRecipes -> vacuumFreezerRecipes * Add config to tweak visibility of recipe categories * Remove mHideRecyclingRecipes in favor of recipe category config * Fix typo fluidSolidfierRecipes -> fluidSolidifierRecipes * Refactor findRecipe and ProcessingLogic to use Stream * Fix BBF handler icon & remove bronze blast furnace * Add fluent API for findRecipe * Add way to stop adding progressbar * Change arg order for special texture * Avoid overwriting interesting failure with NO_RECIPE * Some changes for FuelBackend * Set space project icon * Remove localization from TT * Remove CNC recipe adder * Move recipe extractor from AE2FC * Minor internal change for ProcessingLogic#applyRecipe * More javadoc on #getAvailableRecipeMaps * Better implementation of #ofSupplier * Move replicator exponent config to GT_Proxy * Remove RC & IC2 macerator handling * Rename StreamUtil -> GT_StreamUtil * Refactor code around RecipeMetadataStorage * Revise #compileRecipe javadoc * Switch extreme diesel recipe loader to downstream recipe map * Optimize #reMap * Rename reload -> reloadNEICache * Minor tweak for drawEnergyInfo * a bit more doc * Adjust recipe catalysts * Add toString implementation for GT_Fluid for debug * Minor revision for OilCrackerBackend * Index replicator recipes by material --------- Co-authored-by: Glease <4586901+Glease@users.noreply.github.com>
Diffstat (limited to 'src/main/java/gregtech/api/util/GT_RecipeBuilder.java')
-rw-r--r--src/main/java/gregtech/api/util/GT_RecipeBuilder.java257
1 files changed, 146 insertions, 111 deletions
diff --git a/src/main/java/gregtech/api/util/GT_RecipeBuilder.java b/src/main/java/gregtech/api/util/GT_RecipeBuilder.java
index 3fa8d91da0..271ea28d87 100644
--- a/src/main/java/gregtech/api/util/GT_RecipeBuilder.java
+++ b/src/main/java/gregtech/api/util/GT_RecipeBuilder.java
@@ -1,30 +1,34 @@
package gregtech.api.util;
+import static gregtech.api.util.GT_RecipeMapUtil.SPECIAL_VALUE_ALIASES;
import static gregtech.api.util.GT_Utility.copyFluidArray;
import static gregtech.api.util.GT_Utility.copyItemArray;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
import java.util.Objects;
import java.util.Optional;
-import java.util.function.Function;
import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
import net.minecraft.item.ItemStack;
import net.minecraft.launchwrapper.Launch;
import net.minecraftforge.fluids.FluidStack;
+import org.jetbrains.annotations.Contract;
+
import gregtech.GT_Mod;
-import gregtech.api.interfaces.IGT_RecipeMap;
+import gregtech.api.interfaces.IRecipeMap;
+import gregtech.api.recipe.RecipeCategory;
+import gregtech.api.recipe.RecipeMetadataKey;
+import gregtech.api.recipe.metadata.IRecipeMetadataStorage;
+import gregtech.api.recipe.metadata.RecipeMetadataStorage;
import gregtech.api.util.extensions.ArrayExt;
-@SuppressWarnings("unused")
+@SuppressWarnings({ "unused", "UnusedReturnValue" })
public class GT_RecipeBuilder {
// debug mode expose problems. panic mode help you check nothing is wrong-ish without you actively monitoring
@@ -86,9 +90,13 @@ public class GT_RecipeBuilder {
protected boolean fakeRecipe = false;
protected boolean mCanBeBuffered = true;
protected boolean mNeedsEmptyOutput = false;
+ protected boolean nbtSensitive = false;
protected String[] neiDesc;
+ protected RecipeCategory recipeCategory;
protected boolean optimize = true;
- protected Map<MetadataIdentifier<?>, Object> additionalData = new HashMap<>();
+ @Nullable
+ protected IRecipeMetadataStorage metadataStorage;
+ protected boolean checkForCollision = true;
protected boolean valid = true;
GT_RecipeBuilder() {}
@@ -96,8 +104,8 @@ public class GT_RecipeBuilder {
private GT_RecipeBuilder(ItemStack[] inputsBasic, Object[] inputsOreDict, ItemStack[] outputs, ItemStack[][] alts,
FluidStack[] fluidInputs, FluidStack[] fluidOutputs, int[] chances, Object special, int duration, int eut,
int specialValue, boolean enabled, boolean hidden, boolean fakeRecipe, boolean mCanBeBuffered,
- boolean mNeedsEmptyOutput, String[] neiDesc, boolean optimize,
- Map<MetadataIdentifier<?>, Object> additionalData, boolean valid) {
+ boolean mNeedsEmptyOutput, boolean nbtSensitive, String[] neiDesc, RecipeCategory recipeCategory,
+ boolean optimize, @Nullable IRecipeMetadataStorage metadataStorage, boolean checkForCollision, boolean valid) {
this.inputsBasic = inputsBasic;
this.inputsOreDict = inputsOreDict;
this.outputs = outputs;
@@ -114,9 +122,15 @@ public class GT_RecipeBuilder {
this.fakeRecipe = fakeRecipe;
this.mCanBeBuffered = mCanBeBuffered;
this.mNeedsEmptyOutput = mNeedsEmptyOutput;
+ this.nbtSensitive = nbtSensitive;
this.neiDesc = neiDesc;
+ this.recipeCategory = recipeCategory;
this.optimize = optimize;
- this.additionalData.putAll(additionalData);
+ this.metadataStorage = metadataStorage;
+ if (this.metadataStorage != null) {
+ this.metadataStorage = this.metadataStorage.copy();
+ }
+ this.checkForCollision = checkForCollision;
this.valid = valid;
}
@@ -137,6 +151,14 @@ public class GT_RecipeBuilder {
return new GT_RecipeBuilder();
}
+ /**
+ * Creates empty builder where only duration and EU/t are set to 0.
+ */
+ public static GT_RecipeBuilder empty() {
+ return new GT_RecipeBuilder().duration(0)
+ .eut(0);
+ }
+
private static boolean containsNull(Object[] arr) {
return arr == null || Arrays.stream(arr)
.anyMatch(Objects::isNull);
@@ -156,7 +178,7 @@ public class GT_RecipeBuilder {
return DEBUG_MODE_NULL || PANIC_MODE_NULL;
}
- private static void handleInvalidRecipe() {
+ public static void handleInvalidRecipe() {
if (!DEBUG_MODE_INVALID && !PANIC_MODE_INVALID) {
return;
}
@@ -247,14 +269,6 @@ public class GT_RecipeBuilder {
return noOptimize();
}
- /**
- * @deprecated You don't need to call this method, RecipeBuilder now takes empty item input array by default.
- */
- @Deprecated
- public GT_RecipeBuilder noItemInputs() {
- return this;
- }
-
public GT_RecipeBuilder itemOutputs(ItemStack... outputs) {
if (debugNull() && containsNull(outputs)) handleNullRecipeComponents("itemOutputs");
this.outputs = outputs;
@@ -278,50 +292,18 @@ public class GT_RecipeBuilder {
return this;
}
- /**
- * @deprecated You don't need to call this method, RecipeBuilder now takes empty item output array by default.
- */
- @Deprecated
- public GT_RecipeBuilder noItemOutputs() {
- return this;
- }
-
public GT_RecipeBuilder fluidInputs(FluidStack... fluidInputs) {
if (debugNull() && containsNull(fluidInputs)) handleNullRecipeComponents("fluidInputs");
this.fluidInputs = fix(fluidInputs);
return this;
}
- /**
- * @deprecated You don't need to call this method, RecipeBuilder now takes empty fluid input array by default.
- */
- @Deprecated
- public GT_RecipeBuilder noFluidInputs() {
- return this;
- }
-
public GT_RecipeBuilder fluidOutputs(FluidStack... fluidOutputs) {
if (debugNull() && containsNull(fluidOutputs)) handleNullRecipeComponents("fluidOutputs");
this.fluidOutputs = fix(fluidOutputs);
return this;
}
- /**
- * @deprecated You don't need to call this method, RecipeBuilder now takes empty fluid output array by default.
- */
- @Deprecated
- public GT_RecipeBuilder noFluidOutputs() {
- return this;
- }
-
- /**
- * @deprecated You don't need to call this method, RecipeBuilder now takes empty arrays by default.
- */
- @Deprecated
- public GT_RecipeBuilder noOutputs() {
- return this;
- }
-
public GT_RecipeBuilder outputChances(int... chances) {
if (outputs != null && chances.length != outputs.length) {
throw new IllegalArgumentException("Output chances array and items array length differs");
@@ -398,11 +380,21 @@ public class GT_RecipeBuilder {
return this;
}
+ public GT_RecipeBuilder nbtSensitive() {
+ this.nbtSensitive = true;
+ return this;
+ }
+
public GT_RecipeBuilder setNEIDesc(String... neiDesc) {
this.neiDesc = neiDesc;
return this;
}
+ public GT_RecipeBuilder recipeCategory(RecipeCategory recipeCategory) {
+ this.recipeCategory = recipeCategory;
+ return this;
+ }
+
/**
* Prevent the resulting recipe from optimizing recipe, which is a process that reduce recipe batch size.
*/
@@ -411,17 +403,51 @@ public class GT_RecipeBuilder {
return this;
}
- public <T> GT_RecipeBuilder metadata(MetadataIdentifier<T> key, T value) {
- additionalData.put(key, value);
+ /**
+ * Prevents checking collision with existing recipes when adding the built recipe.
+ */
+ public GT_RecipeBuilder ignoreCollision() {
+ this.checkForCollision = false;
return this;
}
- public <T> T getMetadata(MetadataIdentifier<T> key) {
- return key.cast(additionalData.get(key));
+ /**
+ * Sets metadata of the recipe. It can be used for recipe emitter to do special things, or for being stored in the
+ * built recipe and used for actual recipe processing.
+ * <p>
+ * {@link GT_RecipeConstants} has a series of metadata keys. Or you can create one by yourself.
+ */
+ public <T> GT_RecipeBuilder metadata(RecipeMetadataKey<T> key, T value) {
+ if (metadataStorage == null) {
+ metadataStorage = new RecipeMetadataStorage();
+ }
+ metadataStorage.store(key, value);
+ return this;
+ }
+
+ /**
+ * Gets metadata already set for this builder. Can return null. Use
+ * {@link #getMetadataOrDefault(RecipeMetadataKey, Object)}
+ * if you want to specify default value.
+ */
+ @Nullable
+ public <T> T getMetadata(RecipeMetadataKey<T> key) {
+ if (metadataStorage == null) {
+ return null;
+ }
+ return key.cast(metadataStorage.getMetadata(key));
}
- public <T> T getMetadata(MetadataIdentifier<T> key, T defaultValue) {
- return key.cast(additionalData.getOrDefault(key, defaultValue));
+ /**
+ * Gets metadata already set for this builder with default value. Does not return null unless default value is null.
+ */
+ @Contract("_, !null -> !null")
+ @Nullable
+ public <T> T getMetadataOrDefault(RecipeMetadataKey<T> key, T defaultValue) {
+ if (metadataStorage == null) {
+ return defaultValue;
+ }
+ return key.cast(metadataStorage.getMetadataOrDefault(key, defaultValue));
}
public GT_RecipeBuilder requiresCleanRoom() {
@@ -466,9 +492,12 @@ public class GT_RecipeBuilder {
fakeRecipe,
mCanBeBuffered,
mNeedsEmptyOutput,
+ nbtSensitive,
copy(neiDesc),
+ recipeCategory,
optimize,
- additionalData,
+ metadataStorage,
+ checkForCollision,
valid);
}
@@ -493,9 +522,12 @@ public class GT_RecipeBuilder {
fakeRecipe,
mCanBeBuffered,
mNeedsEmptyOutput,
+ nbtSensitive,
copy(neiDesc),
+ recipeCategory,
optimize,
- Collections.emptyMap(),
+ null,
+ checkForCollision,
valid);
}
@@ -553,6 +585,18 @@ public class GT_RecipeBuilder {
return eut;
}
+ public RecipeCategory getRecipeCategory() {
+ return recipeCategory;
+ }
+
+ public boolean isOptimize() {
+ return optimize;
+ }
+
+ public boolean isCheckForCollision() {
+ return checkForCollision;
+ }
+
// endregion
// region validator
@@ -581,7 +625,7 @@ public class GT_RecipeBuilder {
/**
* Validate if input item match requirement. Return as invalidated if fails prereq. Specify -1 as min to allow
- * unset. Both bound inclusive. Only supposed to be called by IGT_RecipeMap and not client code.
+ * unset. Both bound inclusive. Only supposed to be called by IRecipeMap and not client code.
*/
public GT_RecipeBuilder validateNoInput() {
return GT_Utility.isArrayEmptyOrNull(inputsBasic) ? this : invalidate();
@@ -589,7 +633,7 @@ public class GT_RecipeBuilder {
/**
* Validate if input fluid match requirement. Return as invalidated if fails prereq. Specify -1 as min to allow
- * unset. Both bound inclusive. Only supposed to be called by IGT_RecipeMap and not client code.
+ * unset. Both bound inclusive. Only supposed to be called by IRecipeMap and not client code.
*/
public GT_RecipeBuilder validateNoInputFluid() {
return GT_Utility.isArrayEmptyOrNull(fluidInputs) ? this : invalidate();
@@ -597,7 +641,7 @@ public class GT_RecipeBuilder {
/**
* Validate if output item match requirement. Return as invalidated if fails prereq. Specify -1 as min to allow
- * unset. Both bound inclusive. Only supposed to be called by IGT_RecipeMap and not client code.
+ * unset. Both bound inclusive. Only supposed to be called by IRecipeMap and not client code.
*/
public GT_RecipeBuilder validateNoOutput() {
return GT_Utility.isArrayEmptyOrNull(outputs) ? this : invalidate();
@@ -605,7 +649,7 @@ public class GT_RecipeBuilder {
/**
* Validate if output fluid match requirement. Return as invalidated if fails prereq. Specify -1 as min to allow
- * unset. Both bound inclusive. Only supposed to be called by IGT_RecipeMap and not client code.
+ * unset. Both bound inclusive. Only supposed to be called by IRecipeMap and not client code.
*/
public GT_RecipeBuilder validateNoOutputFluid() {
return GT_Utility.isArrayEmptyOrNull(fluidOutputs) ? this : invalidate();
@@ -613,7 +657,7 @@ public class GT_RecipeBuilder {
/**
* Validate if input item match requirement. Return as invalidated if fails prereq. Specify -1 as min to allow
- * unset. Both bound inclusive. Only supposed to be called by IGT_RecipeMap and not client code.
+ * unset. Both bound inclusive. Only supposed to be called by IRecipeMap and not client code.
*/
public GT_RecipeBuilder validateInputCount(int min, int max) {
if (inputsBasic == null) return min < 0 ? this : invalidate();
@@ -622,7 +666,7 @@ public class GT_RecipeBuilder {
/**
* Validate if input fluid match requirement. Return as invalidated if fails prereq. Specify -1 as min to allow
- * unset. Both bound inclusive. Only supposed to be called by IGT_RecipeMap and not client code.
+ * unset. Both bound inclusive. Only supposed to be called by IRecipeMap and not client code.
*/
public GT_RecipeBuilder validateInputFluidCount(int min, int max) {
if (fluidInputs == null) return min < 0 ? this : invalidate();
@@ -631,7 +675,7 @@ public class GT_RecipeBuilder {
/**
* Validate if output item match requirement. Return as invalidated if fails prereq. Specify -1 as min to allow
- * unset. Both bound inclusive. Only supposed to be called by IGT_RecipeMap and not client code.
+ * unset. Both bound inclusive. Only supposed to be called by IRecipeMap and not client code.
*/
public GT_RecipeBuilder validateOutputCount(int min, int max) {
if (outputs == null) return min < 0 ? this : invalidate();
@@ -640,7 +684,7 @@ public class GT_RecipeBuilder {
/**
* Validate if output fluid match requirement. Return as invalidated if fails prereq. Specify -1 as min to allow
- * unset. Both bound inclusive. Only supposed to be called by IGT_RecipeMap and not client code.
+ * unset. Both bound inclusive. Only supposed to be called by IRecipeMap and not client code.
*/
public GT_RecipeBuilder validateOutputFluidCount(int min, int max) {
if (fluidOutputs == null) return min < 0 ? this : invalidate();
@@ -669,6 +713,12 @@ public class GT_RecipeBuilder {
// endregion
+ /**
+ * Builds new recipe, without custom behavior of recipemaps. For adding recipe to recipemap,
+ * use {@link #addTo} instead.
+ *
+ * @return Built recipe. Returns empty if failed to build.
+ */
public Optional<GT_Recipe> build() {
if (!valid) {
handleInvalidRecipe();
@@ -693,7 +743,10 @@ public class GT_RecipeBuilder {
fakeRecipe,
mCanBeBuffered,
mNeedsEmptyOutput,
- neiDesc)));
+ nbtSensitive,
+ neiDesc,
+ metadataStorage,
+ recipeCategory)));
}
public GT_RecipeBuilder forceOreDictInput() {
@@ -728,7 +781,10 @@ public class GT_RecipeBuilder {
fakeRecipe,
mCanBeBuffered,
mNeedsEmptyOutput,
+ nbtSensitive,
neiDesc,
+ metadataStorage,
+ recipeCategory,
alts)));
}
@@ -769,18 +825,36 @@ public class GT_RecipeBuilder {
r.mHidden = hidden;
r.mCanBeBuffered = mCanBeBuffered;
r.mNeedsEmptyOutput = mNeedsEmptyOutput;
+ r.isNBTSensitive = nbtSensitive;
r.mFakeRecipe = fakeRecipe;
r.mEnabled = enabled;
if (neiDesc != null) r.setNeiDesc(neiDesc);
+ applyDefaultSpecialValues(r);
return r;
}
- public Collection<GT_Recipe> addTo(IGT_RecipeMap recipeMap) {
+ private void applyDefaultSpecialValues(GT_Recipe recipe) {
+ if (recipe.mSpecialValue != 0) return;
+
+ int specialValue = 0;
+ if (getMetadataOrDefault(GT_RecipeConstants.LOW_GRAVITY, false)) specialValue -= 100;
+ if (getMetadataOrDefault(GT_RecipeConstants.CLEANROOM, false)) specialValue -= 200;
+ for (RecipeMetadataKey<Integer> ident : SPECIAL_VALUE_ALIASES) {
+ Integer metadata = getMetadataOrDefault(ident, null);
+ if (metadata != null) {
+ specialValue = metadata;
+ break;
+ }
+ }
+ recipe.mSpecialValue = specialValue;
+ }
+
+ public Collection<GT_Recipe> addTo(IRecipeMap recipeMap) {
return recipeMap.doAdd(this);
}
public GT_RecipeBuilder reset() {
- additionalData.clear();
+ metadataStorage = null;
alts = null;
chances = null;
duration = -1;
@@ -794,7 +868,9 @@ public class GT_RecipeBuilder {
inputsOreDict = null;
mCanBeBuffered = true;
mNeedsEmptyOutput = false;
+ nbtSensitive = false;
neiDesc = null;
+ recipeCategory = null;
optimize = true;
outputs = null;
special = null;
@@ -802,45 +878,4 @@ public class GT_RecipeBuilder {
valid = true;
return this;
}
-
- public final static class MetadataIdentifier<T> {
-
- private static final Map<MetadataIdentifier<?>, MetadataIdentifier<?>> allIdentifiers = Collections
- .synchronizedMap(new HashMap<>());
- private final Class<T> clazz;
- private final String identifier;
-
- private MetadataIdentifier(Class<T> clazz, String identifier) {
- this.clazz = clazz;
- this.identifier = identifier;
- }
-
- public static <T> MetadataIdentifier<T> create(Class<T> clazz, String identifier) {
- MetadataIdentifier<T> key = new MetadataIdentifier<>(clazz, identifier);
- // noinspection unchecked // The class uses type T to fill allIdentifiers
- return (MetadataIdentifier<T>) allIdentifiers.computeIfAbsent(key, Function.identity());
- }
-
- public T cast(Object o) {
- return clazz.cast(o);
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
-
- MetadataIdentifier<?> that = (MetadataIdentifier<?>) o;
-
- if (!clazz.equals(that.clazz)) return false;
- return identifier.equals(that.identifier);
- }
-
- @Override
- public int hashCode() {
- int result = clazz.hashCode();
- result = 31 * result + identifier.hashCode();
- return result;
- }
- }
}