diff --git a/common/src/main/java/gripe/_90/megacells/crafting/DecompressionPattern.java b/common/src/main/java/gripe/_90/megacells/crafting/DecompressionPattern.java index bcb91e67..79f85512 100644 --- a/common/src/main/java/gripe/_90/megacells/crafting/DecompressionPattern.java +++ b/common/src/main/java/gripe/_90/megacells/crafting/DecompressionPattern.java @@ -11,22 +11,20 @@ public class DecompressionPattern implements IPatternDetails { private final AEItemKey definition; - private final AEItemKey input; - private final IInput[] inputs; - private final GenericStack[] outputs; + private final AEItemKey compressed; + private final long count; + private final AEItemKey decompressed; + private final int factor; public DecompressionPattern(AEItemKey definition) { this.definition = definition; var tag = Objects.requireNonNull(definition.getTag()); - this.input = DecompressionPatternEncoding.getCompressed(tag); + this.compressed = DecompressionPatternEncoding.getCompressed(tag); + this.count = DecompressionPatternEncoding.getCount(tag); - var decompressed = DecompressionPatternEncoding.getDecompressed(tag); - var factor = DecompressionPatternEncoding.getFactor(tag); - var output = decompressed.toStack(factor); - - this.inputs = new IInput[] {new Input()}; - this.outputs = new GenericStack[] {GenericStack.fromItemStack(output)}; + this.decompressed = DecompressionPatternEncoding.getDecompressed(tag); + this.factor = DecompressionPatternEncoding.getFactor(tag); } @Override @@ -36,12 +34,12 @@ public AEItemKey getDefinition() { @Override public IInput[] getInputs() { - return inputs; + return new IInput[] {new Input(compressed, count)}; } @Override public GenericStack[] getOutputs() { - return outputs; + return new GenericStack[] {new GenericStack(decompressed, count * factor)}; } @Override @@ -56,7 +54,7 @@ public int hashCode() { return definition.hashCode(); } - private class Input implements IInput { + private record Input(AEItemKey input, long multiplier) implements IInput { @Override public GenericStack[] getPossibleInputs() { return new GenericStack[] {new GenericStack(input, 1)}; @@ -64,7 +62,7 @@ public GenericStack[] getPossibleInputs() { @Override public long getMultiplier() { - return 1; + return multiplier; } @Override diff --git a/common/src/main/java/gripe/_90/megacells/crafting/DecompressionPatternEncoding.java b/common/src/main/java/gripe/_90/megacells/crafting/DecompressionPatternEncoding.java index 044c6b67..91c4a8cf 100644 --- a/common/src/main/java/gripe/_90/megacells/crafting/DecompressionPatternEncoding.java +++ b/common/src/main/java/gripe/_90/megacells/crafting/DecompressionPatternEncoding.java @@ -9,6 +9,7 @@ public class DecompressionPatternEncoding { private static final String NBT_COMPRESSED = "compressed"; private static final String NBT_DECOMPRESSED = "decompressed"; + private static final String NBT_COUNT = "count"; private static final String NBT_FACTOR = "factor"; public static AEItemKey getCompressed(CompoundTag nbt) { @@ -21,14 +22,20 @@ public static AEItemKey getDecompressed(CompoundTag nbt) { return AEItemKey.fromTag(nbt.getCompound(NBT_DECOMPRESSED)); } + public static long getCount(CompoundTag nbt) { + Objects.requireNonNull(nbt, "Pattern must have a count tag."); + return nbt.getLong(NBT_COUNT); + } + public static int getFactor(CompoundTag nbt) { Objects.requireNonNull(nbt, "Pattern must have a factor tag."); return nbt.getInt(NBT_FACTOR); } - public static void encode(CompoundTag tag, AEItemKey compressed, AEItemKey decompressed, int factor) { + public static void encode(CompoundTag tag, AEItemKey compressed, AEItemKey decompressed, long count, int factor) { tag.put(NBT_COMPRESSED, compressed.toTag()); tag.put(NBT_DECOMPRESSED, decompressed.toTag()); + tag.putLong(NBT_COUNT, count); tag.putInt(NBT_FACTOR, factor); } } diff --git a/common/src/main/java/gripe/_90/megacells/crafting/DecompressionService.java b/common/src/main/java/gripe/_90/megacells/crafting/DecompressionService.java index b21cc02d..152f64ac 100644 --- a/common/src/main/java/gripe/_90/megacells/crafting/DecompressionService.java +++ b/common/src/main/java/gripe/_90/megacells/crafting/DecompressionService.java @@ -9,25 +9,32 @@ import net.minecraft.world.item.ItemStack; +import appeng.api.crafting.IPatternDetails; import appeng.api.implementations.blockentities.IChestOrDrive; import appeng.api.networking.IGridNode; import appeng.api.networking.IGridService; import appeng.api.networking.IGridServiceProvider; +import appeng.api.networking.crafting.ICraftingProvider; import appeng.api.stacks.AEItemKey; import gripe._90.megacells.definition.MEGAItems; import gripe._90.megacells.item.cell.BulkCellInventory; -import gripe._90.megacells.util.CompressionChain; +import gripe._90.megacells.item.part.DecompressionModulePart; public class DecompressionService implements IGridService, IGridServiceProvider { - private final Set decompressionChains = new ObjectLinkedOpenHashSet<>(); private final List cellHosts = new ObjectArrayList<>(); + private final List patterns = new ObjectArrayList<>(); + private final List modules = new ObjectArrayList<>(); @Override public void addNode(IGridNode node) { if (node.getOwner() instanceof IChestOrDrive cellHost) { cellHosts.add(cellHost); } + + if (node.getOwner() instanceof DecompressionModulePart module) { + modules.add(module); + } } @Override @@ -35,43 +42,55 @@ public void removeNode(IGridNode node) { if (node.getOwner() instanceof IChestOrDrive cellHost) { cellHosts.remove(cellHost); } + + if (node.getOwner() instanceof DecompressionModulePart module) { + modules.remove(module); + } } @Override public void onServerStartTick() { - decompressionChains.clear(); + patterns.clear(); for (var cellHost : cellHosts) { for (int i = 0; i < cellHost.getCellCount(); i++) { var cell = cellHost.getOriginalCellInventory(i); if (cell instanceof BulkCellInventory bulkCell && bulkCell.isCompressionEnabled()) { - decompressionChains.add(bulkCell.getDecompressionChain()); + patterns.addAll(generatePatterns(bulkCell)); } } } + + if (!modules.isEmpty()) { + ICraftingProvider.requestUpdate(modules.get(0).getMainNode()); + } } - public Set getDecompressionChains() { - return Collections.unmodifiableSet(decompressionChains); + public List getPatterns() { + return Collections.unmodifiableList(patterns); } - public Set getDecompressionPatterns(CompressionChain chain) { - var patterns = new ObjectLinkedOpenHashSet(); + private Set generatePatterns(BulkCellInventory cell) { + var patterns = new ObjectLinkedOpenHashSet(); + var chain = cell.getDecompressionChain(); + var count = 1; for (var variant : chain) { if (variant == chain.last()) { continue; } - var pattern = new ItemStack(MEGAItems.DECOMPRESSION_PATTERN); + var patternItem = new ItemStack(MEGAItems.DECOMPRESSION_PATTERN); var decompressed = chain.get(chain.indexOf(variant) + 1); DecompressionPatternEncoding.encode( - pattern.getOrCreateTag(), variant.item(), decompressed.item(), variant.factor()); - patterns.add(AEItemKey.of(pattern)); + patternItem.getOrCreateTag(), variant.item(), decompressed.item(), count, variant.factor()); + var pattern = new DecompressionPattern(AEItemKey.of(patternItem.getItem(), patternItem.getTag())); + patterns.add(pattern); + count *= variant.factor(); } - return Collections.unmodifiableSet(patterns); + return patterns; } } diff --git a/common/src/main/java/gripe/_90/megacells/item/cell/BulkCellInventory.java b/common/src/main/java/gripe/_90/megacells/item/cell/BulkCellInventory.java index 9752f7c2..f397ea38 100644 --- a/common/src/main/java/gripe/_90/megacells/item/cell/BulkCellInventory.java +++ b/common/src/main/java/gripe/_90/megacells/item/cell/BulkCellInventory.java @@ -4,6 +4,10 @@ import java.math.BigInteger; +import it.unimi.dsi.fastutil.objects.Object2LongLinkedOpenHashMap; +import it.unimi.dsi.fastutil.objects.Object2LongMap; +import it.unimi.dsi.fastutil.objects.ObjectSet; + import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; import net.minecraft.world.item.ItemStack; @@ -18,12 +22,14 @@ import appeng.api.storage.cells.StorageCell; import gripe._90.megacells.item.MEGABulkCell; +import gripe._90.megacells.item.part.DecompressionModulePart; import gripe._90.megacells.util.CompressionChain; import gripe._90.megacells.util.CompressionService; public class BulkCellInventory implements StorageCell { private static final String KEY = "key"; private static final String UNIT_COUNT = "smallestUnitCount"; + private static final long STACK_LIMIT = (long) Math.pow(2, 42); private final ISaveProvider container; private final ItemStack i; @@ -31,11 +37,14 @@ public class BulkCellInventory implements StorageCell { private AEItemKey storedItem; private final AEItemKey filterItem; + private final boolean compressionEnabled; private final CompressionChain compressionChain; private BigInteger unitCount; - private AEItemKey highestCompressed; - private final long unitFactor; - private final boolean compressionEnabled; + private final BigInteger unitFactor; + + private Object2LongMap availableVariants; + // Cache used by the decompression service in order for auto-crafting to not break and hang indefinitely. + private ObjectSet cachedVariants; private boolean isPersisted = true; @@ -43,17 +52,17 @@ public BulkCellInventory(MEGABulkCell cell, ItemStack o, ISaveProvider container this.i = o; this.container = container; - this.filterItem = (AEItemKey) cell.getConfigInventory(i).getKey(0); - this.compressionChain = CompressionService.getChain(filterItem).orElseGet(CompressionChain::new); - this.unitFactor = compressionChain.unitFactor(filterItem); + filterItem = (AEItemKey) cell.getConfigInventory(i).getKey(0); + compressionEnabled = cell.getUpgrades(i).isInstalled(COMPRESSION_CARD); + compressionChain = CompressionService.getChain(filterItem).orElseGet(CompressionChain::new); + unitFactor = compressionChain.unitFactor(filterItem); - this.storedItem = getTag().contains(KEY) ? AEItemKey.fromTag(getTag().getCompound(KEY)) : null; - this.unitCount = !getTag().getString(UNIT_COUNT).isEmpty() + storedItem = getTag().contains(KEY) ? AEItemKey.fromTag(getTag().getCompound(KEY)) : null; + unitCount = !getTag().getString(UNIT_COUNT).isEmpty() ? new BigInteger(getTag().getString(UNIT_COUNT)) : BigInteger.ZERO; - this.highestCompressed = storedItem; - this.compressionEnabled = cell.getUpgrades(i).isInstalled(COMPRESSION_CARD); + updateVariants(true); } private long clampedLong(BigInteger toClamp, long limit) { @@ -82,7 +91,7 @@ public AEItemKey getStoredItem() { } public long getStoredQuantity() { - return clampedLong(unitCount.divide(BigInteger.valueOf(unitFactor)), Long.MAX_VALUE); + return clampedLong(unitCount.divide(unitFactor), Long.MAX_VALUE); } public AEItemKey getFilterItem() { @@ -94,12 +103,54 @@ public boolean isCompressionEnabled() { } public CompressionChain getDecompressionChain() { - return compressionChain.decompressFrom(highestCompressed); + if (cachedVariants.isEmpty()) { + return new CompressionChain(); + } + + var highest = cachedVariants.stream().toList().get(0); + return compressionChain.decompressFrom(highest); + } + + private void updateVariants(boolean cache) { + availableVariants = gatherVariants(); + + if (cache) { + cachedVariants = availableVariants.keySet(); + } + } + + private Object2LongMap gatherVariants() { + var variants = new Object2LongLinkedOpenHashMap(); + + if (storedItem != null) { + if (compressionEnabled && storedItem.equals(filterItem) && !compressionChain.isEmpty()) { + var count = unitCount; + var chain = compressionChain.lastMultiplierSwapped(); + + for (var variant : chain) { + var compressionFactor = BigInteger.valueOf(variant.factor()); + var key = variant.item(); + + if (count.divide(compressionFactor).signum() == 1 && variant != chain.last()) { + variants.putAndMoveToFirst( + key, count.remainder(compressionFactor).longValue()); + count = count.divide(compressionFactor); + } else { + variants.putAndMoveToFirst(key, clampedLong(count, STACK_LIMIT)); + break; + } + } + } else { + variants.put(storedItem, clampedLong(unitCount.divide(unitFactor), STACK_LIMIT)); + } + } + + return variants; } @Override public double getIdleDrain() { - return 10.0f; + return 5.0f; } @Override @@ -116,7 +167,7 @@ public long insert(AEKey what, long amount, Actionable mode, IActionSource sourc return 0; } - var factor = BigInteger.valueOf(compressionChain.unitFactor(item)); + var factor = compressionChain.unitFactor(item); var units = BigInteger.valueOf(amount).multiply(factor); if (mode == Actionable.MODULATE) { @@ -125,6 +176,7 @@ public long insert(AEKey what, long amount, Actionable mode, IActionSource sourc } unitCount = unitCount.add(units); + updateVariants(source.machine().isPresent() && source.machine().get() instanceof DecompressionModulePart); saveChanges(); } @@ -137,7 +189,7 @@ public long extract(AEKey what, long amount, Actionable mode, IActionSource sour return 0; } - var itemCount = unitCount.divide(BigInteger.valueOf(unitFactor)); + var itemCount = unitCount.divide(unitFactor); if (!compressionEnabled && (itemCount.signum() < 1 || !storedItem.equals(what))) { return 0; } @@ -149,7 +201,7 @@ public long extract(AEKey what, long amount, Actionable mode, IActionSource sour return 0; } - var factor = BigInteger.valueOf(compressionChain.unitFactor(item)); + var factor = compressionChain.unitFactor(item); var units = BigInteger.valueOf(amount).multiply(factor); var currentUnitCount = unitCount; @@ -157,6 +209,7 @@ public long extract(AEKey what, long amount, Actionable mode, IActionSource sour if (mode == Actionable.MODULATE) { storedItem = null; unitCount = BigInteger.ZERO; + updateVariants(false); saveChanges(); } @@ -164,6 +217,7 @@ public long extract(AEKey what, long amount, Actionable mode, IActionSource sour } else { if (mode == Actionable.MODULATE) { unitCount = unitCount.subtract(units); + updateVariants(false); saveChanges(); } @@ -201,30 +255,7 @@ public void persist() { @Override public void getAvailableStacks(KeyCounter out) { - if (storedItem != null) { - var stackLimit = (long) Math.pow(2, 42); - - if (compressionEnabled && storedItem.equals(filterItem) && !compressionChain.isEmpty()) { - var count = unitCount; - var chain = compressionChain.lastMultiplierSwapped(); - - for (var variant : chain) { - var compressionFactor = BigInteger.valueOf(variant.factor()); - var key = variant.item(); - - if (count.divide(compressionFactor).signum() == 1 && variant != chain.last()) { - out.add(key, clampedLong(count.remainder(compressionFactor), stackLimit)); - count = count.divide(compressionFactor); - } else { - out.add(key, clampedLong(count, stackLimit)); - highestCompressed = key; - break; - } - } - } else { - out.add(storedItem, clampedLong(unitCount.divide(BigInteger.valueOf(unitFactor)), stackLimit)); - } - } + availableVariants.forEach(out::add); } @Override diff --git a/common/src/main/java/gripe/_90/megacells/item/part/DecompressionModulePart.java b/common/src/main/java/gripe/_90/megacells/item/part/DecompressionModulePart.java index 369118f7..7e434bb7 100644 --- a/common/src/main/java/gripe/_90/megacells/item/part/DecompressionModulePart.java +++ b/common/src/main/java/gripe/_90/megacells/item/part/DecompressionModulePart.java @@ -4,7 +4,6 @@ import it.unimi.dsi.fastutil.objects.Object2LongMap; import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap; -import it.unimi.dsi.fastutil.objects.ObjectArrayList; import appeng.api.config.Actionable; import appeng.api.crafting.IPatternDetails; @@ -20,7 +19,6 @@ import appeng.api.parts.IPartModel; import appeng.api.stacks.AEKey; import appeng.api.stacks.KeyCounter; -import appeng.crafting.pattern.AEPatternDecoder; import appeng.items.parts.PartModels; import appeng.parts.AEBasePart; import appeng.parts.PartModel; @@ -33,7 +31,6 @@ public class DecompressionModulePart extends AEBasePart implements ICraftingProv @PartModels public static final IPartModel MODEL = new PartModel(MEGACells.makeId("part/decompression_module")); - private final List patterns = new ObjectArrayList<>(); private final Object2LongMap outputs = new Object2LongOpenHashMap<>(); public DecompressionModulePart(IPartItem partItem) { @@ -47,7 +44,8 @@ public DecompressionModulePart(IPartItem partItem) { @Override public List getAvailablePatterns() { - return patterns; + var grid = getMainNode().getGrid(); + return grid != null ? grid.getService(DecompressionService.class).getPatterns() : List.of(); } @Override @@ -63,6 +61,8 @@ public boolean pushPattern(IPatternDetails patternDetails, KeyCounter[] inputHol var output = pattern.getPrimaryOutput(); outputs.mergeLong(output.what(), output.amount(), Long::sum); + + getMainNode().ifPresent((grid, node) -> grid.getTickManager().alertDevice(node)); return true; } @@ -84,38 +84,23 @@ public IPartModel getStaticModels() { @Override public TickingRequest getTickingRequest(IGridNode node) { - return new TickingRequest(1, 1, false, false); + return new TickingRequest(1, 1, !isBusy(), true); } @Override public TickRateModulation tickingRequest(IGridNode node, int ticksSinceLastCall) { - patterns.clear(); - var grid = getMainNode().getGrid(); - - if (grid != null) { - var decompressionService = grid.getService(DecompressionService.class); + var storage = node.getGrid().getStorageService().getInventory(); - for (var chain : decompressionService.getDecompressionChains()) { - var patternItems = decompressionService.getDecompressionPatterns(chain); - var patterns = patternItems.stream().map(p -> AEPatternDecoder.INSTANCE.decodePattern(p, getLevel())); - this.patterns.addAll(patterns.toList()); - } - - var storage = grid.getStorageService().getInventory(); + for (var output : outputs.object2LongEntrySet()) { + var what = output.getKey(); + var amount = output.getLongValue(); + var inserted = storage.insert(what, amount, Actionable.MODULATE, IActionSource.ofMachine(this)); - for (var output : outputs.object2LongEntrySet()) { - var what = output.getKey(); - var amount = output.getLongValue(); - var inserted = storage.insert(what, amount, Actionable.MODULATE, IActionSource.ofMachine(this)); - - if (inserted >= amount) { - outputs.removeLong(what); - } else if (inserted > 0) { - outputs.put(what, amount - inserted); - } + if (inserted >= amount) { + outputs.removeLong(what); + } else if (inserted > 0) { + outputs.put(what, amount - inserted); } - - ICraftingProvider.requestUpdate(getMainNode()); } return TickRateModulation.URGENT; diff --git a/common/src/main/java/gripe/_90/megacells/util/CompressionChain.java b/common/src/main/java/gripe/_90/megacells/util/CompressionChain.java index 51e56823..b7499530 100644 --- a/common/src/main/java/gripe/_90/megacells/util/CompressionChain.java +++ b/common/src/main/java/gripe/_90/megacells/util/CompressionChain.java @@ -1,5 +1,6 @@ package gripe._90.megacells.util; +import java.math.BigInteger; import java.util.Collections; import java.util.stream.Collectors; @@ -16,15 +17,16 @@ public boolean containsVariant(AEItemKey item) { return this.stream().anyMatch(v -> v.item().equals(item)); } - public long unitFactor(AEItemKey item) { + public BigInteger unitFactor(AEItemKey item) { var variant = this.stream().filter(v -> v.item().equals(item)).findFirst(); if (variant.isEmpty()) { - return 1; + return BigInteger.ONE; } var subChain = this.subList(0, indexOf(variant.get()) + 1); - return subChain.stream().map(CompressionVariant::longFactor).reduce(1L, Math::multiplyExact); + var factor = subChain.stream().map(CompressionVariant::longFactor).reduce(1L, Math::multiplyExact); + return BigInteger.valueOf(factor); } public CompressionVariant last() {