diff options
author | Glease <4586901+Glease@users.noreply.github.com> | 2023-02-27 13:49:05 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-02-27 06:49:05 +0100 |
commit | 44824520ab2140f561a741bc486bc2f3459da2fe (patch) | |
tree | 5d81da726f4cb783a6b2369241a7613a2eeefff6 /src/main/java/gregtech | |
parent | f04ae276af1346fb8ca5a022b21f5f372960ae13 (diff) | |
download | GT5-Unofficial-44824520ab2140f561a741bc486bc2f3459da2fe.tar.gz GT5-Unofficial-44824520ab2140f561a741bc486bc2f3459da2fe.tar.bz2 GT5-Unofficial-44824520ab2140f561a741bc486bc2f3459da2fe.zip |
implement save & load for single recipe lock (#1771)
* implement save & load for single recipe lock
* fix fat finger
* fix NPE
* disable machine if old locked recipe is gone
* address reviews
* spotless
Diffstat (limited to 'src/main/java/gregtech')
7 files changed, 244 insertions, 13 deletions
diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_MultiBlockBase.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_MultiBlockBase.java index 57ec61e19d..7bbfd46b35 100644 --- a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_MultiBlockBase.java +++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_MultiBlockBase.java @@ -20,6 +20,7 @@ import net.minecraft.tileentity.TileEntity; import net.minecraft.util.EnumChatFormatting; import net.minecraft.util.StatCollector; import net.minecraft.world.World; +import net.minecraftforge.common.util.Constants; import net.minecraftforge.common.util.ForgeDirection; import net.minecraftforge.fluids.FluidStack; @@ -195,7 +196,11 @@ public abstract class GT_MetaTileEntity_MultiBlockBase extends MetaTileEntity aNBT.setInteger("mEfficiency", mEfficiency); aNBT.setInteger("mPollution", mPollution); aNBT.setInteger("mRuntime", mRuntime); - aNBT.setBoolean("mLockedToSingleRecipe", mLockedToSingleRecipe); + if (supportsSingleRecipeLocking()) { + aNBT.setBoolean("mLockedToSingleRecipe", mLockedToSingleRecipe); + if (mLockedToSingleRecipe && mSingleRecipeCheck != null) + aNBT.setTag("mSingleRecipeCheck", mSingleRecipeCheck.writeToNBT()); + } if (mOutputItems != null) { aNBT.setInteger("mOutputItemsLength", mOutputItems.length); @@ -232,7 +237,16 @@ public abstract class GT_MetaTileEntity_MultiBlockBase extends MetaTileEntity mEfficiency = aNBT.getInteger("mEfficiency"); mPollution = aNBT.getInteger("mPollution"); mRuntime = aNBT.getInteger("mRuntime"); - mLockedToSingleRecipe = aNBT.getBoolean("mLockedToSingleRecipe"); + if (supportsSingleRecipeLocking()) { + mLockedToSingleRecipe = aNBT.getBoolean("mLockedToSingleRecipe"); + if (mLockedToSingleRecipe && aNBT.hasKey("mSingleRecipeCheck", Constants.NBT.TAG_COMPOUND)) { + GT_Single_Recipe_Check c = loadSingleRecipeChecker(aNBT.getCompoundTag("mSingleRecipeCheck")); + if (c != null) mSingleRecipeCheck = c; + // the old recipe is gone. we disable the machine to prevent making garbage in case of shared inputs + // maybe use a better way to inform player in the future. + else getBaseMetaTileEntity().disableWorking(); + } + } batchMode = aNBT.getBoolean(BATCH_MODE_NBT_KEY); inputSeparation = aNBT.getBoolean(INPUT_SEPARATION_NBT_KEY); voidExcess = aNBT.getBoolean(VOID_EXCESS_NBT_KEY); @@ -259,6 +273,10 @@ public abstract class GT_MetaTileEntity_MultiBlockBase extends MetaTileEntity mCrowbar = aNBT.getBoolean("mCrowbar"); } + protected GT_Single_Recipe_Check loadSingleRecipeChecker(NBTTagCompound aNBT) { + return GT_Single_Recipe_Check.tryLoad(this, aNBT); + } + @Override public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) { GT_UIInfos.openGTTileEntityUI(aBaseMetaTileEntity, aPlayer); diff --git a/src/main/java/gregtech/api/util/GT_Single_Recipe_Check.java b/src/main/java/gregtech/api/util/GT_Single_Recipe_Check.java index fae02badf2..89a7fb371d 100644 --- a/src/main/java/gregtech/api/util/GT_Single_Recipe_Check.java +++ b/src/main/java/gregtech/api/util/GT_Single_Recipe_Check.java @@ -1,15 +1,27 @@ package gregtech.api.util; +import java.util.Arrays; +import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.function.Function; + +import javax.annotation.Nullable; import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTBase; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraftforge.common.util.Constants; import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidRegistry; import net.minecraftforge.fluids.FluidStack; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; + +import gregtech.api.enums.GT_Values; import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_MultiBlockBase; /** Used by machines that are locked to a single recipe, for fast computation. */ @@ -171,6 +183,115 @@ public class GT_Single_Recipe_Check { return true; } + public NBTTagCompound writeToNBT() { + // here we encode recipe input, output and all other important values + // at load time we do a recipe check again, so in case the recipe is gone, we can stop tracking + // of course the next step would be auto migrating to new recipe (if any), but given + // we don't yet have a mean to uniquely name a recipe, this will have to make do. + // consider move serialization code to GT_Recipe once this has been proven to work + NBTTagCompound tag = new NBTTagCompound(); + tag.setTag("inputs", writeList(recipe.mInputs, GT_Utility::saveItem)); + tag.setTag("outputs", writeList(recipe.mOutputs, GT_Utility::saveItem)); + tag.setIntArray("chances", recipe.mChances); + tag.setTag( + "fInputs", + writeList( + recipe.mFluidInputs, + s -> s == null ? new NBTTagCompound() : s.writeToNBT(new NBTTagCompound()))); + tag.setTag( + "fOutputs", + writeList( + recipe.mFluidOutputs, + s -> s == null ? new NBTTagCompound() : s.writeToNBT(new NBTTagCompound()))); + tag.setInteger("eut", recipe.mEUt); + tag.setInteger("duration", recipe.mDuration); + tag.setInteger("specialValue", recipe.mSpecialValue); + tag.setTag("itemCost", writeList(itemCost.entrySet(), e -> { + NBTTagCompound ret = new NBTTagCompound(); + ret.setTag("id", e.getKey().writeToNBT()); + ret.setInteger("count", e.getValue()); + return ret; + })); + tag.setTag("fluidCost", writeList(fluidCost.entrySet(), e -> { + NBTTagCompound ret = new NBTTagCompound(); + ret.setString("id", e.getKey().getName()); + ret.setInteger("count", e.getValue()); + return ret; + })); + return tag; + } + + private static <T, NBT extends NBTBase> NBTTagList writeList(T[] arr, Function<T, NBT> ser) { + return writeList(Arrays.asList(arr), ser); + } + + private static <T, NBT extends NBTBase> NBTTagList writeList(Collection<T> arr, Function<T, NBT> ser) { + NBTTagList l = new NBTTagList(); + for (T t : arr) { + l.appendTag(ser.apply(t)); + } + return l; + } + + @Nullable + public static GT_Single_Recipe_Check tryLoad(GT_MetaTileEntity_MultiBlockBase parent, NBTTagCompound tag) { + return tryLoad(parent, parent.getRecipeMap(), tag); + } + + @Nullable + + public static GT_Single_Recipe_Check tryLoad(GT_MetaTileEntity_MultiBlockBase parent, + GT_Recipe.GT_Recipe_Map recipeMap, NBTTagCompound tag) { + GT_Recipe found = tryFindRecipe(parent, recipeMap, tag); + if (found == null) return null; + return new GT_Single_Recipe_Check(parent, found, loadItemCost(tag), loadFluidCost(tag)); + } + + protected static ImmutableMap<Fluid, Integer> loadFluidCost(NBTTagCompound tag) { + return GT_Utility.streamCompounds(tag.getTagList("fluidCost", Constants.NBT.TAG_COMPOUND)).collect( + GT_Utility.toImmutableMapSerial( + t -> FluidRegistry.getFluid(t.getString("id")), + t -> t.getInteger("count"))); + } + + protected static ImmutableMap<GT_Utility.ItemId, Integer> loadItemCost(NBTTagCompound tag) { + return GT_Utility.streamCompounds(tag.getTagList("itemCost", Constants.NBT.TAG_COMPOUND)).collect( + GT_Utility.toImmutableMapSerial( + t -> GT_Utility.ItemId.create(t.getCompoundTag("id")), + t -> t.getInteger("count"))); + } + + protected static GT_Recipe tryFindRecipe(GT_MetaTileEntity_MultiBlockBase parent, GT_Recipe.GT_Recipe_Map recipeMap, + NBTTagCompound tag) { + if (tag == null || tag.hasNoTags()) return null; + ItemStack[] inputs = GT_Utility.streamCompounds(tag.getTagList("inputs", Constants.NBT.TAG_COMPOUND)) + .map(GT_Utility::loadItem).toArray(ItemStack[]::new); + ItemStack[] outputs = GT_Utility.streamCompounds(tag.getTagList("outputs", Constants.NBT.TAG_COMPOUND)) + .map(GT_Utility::loadItem).toArray(ItemStack[]::new); + FluidStack[] fInputs = GT_Utility.streamCompounds(tag.getTagList("fInputs", Constants.NBT.TAG_COMPOUND)) + .map(FluidStack::loadFluidStackFromNBT).toArray(FluidStack[]::new); + FluidStack[] fOutputs = GT_Utility.streamCompounds(tag.getTagList("fOutputs", Constants.NBT.TAG_COMPOUND)) + .map(FluidStack::loadFluidStackFromNBT).toArray(FluidStack[]::new); + int eut = tag.getInteger("eut"); + GT_Recipe found = recipeMap.findRecipe( + parent.getBaseMetaTileEntity(), + false, + GT_Values.V[GT_Utility.getTier(eut)], + fInputs, + inputs); + int[] chances = tag.getIntArray("chances"); + if (found == null || !GT_Utility.equals(inputs, found.mInputs) + || !Arrays.equals(fInputs, found.mFluidInputs) + || !GT_Utility.equals(outputs, found.mOutputs) + || !Arrays.equals(fOutputs, found.mFluidOutputs) + || !Arrays.equals(chances, found.mChances) + || found.mDuration != tag.getInteger("duration") + || found.mEUt != eut + || found.mSpecialValue != tag.getInteger("specialValue")) + return null; + return found; + } + protected static Map<GT_Utility.ItemId, Integer> buildItemMap(GT_MetaTileEntity_MultiBlockBase multiBlockBase) { Map<GT_Utility.ItemId, Integer> itemMap = new HashMap<>(); for (ItemStack itemStack : multiBlockBase.getStoredInputs()) { diff --git a/src/main/java/gregtech/api/util/GT_Single_Recipe_Check_Processing_Array.java b/src/main/java/gregtech/api/util/GT_Single_Recipe_Check_Processing_Array.java index c102d63f48..656c752ea6 100644 --- a/src/main/java/gregtech/api/util/GT_Single_Recipe_Check_Processing_Array.java +++ b/src/main/java/gregtech/api/util/GT_Single_Recipe_Check_Processing_Array.java @@ -5,7 +5,10 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; +import javax.annotation.Nullable; + import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; import net.minecraftforge.fluids.Fluid; import net.minecraftforge.fluids.FluidStack; @@ -131,6 +134,22 @@ public class GT_Single_Recipe_Check_Processing_Array extends GT_Single_Recipe_Ch return finalParallel; } + @Nullable + + public static GT_Single_Recipe_Check tryLoad(GT_MetaTileEntity_MultiBlockBase parent, + GT_Recipe.GT_Recipe_Map recipeMap, NBTTagCompound tag, ItemStack machineStack) { + if (recipeMap == null || machineStack == null) return null; + GT_Recipe found = tryFindRecipe(parent, recipeMap, tag); + if (found == null) return null; + return new GT_Single_Recipe_Check_Processing_Array( + parent, + found, + loadItemCost(tag), + loadFluidCost(tag), + recipeMap.mAmperage, + machineStack.copy()); + } + public static Builder processingArrayBuilder(GT_MetaTileEntity_MultiBlockBase multiBlockBase) { return new Builder(multiBlockBase); } diff --git a/src/main/java/gregtech/api/util/GT_Utility.java b/src/main/java/gregtech/api/util/GT_Utility.java index 2134d94253..577364c0f7 100644 --- a/src/main/java/gregtech/api/util/GT_Utility.java +++ b/src/main/java/gregtech/api/util/GT_Utility.java @@ -24,6 +24,9 @@ import java.util.Map.Entry; import java.util.function.Function; import java.util.function.IntFunction; import java.util.function.Supplier; +import java.util.stream.Collector; +import java.util.stream.IntStream; +import java.util.stream.Stream; import javax.annotation.Nullable; @@ -82,6 +85,7 @@ import cofh.api.transport.IItemDuct; import com.google.auto.value.AutoValue; import com.google.common.base.Suppliers; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Maps; import com.gtnewhorizon.structurelib.alignment.IAlignment; @@ -2421,7 +2425,7 @@ public class GT_Utility { /** * Return texture id from page and index, for use when determining hatches, but can also be precomputed from: * (page<<7)+index - * + * * @param page 0 to 127 page * @param index 0 to 127 texture index * @return casing texture 0 to 16383 @@ -2436,7 +2440,7 @@ public class GT_Utility { /** * Return texture id from page and index, for use when determining hatches, but can also be precomputed from: * (page<<7)+index - * + * * @param page 0 to 127 page * @param startIndex 0 to 127 start texture index * @param blockMeta meta of the block @@ -2459,7 +2463,7 @@ public class GT_Utility { /** * Return texture id from item stack, unoptimized but readable? - * + * * @return casing texture 0 to 16383 */ @Deprecated @@ -2469,7 +2473,7 @@ public class GT_Utility { /** * Return texture id from item stack, unoptimized but readable? - * + * * @return casing texture 0 to 16383 */ public static int getTextureId(Block blockFromBlock, byte metaFromBlock) { @@ -4379,9 +4383,45 @@ public class GT_Utility { return new ItemStack(aItem.getItem(), 0, aItem.getItemDamage()); } + public static Stream<NBTTagCompound> streamCompounds(NBTTagList list) { + if (list == null) return Stream.empty(); + return IntStream.range(0, list.tagCount()).mapToObj(list::getCompoundTagAt); + } + + public static boolean equals(ItemStack[] a, ItemStack[] b) { + // because stupid mojang didn't override equals for us + if (a == null && b == null) return true; + if ((a == null) != (b == null)) return false; + if (a.length != b.length) return false; + for (int i = 0; i < a.length; i++) { + if (!areStacksEqual(a[i], b[i], false)) return false; + } + return true; + } + + /** + * Guava ImmutableMap variant of Collectors.toMap. Optimized for serial streams. + */ + public static <T, K, U> Collector<T, ?, ImmutableMap<K, U>> toImmutableMapSerial( + Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper) { + // petty type inference cannot work out the correct type parameter + return Collector.<T, ImmutableMap.Builder<K, U>, ImmutableMap<K, U>>of( + ImmutableMap::builder, + (b, t) -> b.put(keyMapper.apply(t), valueMapper.apply(t)), + (b1, b2) -> b1.putAll(b2.build()), + ImmutableMap.Builder::build); + } + @AutoValue public abstract static class ItemId { + public static AutoValue_GT_Utility_ItemId create(NBTTagCompound tag) { + return new AutoValue_GT_Utility_ItemId( + Item.getItemById(tag.getShort("item")), + tag.getShort("meta"), + tag.hasKey("tag", Constants.NBT.TAG_COMPOUND) ? tag.getCompoundTag("tag") : null); + } + /** This method copies NBT, as it is mutable. */ public static ItemId create(ItemStack itemStack) { NBTTagCompound nbt = itemStack.getTagCompound(); @@ -4406,6 +4446,14 @@ public class GT_Utility { @Nullable protected abstract NBTTagCompound nbt(); + + public NBTTagCompound writeToNBT() { + NBTTagCompound tag = new NBTTagCompound(); + tag.setShort("item", (short) Item.getIdFromItem(item())); + tag.setShort("meta", (short) metaData()); + if (nbt() != null) tag.setTag("tag", nbt()); + return tag; + } } public static int getPlasmaFuelValueInEUPerLiterFromMaterial(Materials material) { diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeChemicalReactor.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeChemicalReactor.java index 76d2ce80d3..70d9250206 100644 --- a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeChemicalReactor.java +++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeChemicalReactor.java @@ -129,6 +129,11 @@ public class GT_MetaTileEntity_LargeChemicalReactor } @Override + public GT_Recipe.GT_Recipe_Map getRecipeMap() { + return GT_Recipe.GT_Recipe_Map.sMultiblockChemicalRecipes; + } + + @Override public boolean checkRecipe(ItemStack aStack) { long tVoltage = getMaxInputVoltage(); byte tier = (byte) Math.max(1, GT_Utility.getTier(tVoltage)); diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_ProcessingArray.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_ProcessingArray.java index f5f485b53e..b988f1f7a5 100644 --- a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_ProcessingArray.java +++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_ProcessingArray.java @@ -53,6 +53,7 @@ import gregtech.api.util.GT_Multiblock_Tooltip_Builder; import gregtech.api.util.GT_ProcessingArray_Manager; import gregtech.api.util.GT_Recipe; import gregtech.api.util.GT_Recipe.GT_Recipe_Map; +import gregtech.api.util.GT_Single_Recipe_Check; import gregtech.api.util.GT_Single_Recipe_Check_Processing_Array; import gregtech.api.util.GT_Utility; import gregtech.common.blocks.GT_Item_Machines; @@ -194,13 +195,7 @@ public class GT_MetaTileEntity_ProcessingArray } if (mLastRecipe == null) { - IMetaTileEntity aMachine = GT_Item_Machines.getMetaTileEntity(mInventory[1]); - if (aMachine != null) tTier = ((GT_MetaTileEntity_TieredMachineBlock) aMachine).mTier; - mMult = 0; - if (downtierUEV && tTier > 9) { - tTier--; // Lowers down the tier by 1 to allow for bigger parallel - mMult = 2; // Multiplies Parallels by 4x, keeping the energy cost - } + setTierAndMult(); } ArrayList<FluidStack> tFluidList = getStoredFluids(); FluidStack[] tFluids = tFluidList.toArray(new FluidStack[0]); @@ -223,9 +218,24 @@ public class GT_MetaTileEntity_ProcessingArray return false; } + private void setTierAndMult() { + IMetaTileEntity aMachine = GT_Item_Machines.getMetaTileEntity(mInventory[1]); + if (aMachine != null) tTier = ((GT_MetaTileEntity_TieredMachineBlock) aMachine).mTier; + mMult = 0; + if (downtierUEV && tTier > 9) { + tTier--; // Lowers down the tier by 1 to allow for bigger parallel + mMult = 2; // Multiplies Parallels by 4x, keeping the energy cost + } + } + public boolean processLockedRecipe() { GT_Single_Recipe_Check_Processing_Array tSingleRecipeCheck = (GT_Single_Recipe_Check_Processing_Array) mSingleRecipeCheck; + if (mLastRecipe == null) { + setTierAndMult(); + mLastRecipe = tSingleRecipeCheck.getRecipe(); + } + int machines = mInventory[1].stackSize << mMult; int parallel = tSingleRecipeCheck.checkRecipeInputs(true, machines); @@ -410,6 +420,11 @@ public class GT_MetaTileEntity_ProcessingArray } @Override + protected GT_Single_Recipe_Check loadSingleRecipeChecker(NBTTagCompound aNBT) { + return GT_Single_Recipe_Check_Processing_Array.tryLoad(this, getRecipeMap(), aNBT, mInventory[1]); + } + + @Override public final void onScrewdriverRightClick(byte aSide, EntityPlayer aPlayer, float aX, float aY, float aZ) { if (aPlayer.isSneaking()) { // Lock to single recipe diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_PyrolyseOven.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_PyrolyseOven.java index f87664a5fb..6d0b194612 100644 --- a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_PyrolyseOven.java +++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_PyrolyseOven.java @@ -130,6 +130,11 @@ public class GT_MetaTileEntity_PyrolyseOven extends } @Override + public GT_Recipe.GT_Recipe_Map getRecipeMap() { + return GT_Recipe.GT_Recipe_Map.sPyrolyseRecipes; + } + + @Override public boolean checkRecipe(ItemStack aStack) { long tVoltage = getMaxInputVoltage(); byte tTier = (byte) Math.max(1, GT_Utility.getTier(tVoltage)); |