diff --git a/src/main/java/committee/nova/mods/avaritia_integration/module/mekanism/common/MekIntegrationLang.java b/src/main/java/committee/nova/mods/avaritia_integration/module/mekanism/common/MekIntegrationLang.java index 8ed082b6..a090ea31 100644 --- a/src/main/java/committee/nova/mods/avaritia_integration/module/mekanism/common/MekIntegrationLang.java +++ b/src/main/java/committee/nova/mods/avaritia_integration/module/mekanism/common/MekIntegrationLang.java @@ -1,10 +1,29 @@ package committee.nova.mods.avaritia_integration.module.mekanism.common; -import mekanism.common.MekanismLang; +import committee.nova.mods.avaritia_integration.AvaritiaIntegration; +import mekanism.api.annotations.NothingNullByDefault; +import mekanism.api.text.ILangEntry; +import net.minecraft.Util; -public class MekIntegrationLang { - public static MekanismLang NEUTRON_COLLECTING; - public static MekanismLang DESCRIPTION_NEUTRON_COLLECTING; - public static MekanismLang NEUTRON_COMPRESSING; - public static MekanismLang DESCRIPTION_SINGULARITY_COMPRESSING; +@NothingNullByDefault +public enum MekIntegrationLang implements ILangEntry { + NEUTRON_COLLECTING("factory", "neutron_collecting"), + DESCRIPTION_NEUTRON_COLLECTING("description", "neutron_collecting"), + NEUTRON_COMPRESSING("factory", "singularity_compressing"), + DESCRIPTION_SINGULARITY_COMPRESSING("description", "singularity_compressing"); + + private final String key; + + MekIntegrationLang(String type, String path) { + this(Util.makeDescriptionId(type, AvaritiaIntegration.rl(path))); + } + + MekIntegrationLang(String key) { + this.key = key; + } + + @Override + public String getTranslationKey() { + return key; + } } diff --git a/src/main/java/committee/nova/mods/avaritia_integration/module/mekanism/common/content/blocktype/MekIntegrationFactoryMachine.java b/src/main/java/committee/nova/mods/avaritia_integration/module/mekanism/common/content/blocktype/MekIntegrationFactoryMachine.java index 92221207..2c6ca33f 100644 --- a/src/main/java/committee/nova/mods/avaritia_integration/module/mekanism/common/content/blocktype/MekIntegrationFactoryMachine.java +++ b/src/main/java/committee/nova/mods/avaritia_integration/module/mekanism/common/content/blocktype/MekIntegrationFactoryMachine.java @@ -2,6 +2,7 @@ import committee.nova.mods.avaritia_integration.module.mekanism.common.block.attribute.AttributeMekIntegrationFactoryType; import committee.nova.mods.avaritia_integration.module.mekanism.common.registries.MekIntegrationBlocks; +import mekanism.api.text.ILangEntry; import mekanism.common.MekanismLang; import mekanism.common.block.attribute.AttributeUpgradeable; import mekanism.common.content.blocktype.Machine; @@ -14,7 +15,7 @@ public class MekIntegrationFactoryMachine extends Machine { - public MekIntegrationFactoryMachine(Supplier> tileEntityRegistrar, MekanismLang description, MekIntegrationFactoryType factoryType) { + public MekIntegrationFactoryMachine(Supplier> tileEntityRegistrar, ILangEntry description, MekIntegrationFactoryType factoryType) { super(tileEntityRegistrar, description); add(new AttributeMekIntegrationFactoryType(factoryType), new AttributeUpgradeable(() -> MekIntegrationBlocks.getMekIntegrationFactory(FactoryTier.BASIC, getMekIntegrationFactoryType()))); } @@ -34,7 +35,7 @@ protected MekIntegrationMachineBuilder(MACHINE holder) { } public static MekIntegrationMachineBuilder, TILE, ?> createMekIntegrationFactoryMachine(Supplier> tileEntityRegistrar, - MekanismLang description, MekIntegrationFactoryType factoryType) { + ILangEntry description, MekIntegrationFactoryType factoryType) { return new MekIntegrationMachineBuilder<>(new MekIntegrationFactoryMachine<>(tileEntityRegistrar, description, factoryType)); } } diff --git a/src/main/java/committee/nova/mods/avaritia_integration/module/mekanism/common/content/blocktype/MekIntegrationFactoryType.java b/src/main/java/committee/nova/mods/avaritia_integration/module/mekanism/common/content/blocktype/MekIntegrationFactoryType.java index 73e9ecef..f3d777fb 100644 --- a/src/main/java/committee/nova/mods/avaritia_integration/module/mekanism/common/content/blocktype/MekIntegrationFactoryType.java +++ b/src/main/java/committee/nova/mods/avaritia_integration/module/mekanism/common/content/blocktype/MekIntegrationFactoryType.java @@ -5,7 +5,7 @@ import committee.nova.mods.avaritia_integration.module.mekanism.common.registries.MekIntegrationBlocks; import mekanism.api.annotations.NothingNullByDefault; import mekanism.api.text.IHasTranslationKey; -import mekanism.common.MekanismLang; +import mekanism.api.text.ILangEntry; import mekanism.common.registration.impl.BlockRegistryObject; import java.util.Locale; @@ -17,11 +17,11 @@ public enum MekIntegrationFactoryType implements IHasTranslationKey { SINGULARITY_COMPRESSING("singularity_compressing", MekIntegrationLang.NEUTRON_COMPRESSING, () -> MekIntegrationBlockTypes.SINGULARITY_COMPRESSOR, () -> MekIntegrationBlocks.SINGULARITY_COMPRESSOR); private final String registryNameComponent; - private final MekanismLang langEntry; + private final ILangEntry langEntry; private final Supplier> baseMachine; private final Supplier> baseBlock; - MekIntegrationFactoryType(String registryNameComponent, MekanismLang langEntry, Supplier> baseMachine, Supplier> baseBlock) { + MekIntegrationFactoryType(String registryNameComponent, ILangEntry langEntry, Supplier> baseMachine, Supplier> baseBlock) { this.registryNameComponent = registryNameComponent; this.langEntry = langEntry; this.baseMachine = baseMachine; diff --git a/src/main/java/committee/nova/mods/avaritia_integration/module/mekanism/common/tile/factory/TileEntityChemicalToItemMIFactory.java b/src/main/java/committee/nova/mods/avaritia_integration/module/mekanism/common/tile/factory/TileEntityChemicalToItemMIFactory.java index de7568f5..9e0f884f 100644 --- a/src/main/java/committee/nova/mods/avaritia_integration/module/mekanism/common/tile/factory/TileEntityChemicalToItemMIFactory.java +++ b/src/main/java/committee/nova/mods/avaritia_integration/module/mekanism/common/tile/factory/TileEntityChemicalToItemMIFactory.java @@ -1,15 +1,16 @@ package committee.nova.mods.avaritia_integration.module.mekanism.common.tile.factory; import committee.nova.mods.avaritia_integration.module.mekanism.common.upgrade.ChemicalToItemUpgradeData; +import it.unimi.dsi.fastutil.Hash; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap; +import mekanism.api.Action; import mekanism.api.AutomationType; import mekanism.api.IContentsListener; import mekanism.api.chemical.BasicChemicalTank; import mekanism.api.chemical.attribute.ChemicalAttributeValidator; import mekanism.api.chemical.ChemicalStack; import mekanism.api.chemical.IChemicalTank; -import mekanism.api.chemical.attribute.ChemicalAttributes; import mekanism.api.inventory.IInventorySlot; -import mekanism.api.radiation.IRadiationManager; import mekanism.api.recipes.MekanismRecipe; import mekanism.api.recipes.cache.CachedRecipe; import mekanism.api.recipes.cache.CachedRecipe.OperationTracker.RecipeError; @@ -22,6 +23,7 @@ import mekanism.common.capabilities.holder.slot.InventorySlotHelper; import mekanism.common.inventory.slot.OutputInventorySlot; import mekanism.common.inventory.warning.WarningTracker.WarningType; +import mekanism.common.lib.radiation.RadiationManager; import mekanism.common.lib.transmitter.TransmissionType; import mekanism.common.tile.component.ITileComponent; import mekanism.common.tile.component.TileComponentConfig; @@ -29,6 +31,7 @@ import mekanism.common.tile.component.config.DataType; import mekanism.common.tile.component.config.slot.InventorySlotInfo; import mekanism.common.upgrade.IUpgradeData; +import mekanism.common.util.MekanismUtils; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; import net.minecraft.core.HolderLookup; @@ -40,7 +43,9 @@ import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Set; +import java.util.function.ToIntBiFunction; public abstract class TileEntityChemicalToItemMIFactory> extends TileEntityMIFactory { @@ -49,7 +54,7 @@ public abstract class TileEntityChemicalToItemMIFactory inputGasTanks; @@ -57,12 +62,12 @@ public TileEntityChemicalToItemMIFactory(Holder blockProvider, BlockPos p super(blockProvider, pos, state, errorTypes, globalErrorTypes); inputGasTanks = new ArrayList<>(); - processInfoSlots = new GasToItemProcessInfo[tier.processes]; + processInfoSlots = new CIProcessInfo[tier.processes]; for (int i = 0; i < tier.processes; i++) { - processInfoSlots[i] = new GasToItemProcessInfo(i, inputTank[i], outputSlot[i]); + processInfoSlots[i] = new CIProcessInfo(i, inputTank[i], outputSlot[i]); } - for (GasToItemProcessInfo info : processInfoSlots) { + for (CIProcessInfo info : processInfoSlots) { inputGasTanks.add(info.inputTank()); outputItemSlots.add(info.outputSlot()); } @@ -84,16 +89,15 @@ public TileEntityChemicalToItemMIFactory(Holder blockProvider, BlockPos p protected void addGasTanks(ChemicalTankHelper builder, IContentsListener listener, IContentsListener updateSortingListener) { inputTank = new IChemicalTank[tier.processes]; gasInputHandlers = new IInputHandler[tier.processes]; - processInfoSlots = new GasToItemProcessInfo[tier.processes]; + processInfoSlots = new CIProcessInfo[tier.processes]; for (int i = 0; i < tier.processes; i++) { int index = i; - inputTank[i] = BasicChemicalTank.createModern(MAX_CHEMICAL * tier.processes, (stack, automationType) -> - automationType != AutomationType.EXTERNAL || (stack.has(ChemicalAttributes.Radiation.class) && IRadiationManager.INSTANCE.isRadiationEnabled()), - (stack, type) -> isValidInputChemical(stack.copyWithAmount(1)), - stack -> isChemicalValidForTank(stack.copyWithAmount(1)) && inputProducesOutput(index, stack.copyWithAmount(1), outputSlot[index], false), - ChemicalAttributeValidator.ALWAYS_ALLOW, recipeCacheLookupMonitors[index]); + inputTank[i] = BasicChemicalTank.createModern(MAX_CHEMICAL * tier.processes, (type, automationType) -> automationType != AutomationType.EXTERNAL || + (type.isRadioactive() && RadiationManager.isGlobalRadiationEnabled()), (stack, type) -> isValidInputChemical(stack), + stack -> isChemicalValidForTank(stack) && inputProducesOutput(index, stack, outputSlot[index], false), ChemicalAttributeValidator.ALWAYS_ALLOW, + recipeCacheLookupMonitors[index]); builder.addTank(inputTank[i]); - gasInputHandlers[i] = InputHelper.getInputHandler(inputTank[i], CachedRecipe.OperationTracker.RecipeError.NOT_ENOUGH_INPUT); + gasInputHandlers[i] = InputHelper.getInputHandler(inputTank[i], RecipeError.NOT_ENOUGH_INPUT); } } @@ -176,6 +180,247 @@ public void parseUpgradeData(HolderLookup.Provider provider, @NotNull IUpgradeDa } } - public record GasToItemProcessInfo(int process, @NotNull IChemicalTank inputTank, @NotNull IInventorySlot outputSlot) { + protected void sortInventoryOrTank() { + Map> processes = new Object2ObjectOpenCustomHashMap<>(new Hash.Strategy<>() { + @Override + public int hashCode(ChemicalStack stack) { + return stack.hashCode(); + } + + @Override + public boolean equals(ChemicalStack first, ChemicalStack second) { + return first == second || first != null && second != null && first.isEmpty() == second.isEmpty() && ChemicalStack.isSameChemical(first, second); + } + }); + List emptyProcesses = new ArrayList<>(); + for (CIProcessInfo processInfo : processInfoSlots) { + IChemicalTank inputTank = processInfo.inputTank(); + if (inputTank.isEmpty()) { + emptyProcesses.add(processInfo); + } else { + ChemicalStack inputStack = inputTank.getStack(); + CIRecipeProcessInfo recipeProcessInfo = processes.computeIfAbsent(inputStack, i -> new CIRecipeProcessInfo<>()); + recipeProcessInfo.processes.add(processInfo); + recipeProcessInfo.totalCount += inputStack.getAmount(); + if (recipeProcessInfo.lazyMinPerTank == null && !CommonWorldTickHandler.flushTagAndRecipeCaches) { + // If we don't have a lazily initialized min per slot calculation set for it yet + // and our cache is not invalid/out of date due to a reload + CachedRecipe cachedRecipe = getCachedRecipe(processInfo.process()); + if (isCachedRecipeValid(cachedRecipe, inputStack)) { + recipeProcessInfo.item = inputStack; + recipeProcessInfo.recipe = cachedRecipe.getRecipe(); + // And our current process has a cached recipe then set the lazily initialized per slot value + // Note: If something goes wrong, and we end up with zero as how much we need as an input + // we just bump the value up to one to make sure we properly handle it + recipeProcessInfo.lazyMinPerTank = (info, factory) -> factory.getNeededInput(info.recipe, (ChemicalStack) info.item); + } + } + } + } + if (processes.isEmpty()) { + // If all input slots are empty, just exit + return; + } + for (Map.Entry> entry : processes.entrySet()) { + CIRecipeProcessInfo recipeProcessInfo = entry.getValue(); + if (recipeProcessInfo.lazyMinPerTank == null) { + recipeProcessInfo.item = entry.getKey(); + // If we don't have a lazy initializer for our minPerTank setup, that means that there is + // no valid cached recipe for any of the slots of this type currently, so we want to try and + // get the recipe we will have for the first slot, once we end up with more items in the stack + recipeProcessInfo.lazyMinPerTank = (info, factory) -> { + // Note: We put all of this logic in the lazy init, so that we don't actually call any of this + // until it is needed. That way if we have no empty slots and all our input slots are filled + // we don't do any extra processing here, and can properly short circuit + ChemicalStack item = (ChemicalStack) info.item; + ChemicalStack largerInput = item.copyWithAmount(Math.min(MAX_CHEMICAL * tier.processes, info.totalCount)); + CIProcessInfo processInfo = info.processes.getFirst(); + // Try getting a recipe for our input with a larger size, and update the cache if we find one + info.recipe = factory.getRecipeForInput(processInfo.process(), largerInput, processInfo.outputSlot(), true); + if (info.recipe != null) { + return factory.getNeededInput(info.recipe, largerInput); + } + return 1; + }; + } + } + if (!emptyProcesses.isEmpty()) { + // If we have any empty slots, we need to factor them in as valid slots for items to transferred to + addEmptyTanksAsTargets(processes, emptyProcesses); + // Note: Any remaining empty slots are "ignored" as we don't have any + // spare items to distribute to them + } + // Distribute items among the slots + distributeItems(processes); + } + + protected void addEmptyTanksAsTargets(Map> processes, List emptyProcesses) { + for (Map.Entry> entry : processes.entrySet()) { + CIRecipeProcessInfo recipeProcessInfo = entry.getValue(); + long minPerTank = recipeProcessInfo.getMinPerTank(this); + long maxTanks = recipeProcessInfo.totalCount / minPerTank; + if (maxTanks <= 1) { + // If we don't have enough to even fill the input for a slot for a single recipe; skip + continue; + } + // Otherwise, if we have at least enough items for two slots see how many we already have with items in them + int processAmount = recipeProcessInfo.processes.size(); + if (maxTanks <= processAmount) { + // If we don't have enough extra to fill another slot skip + continue; + } + // Note: This is some arbitrary input stack one of the stacks contained + ChemicalStack sourceStack = entry.getKey(); + long emptyToAdd = maxTanks - processAmount; + int added = 0; + List toRemove = new ArrayList<>(); + for (CIProcessInfo emptyProcess : emptyProcesses) { + if (inputProducesOutput(emptyProcess.process(), sourceStack, emptyProcess.outputSlot(), true)) { + // If the input is valid for the stuff in the empty process' output slot + // then add our empty process to our recipeProcessInfo, and mark + // the empty process as accounted for + recipeProcessInfo.processes.add(emptyProcess); + toRemove.add(emptyProcess); + added++; + if (added >= emptyToAdd) { + // If we added as many as we could based on how much input we have; exit + break; + } + } + } + emptyProcesses.removeAll(toRemove); + if (emptyProcesses.isEmpty()) { + // We accounted for all our empty processes, stop looking at inputs + // for purposes of distributing empty slots among them + break; + } + } + } + + protected void distributeItems(Map> processes) { + for (Map.Entry> entry : processes.entrySet()) { + CIRecipeProcessInfo recipeProcessInfo = entry.getValue(); + long processAmount = recipeProcessInfo.processes.size(); + if (processAmount == 1) { + // If there is only one process with the item in it; short-circuit, no balancing is needed + continue; + } + ChemicalStack item = entry.getKey(); + // Note: This isn't based on any limits the slot may have (but we currently don't have any reduced ones + // here, so it doesn't matter) + long maxAmount = MAX_CHEMICAL * tier.processes; + long numberPerTank = recipeProcessInfo.totalCount / processAmount; + if (numberPerTank == maxAmount) { + // If all the slots are already maxed out; short-circuit, no balancing is needed + continue; + } + long remainder = recipeProcessInfo.totalCount % processAmount; + long minPerTank = recipeProcessInfo.getMinPerTank(this); + if (minPerTank > 1) { + long perSlotRemainder = numberPerTank % minPerTank; + if (perSlotRemainder > 0) { + // Reduce the number we distribute per slot by what our excess + // is if we are trying to balance it by the size of the input + // required by the recipe + numberPerTank -= perSlotRemainder; + // and then add how many items we removed to our remainder + remainder += perSlotRemainder * processAmount; + // Note: After this processing the remainder is at most: + // processAmount - 1 + processAmount * (minPerTank - 1) = + // processAmount - 1 + processAmount * minPerTank - processAmount = + // processAmount * minPerTank - 1 + // Which means that reducing the remainder by minPerTank for each + // slot while we still have a remainder, will make sure + } + if (numberPerTank + minPerTank > maxAmount) { + // If adding how much we want per slot would cause the slot to overflow + // we reduce how much we set per slot to how much there is room for + // Note: we can do this safely because while our remainder may be + // processAmount * minPerTank - 1 (as shown above), if we are in + // this if statement, that means that we really have at most: + // processAmount * maxStackSize - 1 items being distributed and + // have: processAmount * numberPerSlot + remainder + // which means that our remainder is actually at most: + // processAmount * (maxStackSize - numberPerSlot) - 1 + // so we can safely set our per slot distribution to maxStackSize - numberPerSlot + minPerTank = maxAmount - numberPerTank; + } + } + for (int i = 0; i < processAmount; i++) { + CIProcessInfo processInfo = recipeProcessInfo.processes.get(i); + IChemicalTank inputTank = processInfo.inputTank(); + long sizeForTank = numberPerTank; + if (remainder > 0) { + // If we have a remainder, factor it into our slots + if (remainder > minPerTank) { + // If our remainder is greater than how much we need to fill out the min amount for the slot + // based + // on the recipe then, to keep it distributed as evenly as possible, increase our size for the + // slot + // by how much we need, and decrease our remainder by that amount + sizeForTank += minPerTank; + remainder -= minPerTank; + } else { + // Otherwise, add our entire remainder to the size for slot, and mark our remainder as fully + // used + sizeForTank += remainder; + remainder = 0; + } + } + if (inputTank.isEmpty()) { + // Note: sizeForSlot should never be zero here as we would not have added + // the empty slot to this item's distribution grouping if it would not + // end up getting any items; check it just in case though before creating + // a stack for the slot and setting it + if (sizeForTank > 0) { + // Note: We use setStackUnchecked here, as there is a very small chance that + // the stack is not actually valid for the slot because of a reload causing + // recipes to change. If this is the case, then we want to properly not crash, + // but we would rather not add any extra overhead about revalidating the item + // each time as it can get somewhat expensive. + inputTank.setStackUnchecked(item.copyWithAmount(sizeForTank)); + } + } else { + // Slot is not currently empty + if (sizeForTank == 0) { + // If the amount of the item we want to set it to is zero (all got used by earlier stacks, which + // might + // happen if the recipe requires a stacked input (minPerTank > 1)), then we need to set the slot + // to empty + inputTank.setEmpty(); + } else if (inputTank.getCapacity() != sizeForTank) { + // Otherwise, if our slot doesn't already contain the amount we want it to, + // we need to adjust how much is stored in it, and log an error if it changed + // by a different amount then we expected + // Note: We use setStackSize here rather than setStack to avoid an unnecessary stack copy call + // as copying item stacks can sometimes be rather expensive in a heavily modded environment + MekanismUtils.logMismatchedStackSize(sizeForTank, inputTank.setStackSize(sizeForTank, Action.EXECUTE)); + } + } + } + } + } + + protected static class CIRecipeProcessInfo> { + + private final List processes = new ArrayList<>(); + @Nullable + private ToIntBiFunction, TileEntityChemicalToItemMIFactory> lazyMinPerTank; + private Object item; + private RECIPE recipe; + private long minPerTank = 1; + private long totalCount; + + public long getMinPerTank(TileEntityChemicalToItemMIFactory factory) { + if (lazyMinPerTank != null) { + // Get the value lazily + minPerTank = Math.max(1, lazyMinPerTank.applyAsInt(this, factory)); + lazyMinPerTank = null; + } + return minPerTank; + } + } + + public record CIProcessInfo(int process, @NotNull IChemicalTank inputTank, @NotNull IInventorySlot outputSlot) { } } diff --git a/src/main/java/committee/nova/mods/avaritia_integration/module/mekanism/common/tile/factory/TileEntityItemToItemMIFactory.java b/src/main/java/committee/nova/mods/avaritia_integration/module/mekanism/common/tile/factory/TileEntityItemToItemMIFactory.java index 1fc6e201..32372fb9 100644 --- a/src/main/java/committee/nova/mods/avaritia_integration/module/mekanism/common/tile/factory/TileEntityItemToItemMIFactory.java +++ b/src/main/java/committee/nova/mods/avaritia_integration/module/mekanism/common/tile/factory/TileEntityItemToItemMIFactory.java @@ -17,18 +17,18 @@ import mekanism.common.integration.computer.annotation.ComputerMethod; import mekanism.common.inventory.slot.OutputInventorySlot; import mekanism.common.inventory.warning.WarningTracker.WarningType; -import mekanism.common.lib.inventory.HashedItem; import mekanism.common.util.MekanismUtils; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; +import net.neoforged.neoforge.common.util.ItemStackMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; -import java.util.function.IntSupplier; +import java.util.function.ToIntBiFunction; public abstract class TileEntityItemToItemMIFactory> extends TileEntityMIFactory { @@ -66,11 +66,6 @@ protected void addSlots(InventorySlotHelper builder, IContentsListener listener, } } - @Override - protected void needSortingInventory() { - sortInventory(); - } - /** * Checks if the cached recipe (or recipe for current factory if the cache is out of date) can produce a specific output. * @@ -132,8 +127,8 @@ ItemStack getOutput(int process) throws ComputerException { return processInfoSlots[process].outputSlot().getStack(); } - private void sortInventory() { - Map processes = new HashMap<>(); + protected void sortInventoryOrTank() { + Map> processes = ItemStackMap.createTypeAndTagMap(); List emptyProcesses = new ArrayList<>(); for (ProcessInfo processInfo : processInfoSlots) { IInventorySlot inputSlot = processInfo.inputSlot(); @@ -141,8 +136,7 @@ private void sortInventory() { emptyProcesses.add(processInfo); } else { ItemStack inputStack = inputSlot.getStack(); - HashedItem item = HashedItem.raw(inputStack); - RecipeProcessInfo recipeProcessInfo = processes.computeIfAbsent(item, i -> new RecipeProcessInfo()); + RecipeProcessInfo recipeProcessInfo = processes.computeIfAbsent(inputStack, i -> new RecipeProcessInfo<>()); recipeProcessInfo.processes.add(processInfo); recipeProcessInfo.totalCount += inputStack.getCount(); if (recipeProcessInfo.lazyMinPerSlot == null && !CommonWorldTickHandler.flushTagAndRecipeCaches) { @@ -150,10 +144,12 @@ private void sortInventory() { // and our cache is not invalid/out of date due to a reload CachedRecipe cachedRecipe = getCachedRecipe(processInfo.process()); if (isCachedRecipeValid(cachedRecipe, inputStack)) { + recipeProcessInfo.item = inputStack; + recipeProcessInfo.recipe = cachedRecipe.getRecipe(); // And our current process has a cached recipe then set the lazily initialized per slot value // Note: If something goes wrong, and we end up with zero as how much we need as an input // we just bump the value up to one to make sure we properly handle it - recipeProcessInfo.lazyMinPerSlot = () -> Math.max(1, getNeededInput(cachedRecipe.getRecipe(), inputStack)); + recipeProcessInfo.lazyMinPerSlot = (info, factory) -> factory.getNeededInput(info.recipe, (ItemStack) info.item); } } } @@ -162,23 +158,24 @@ private void sortInventory() { //If all input slots are empty, just exit return; } - for (Map.Entry entry : processes.entrySet()) { - RecipeProcessInfo recipeProcessInfo = entry.getValue(); + for (Map.Entry> entry : processes.entrySet()) { + RecipeProcessInfo recipeProcessInfo = entry.getValue(); if (recipeProcessInfo.lazyMinPerSlot == null) { + recipeProcessInfo.item = entry.getKey(); //If we don't have a lazy initializer for our minPerSlot setup, that means that there is // no valid cached recipe for any of the slots of this type currently, so we want to try and // get the recipe we will have for the first slot, once we end up with more items in the stack - recipeProcessInfo.lazyMinPerSlot = () -> { + recipeProcessInfo.lazyMinPerSlot = (info, factory) -> { //Note: We put all of this logic in the lazy init, so that we don't actually call any of this // until it is needed. That way if we have no empty slots and all our input slots are filled // we don't do any extra processing here, and can properly short circuit - HashedItem item = entry.getKey(); - ItemStack largerInput = item.createStack(Math.min(65536, recipeProcessInfo.totalCount)); - ProcessInfo processInfo = recipeProcessInfo.processes.get(0); + ItemStack item = (ItemStack) info.item; + ItemStack largerInput = item.copyWithCount(Math.min(item.getMaxStackSize(), info.totalCount)); + ProcessInfo processInfo = info.processes.getFirst(); //Try getting a recipe for our input with a larger size, and update the cache if we find one - RECIPE recipe = getRecipeForInput(processInfo.process(), largerInput, processInfo.outputSlot(), true); - if (recipe != null) { - return Math.max(1, getNeededInput(recipe, largerInput)); + info.recipe = factory.getRecipeForInput(processInfo.process(), largerInput, processInfo.outputSlot(), true); + if (info.recipe != null) { + return factory.getNeededInput(info.recipe, largerInput); } return 1; }; @@ -194,10 +191,10 @@ private void sortInventory() { distributeItems(processes); } - private void addEmptySlotsAsTargets(Map processes, List emptyProcesses) { - for (Map.Entry entry : processes.entrySet()) { - RecipeProcessInfo recipeProcessInfo = entry.getValue(); - int minPerSlot = recipeProcessInfo.getMinPerSlot(); + private void addEmptySlotsAsTargets(Map> processes, List emptyProcesses) { + for (Map.Entry> entry : processes.entrySet()) { + RecipeProcessInfo recipeProcessInfo = entry.getValue(); + int minPerSlot = recipeProcessInfo.getMinPerSlot(this); int maxSlots = recipeProcessInfo.totalCount / minPerSlot; if (maxSlots <= 1) { //If we don't have enough to even fill the input for a slot for a single recipe; skip @@ -210,7 +207,7 @@ private void addEmptySlotsAsTargets(Map processes continue; } //Note: This is some arbitrary input stack one of the stacks contained - ItemStack sourceStack = entry.getKey().getInternalStack(); + ItemStack sourceStack = entry.getKey(); int emptyToAdd = maxSlots - processCount; int added = 0; List toRemove = new ArrayList<>(); @@ -237,15 +234,15 @@ private void addEmptySlotsAsTargets(Map processes } } - private void distributeItems(Map processes) { - for (Map.Entry entry : processes.entrySet()) { - RecipeProcessInfo recipeProcessInfo = entry.getValue(); + private void distributeItems(Map> processes) { + for (Map.Entry> entry : processes.entrySet()) { + RecipeProcessInfo recipeProcessInfo = entry.getValue(); int processCount = recipeProcessInfo.processes.size(); if (processCount == 1) { //If there is only one process with the item in it; short-circuit, no balancing is needed continue; } - HashedItem item = entry.getKey(); + ItemStack item = entry.getKey(); //Note: This isn't based on any limits the slot may have (but we currently don't have any reduced ones here, so it doesn't matter) int maxStackSize = 65536; int numberPerSlot = recipeProcessInfo.totalCount / processCount; @@ -254,7 +251,7 @@ private void distributeItems(Map processes) { continue; } int remainder = recipeProcessInfo.totalCount % processCount; - int minPerSlot = recipeProcessInfo.getMinPerSlot(); + int minPerSlot = recipeProcessInfo.getMinPerSlot(this); if (minPerSlot > 1) { int perSlotRemainder = numberPerSlot % minPerSlot; if (perSlotRemainder > 0) { @@ -314,7 +311,7 @@ private void distributeItems(Map processes) { // recipes to change. If this is the case, then we want to properly not crash, // but we would rather not add any extra overhead about revalidating the item // each time as it can get somewhat expensive. - inputSlot.setStackUnchecked(item.createStack(sizeForSlot)); + inputSlot.setStackUnchecked(item.copyWithCount(sizeForSlot)); } } else { //Slot is not currently empty @@ -338,18 +335,20 @@ private void distributeItems(Map processes) { public record ProcessInfo(int process, @NotNull MIFactoryInputInventorySlot inputSlot, @NotNull IInventorySlot outputSlot) { } - private static class RecipeProcessInfo { + private static class RecipeProcessInfo> { private final List processes = new ArrayList<>(); @Nullable - private IntSupplier lazyMinPerSlot; + private ToIntBiFunction, TileEntityItemToItemMIFactory> lazyMinPerSlot; + private Object item; + private RECIPE recipe; private int minPerSlot = 1; private int totalCount; - public int getMinPerSlot() { + public int getMinPerSlot(TileEntityItemToItemMIFactory factory) { if (lazyMinPerSlot != null) { //Get the value lazily - minPerSlot = lazyMinPerSlot.getAsInt(); + minPerSlot = Math.max(1, lazyMinPerSlot.applyAsInt(this, factory)); lazyMinPerSlot = null; } return minPerSlot; diff --git a/src/main/java/committee/nova/mods/avaritia_integration/module/mekanism/common/tile/factory/TileEntityMIFactory.java b/src/main/java/committee/nova/mods/avaritia_integration/module/mekanism/common/tile/factory/TileEntityMIFactory.java index 70e517b2..2e67bd35 100644 --- a/src/main/java/committee/nova/mods/avaritia_integration/module/mekanism/common/tile/factory/TileEntityMIFactory.java +++ b/src/main/java/committee/nova/mods/avaritia_integration/module/mekanism/common/tile/factory/TileEntityMIFactory.java @@ -164,7 +164,12 @@ public IChemicalTankHolder getInitialChemicalTanks(IContentsListener listener) { @Override protected IEnergyContainerHolder getInitialEnergyContainers(IContentsListener listener) { EnergyContainerHelper builder = EnergyContainerHelper.forSideWithConfig(this); - builder.addContainer(energyContainer = MachineEnergyContainer.input(this, listener)); + builder.addContainer(energyContainer = MachineEnergyContainer.input(this, () -> { + listener.onContentsChanged(); + for (FactoryRecipeCacheLookupMonitor cacheLookupMonitor : recipeCacheLookupMonitors) { + cacheLookupMonitor.unpause(); + } + })); return builder.build(); } @@ -219,7 +224,7 @@ protected boolean onUpdateServer() { // would make it so that some slots are now empty (because of stacked inputs // being required), we want to make sure we are able to fill those slots // with other items. - needSortingInventory(); + sortInventoryOrTank(); } else if (!sortingNeeded && CommonWorldTickHandler.flushTagAndRecipeCaches) { //Otherwise, if sorting isn't currently needed and the recipe cache is invalid // Mark sorting as being needed again for the next check as recipes may @@ -251,9 +256,7 @@ protected boolean onUpdateServer() { return sendUpdatePacket; } - protected void needSortingInventory() { - - } + protected abstract void sortInventoryOrTank(); @Nullable protected CachedRecipe getCachedRecipe(int cacheIndex) { diff --git a/src/main/java/committee/nova/mods/avaritia_integration/module/mekanism/common/tile/machine/TileEntityNeutronCollector.java b/src/main/java/committee/nova/mods/avaritia_integration/module/mekanism/common/tile/machine/TileEntityNeutronCollector.java index cd682ba1..e10e1782 100644 --- a/src/main/java/committee/nova/mods/avaritia_integration/module/mekanism/common/tile/machine/TileEntityNeutronCollector.java +++ b/src/main/java/committee/nova/mods/avaritia_integration/module/mekanism/common/tile/machine/TileEntityNeutronCollector.java @@ -9,12 +9,9 @@ import mekanism.api.RelativeSide; import mekanism.api.chemical.BasicChemicalTank; import mekanism.api.chemical.attribute.ChemicalAttributeValidator; -import mekanism.api.chemical.Chemical; import mekanism.api.chemical.ChemicalStack; import mekanism.api.chemical.IChemicalTank; -import mekanism.api.chemical.attribute.ChemicalAttributes; import mekanism.api.functions.ConstantPredicates; -import mekanism.api.radiation.IRadiationManager; import mekanism.api.recipes.cache.CachedRecipe; import mekanism.api.recipes.cache.CachedRecipe.OperationTracker.RecipeError; import mekanism.api.recipes.inputs.IInputHandler; @@ -38,7 +35,6 @@ import mekanism.common.recipe.IMekanismRecipeTypeProvider; import mekanism.common.recipe.lookup.ISingleRecipeLookupHandler.ChemicalRecipeLookupHandler; import mekanism.common.recipe.lookup.cache.InputRecipeCache.SingleChemical; -import mekanism.common.registries.MekanismChemicals; import mekanism.common.tile.component.TileComponentEjector; import mekanism.common.tile.prefab.TileEntityProgressMachine; import net.minecraft.core.BlockPos; @@ -96,7 +92,7 @@ public TileEntityNeutronCollector(BlockPos pos, BlockState state) { @Override protected @Nullable IEnergyContainerHolder getInitialEnergyContainers(IContentsListener listener, IContentsListener recipeCacheListener, IContentsListener recipeCacheUnpauseListener) { EnergyContainerHelper builder = EnergyContainerHelper.forSideWithConfig(this); - builder.addContainer(energyContainer = MachineEnergyContainer.input(this, listener)); + builder.addContainer(energyContainer = MachineEnergyContainer.input(this, recipeCacheUnpauseListener)); return builder.build(); } @@ -104,17 +100,13 @@ public TileEntityNeutronCollector(BlockPos pos, BlockState state) { protected @Nullable IInventorySlotHolder getInitialInventory(IContentsListener listener, IContentsListener recipeCacheListener, IContentsListener recipeCacheUnpauseListener) { InventorySlotHelper builder = InventorySlotHelper.forSideWithConfig(this); builder.addSlot(gasInputSlot = ChemicalInventorySlot.fill(gasTank, listener, 7, 56)); - builder.addSlot(outputSlot = OutputInventorySlot.at(listener, 131, 36)) + builder.addSlot(outputSlot = OutputInventorySlot.at(recipeCacheUnpauseListener, 131, 36)) .tracksWarnings(slot -> slot.warning(WarningType.NO_SPACE_IN_OUTPUT, getWarningCheck(RecipeError.NOT_ENOUGH_OUTPUT_SPACE))); builder.addSlot(energySlot = EnergyInventorySlot.fillOrConvert(energyContainer, this::getLevel, listener, 7, 14)); gasInputSlot.setSlotOverlay(SlotOverlay.PLUS); return builder.build(); } - private boolean canGasInsert(Chemical Chemical) { - return Chemical.equals(MekanismChemicals.SPENT_NUCLEAR_WASTE.get()) || Chemical.equals(MekanismChemicals.POLONIUM.get()) || Chemical.equals(MekanismChemicals.PLUTONIUM.get()) || Chemical.equals(MekanismChemicals.ANTIMATTER.get()); - } - @Override protected boolean onUpdateServer() { boolean sendUpdatePacket = super.onUpdateServer(); diff --git a/src/main/java/committee/nova/mods/avaritia_integration/module/mekanism/common/util/MekIntegrationUtils.java b/src/main/java/committee/nova/mods/avaritia_integration/module/mekanism/common/util/MekIntegrationUtils.java index 7dcba52c..980e4a52 100644 --- a/src/main/java/committee/nova/mods/avaritia_integration/module/mekanism/common/util/MekIntegrationUtils.java +++ b/src/main/java/committee/nova/mods/avaritia_integration/module/mekanism/common/util/MekIntegrationUtils.java @@ -22,7 +22,7 @@ public static FactoryTier[] getFactoryTier() { FactoryTier[] mergedTiers; mergedTiers = Arrays.copyOf(EnumUtils.FACTORY_TIERS, EnumUtils.FACTORY_TIERS.length + MekIntegrationEnumUtils.EM_TIERS.length); System.arraycopy(MekIntegrationEnumUtils.EM_TIERS, 0, mergedTiers, EnumUtils.FACTORY_TIERS.length, MekIntegrationEnumUtils.EM_TIERS.length); - return EnumUtils.FACTORY_TIERS; + return mergedTiers; } else { return EnumUtils.FACTORY_TIERS; } diff --git a/src/main/resources/assets/avaritia_integration/lang/en_us.json b/src/main/resources/assets/avaritia_integration/lang/en_us.json index 01770ea8..180620f8 100644 --- a/src/main/resources/assets/avaritia_integration/lang/en_us.json +++ b/src/main/resources/assets/avaritia_integration/lang/en_us.json @@ -287,5 +287,11 @@ "module.avaritia_integration.name.refinedstorage": "Refined Storage", "module.avaritia_integration.name.slashblade": "SlashBlade", "module.avaritia_integration.name.thermal_expansion": "Thermal Expansion", - "module.avaritia_integration.name.tconstruct": "Tinkers' Construct" + "module.avaritia_integration.name.tconstruct": "Tinkers' Construct", + + "factory.avaritia_integration.neutron_collecting": "neutron_collecting", + "factory.avaritia_integration.singularity_compressing": "singularity_compressing", + + "description.avaritia_integration.neutron_collecting": "A machine that utilizes rare materials to produce neutronium", + "description.avaritia_integration.singularity_compressing": "A machine that compresses resources into a singularity" } \ No newline at end of file diff --git a/src/main/resources/assets/avaritia_integration/lang/zh_cn.json b/src/main/resources/assets/avaritia_integration/lang/zh_cn.json index 659f3c4a..6490a376 100644 --- a/src/main/resources/assets/avaritia_integration/lang/zh_cn.json +++ b/src/main/resources/assets/avaritia_integration/lang/zh_cn.json @@ -287,5 +287,11 @@ "module.avaritia_integration.name.refinedstorage": "精致存储", "module.avaritia_integration.name.slashblade": "拔刀剑", "module.avaritia_integration.name.thermal_expansion": "热力拓展(热力膨胀)", - "module.avaritia_integration.name.tconstruct": "匠魂" + "module.avaritia_integration.name.tconstruct": "匠魂", + + "factory.avaritia_integration.neutron_collecting": "中子收集", + "factory.avaritia_integration.singularity_compressing": "奇点压缩", + + "description.avaritia_integration.neutron_collecting": "一台使用稀有物资产生中子素的机器", + "description.avaritia_integration.singularity_compressing": "一台将资源压缩成奇点的机器" } \ No newline at end of file diff --git a/src/main/templates/META-INF/neoforge.mods.toml b/src/main/templates/META-INF/neoforge.mods.toml index 5ba7c65e..6c8b8e62 100644 --- a/src/main/templates/META-INF/neoforge.mods.toml +++ b/src/main/templates/META-INF/neoforge.mods.toml @@ -72,6 +72,12 @@ versionRange = "${minecraft_version_range}" ordering = "NONE" side = "BOTH" +[[dependencies.${mod_id}]] + modId = "evolvedmekanism" + type = "optional" + ordering = "AFTER" + side = "BOTH" + # Features are specific properties of the game environment, that you may want to declare you require. This example declares # that your mod requires GL version 3.2 or higher. Other features will be added. They are side aware so declaring this won't # stop your mod loading on the server for example.