From 54242d96aa7e67ff04d65dc50a330eef7a82d02b Mon Sep 17 00:00:00 2001 From: 90 Date: Thu, 1 Jun 2023 19:54:37 +0100 Subject: [PATCH] Add MEGA Decompression Module (#52) * Initial decompression service/patterns draft * Initial decompression module draft * Use MethodHandles instead of serialising the content of every cell Initial bit of optimisation * Fix patterns not updating when adding/removing compressing cells * Fix voiding bug when cancelling a craft involving decompression TODO: Fix decompression not actually being carried out * Leave ICraftingProvider::pushPattern out for now * Add successful auto-decompression logic * Add crafting recipe and initial part model * Add better part model, courtesy of Sea_Kerman --- .../crafting/DecompressionPatternDecoder.java | 41 +++++ .../DecompressionPatternEncoding.java | 35 ++++ .../crafting/MEGADecompressionPattern.java | 94 ++++++++++ .../_90/megacells/definition/MEGAItems.java | 2 + .../_90/megacells/definition/MEGAParts.java | 2 + .../part/DecompressionModulePart.java | 124 +++++++++++++ .../service/DecompressionService.java | 163 ++++++++++++++++++ .../models/item/decompression_module.json | 75 ++++++++ .../models/part/decompression_module.json | 90 ++++++++++ .../textures/part/decompression_module.png | Bin 0 -> 193 bytes .../part/decompression_module_side.png | Bin 0 -> 265 bytes .../_90/megacells/datagen/ModelProvider.java | 4 + .../_90/megacells/datagen/RecipeProvider.java | 13 +- .../java/gripe/_90/megacells/MEGACells.java | 7 + .../gripe/_90/megacells/forge/MEGACells.java | 7 + 15 files changed, 656 insertions(+), 1 deletion(-) create mode 100644 common/src/main/java/gripe/_90/megacells/crafting/DecompressionPatternDecoder.java create mode 100644 common/src/main/java/gripe/_90/megacells/crafting/DecompressionPatternEncoding.java create mode 100644 common/src/main/java/gripe/_90/megacells/crafting/MEGADecompressionPattern.java create mode 100644 common/src/main/java/gripe/_90/megacells/part/DecompressionModulePart.java create mode 100644 common/src/main/java/gripe/_90/megacells/service/DecompressionService.java create mode 100644 common/src/main/resources/assets/megacells/models/item/decompression_module.json create mode 100644 common/src/main/resources/assets/megacells/models/part/decompression_module.json create mode 100644 common/src/main/resources/assets/megacells/textures/part/decompression_module.png create mode 100644 common/src/main/resources/assets/megacells/textures/part/decompression_module_side.png diff --git a/common/src/main/java/gripe/_90/megacells/crafting/DecompressionPatternDecoder.java b/common/src/main/java/gripe/_90/megacells/crafting/DecompressionPatternDecoder.java new file mode 100644 index 00000000..48a0cc64 --- /dev/null +++ b/common/src/main/java/gripe/_90/megacells/crafting/DecompressionPatternDecoder.java @@ -0,0 +1,41 @@ +package gripe._90.megacells.crafting; + +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; + +import appeng.api.crafting.IPatternDetailsDecoder; +import appeng.api.stacks.AEItemKey; + +import gripe._90.megacells.util.Utils; + +public class DecompressionPatternDecoder implements IPatternDetailsDecoder { + public static final DecompressionPatternDecoder INSTANCE = new DecompressionPatternDecoder(); + + private DecompressionPatternDecoder() { + } + + @Override + public boolean isEncodedPattern(ItemStack stack) { + return stack.getItem() instanceof MEGADecompressionPattern.Item; + } + + @Override + public MEGADecompressionPattern decodePattern(AEItemKey what, Level level) { + if (level == null || what == null || !(what.getItem() instanceof MEGADecompressionPattern.Item) + || !(what.hasTag())) { + return null; + } + + try { + return new MEGADecompressionPattern(what); + } catch (Exception e) { + Utils.LOGGER.warn("Could not decode an invalid decompression pattern %s: %s".formatted(what.getTag(), e)); + return null; + } + } + + @Override + public MEGADecompressionPattern decodePattern(ItemStack what, Level level, boolean tryRecovery) { + return decodePattern(AEItemKey.of(what), level); + } +} diff --git a/common/src/main/java/gripe/_90/megacells/crafting/DecompressionPatternEncoding.java b/common/src/main/java/gripe/_90/megacells/crafting/DecompressionPatternEncoding.java new file mode 100644 index 00000000..1e67a53e --- /dev/null +++ b/common/src/main/java/gripe/_90/megacells/crafting/DecompressionPatternEncoding.java @@ -0,0 +1,35 @@ +package gripe._90.megacells.crafting; + +import java.util.Objects; + +import net.minecraft.nbt.CompoundTag; + +import appeng.api.stacks.AEItemKey; + +public class DecompressionPatternEncoding { + private static final String NBT_COMPRESSED = "compressed"; + private static final String NBT_DECOMPRESSED = "decompressed"; + private static final String NBT_FACTOR = "factor"; + + public static AEItemKey getCompressed(CompoundTag nbt) { + Objects.requireNonNull(nbt, "Pattern must have a compressed tag."); + return AEItemKey.fromTag(nbt.getCompound(NBT_COMPRESSED)); + } + + public static AEItemKey getDecompressed(CompoundTag nbt) { + Objects.requireNonNull(nbt, "Pattern must have a decompressed tag."); + return AEItemKey.fromTag(nbt.getCompound(NBT_DECOMPRESSED)); + } + + 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) { + tag.put(NBT_COMPRESSED, compressed.toTag()); + tag.put(NBT_DECOMPRESSED, decompressed.toTag()); + tag.putInt(NBT_FACTOR, factor); + } +} diff --git a/common/src/main/java/gripe/_90/megacells/crafting/MEGADecompressionPattern.java b/common/src/main/java/gripe/_90/megacells/crafting/MEGADecompressionPattern.java new file mode 100644 index 00000000..ae90d38d --- /dev/null +++ b/common/src/main/java/gripe/_90/megacells/crafting/MEGADecompressionPattern.java @@ -0,0 +1,94 @@ +package gripe._90.megacells.crafting; + +import java.util.Objects; + +import net.minecraft.core.NonNullList; +import net.minecraft.world.item.CreativeModeTab; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; + +import appeng.api.crafting.IPatternDetails; +import appeng.api.stacks.AEItemKey; +import appeng.api.stacks.AEKey; +import appeng.api.stacks.GenericStack; +import appeng.items.AEBaseItem; + +public class MEGADecompressionPattern implements IPatternDetails { + private final AEItemKey definition; + private final AEItemKey input; + private final IInput[] inputs; + private final GenericStack[] outputs; + + public MEGADecompressionPattern(AEItemKey definition) { + this.definition = definition; + var tag = Objects.requireNonNull(definition.getTag()); + + this.input = DecompressionPatternEncoding.getCompressed(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) }; + } + + @Override + public AEItemKey getDefinition() { + return definition; + } + + @Override + public IInput[] getInputs() { + return inputs; + } + + @Override + public GenericStack[] getOutputs() { + return outputs; + } + + @Override + public boolean equals(Object obj) { + return obj != null && obj.getClass() == getClass() + && ((MEGADecompressionPattern) obj).definition.equals(definition); + } + + @Override + public int hashCode() { + return definition.hashCode(); + } + + private class Input implements IInput { + @Override + public GenericStack[] getPossibleInputs() { + return new GenericStack[] { new GenericStack(input, 1) }; + } + + @Override + public long getMultiplier() { + return 1; + } + + @Override + public boolean isValid(AEKey input, Level level) { + return input.matches(getPossibleInputs()[0]); + } + + @Override + public AEKey getRemainingKey(AEKey template) { + return null; + } + } + + public static class Item extends AEBaseItem { + public Item(Properties properties) { + super(properties); + } + + @Override + public void fillItemCategory(CreativeModeTab tab, NonNullList list) { + // Don't show in creative mode, this isn't meant to be used as an item anyway + } + } +} diff --git a/common/src/main/java/gripe/_90/megacells/definition/MEGAItems.java b/common/src/main/java/gripe/_90/megacells/definition/MEGAItems.java index 9c3298b6..314a6da3 100644 --- a/common/src/main/java/gripe/_90/megacells/definition/MEGAItems.java +++ b/common/src/main/java/gripe/_90/megacells/definition/MEGAItems.java @@ -18,6 +18,7 @@ import appeng.items.storage.StorageTier; import appeng.menu.me.common.MEStorageMenu; +import gripe._90.megacells.crafting.MEGADecompressionPattern; import gripe._90.megacells.item.MEGABulkCell; import gripe._90.megacells.item.MEGAPortableCell; import gripe._90.megacells.util.Utils; @@ -81,6 +82,7 @@ public static List> getItems() { public static final ItemDefinition GREATER_ENERGY_CARD = item("Greater Energy Card", "greater_energy_card", p -> new EnergyCardItem(p, 8)); public static final ItemDefinition COMPRESSION_CARD = item("Compression Card", "compression_card", UpgradeCardItem::new); + public static final ItemDefinition DECOMPRESSION_PATTERN = item("Decompression Pattern", "decompression_pattern", MEGADecompressionPattern.Item::new); // spotless:on public static List> getItemCells() { diff --git a/common/src/main/java/gripe/_90/megacells/definition/MEGAParts.java b/common/src/main/java/gripe/_90/megacells/definition/MEGAParts.java index c6180441..8c0bf2ea 100644 --- a/common/src/main/java/gripe/_90/megacells/definition/MEGAParts.java +++ b/common/src/main/java/gripe/_90/megacells/definition/MEGAParts.java @@ -11,6 +11,7 @@ import appeng.items.parts.PartItem; import appeng.items.parts.PartModelsHelper; +import gripe._90.megacells.part.DecompressionModulePart; import gripe._90.megacells.part.MEGAPatternProviderPart; public final class MEGAParts { @@ -21,6 +22,7 @@ public static void init() { // spotless:off public static final ItemDefinition> MEGA_PATTERN_PROVIDER = customPart("MEGA Pattern Provider", "cable_mega_pattern_provider", MEGAPatternProviderPart.class, MEGAPatternProviderPart.Item::new); + public static final ItemDefinition> DECOMPRESSION_MODULE = part("MEGA Decompression Module", "decompression_module", DecompressionModulePart.class, DecompressionModulePart::new); // spotless:on private static ItemDefinition> part(String englishName, String id, Class partClass, diff --git a/common/src/main/java/gripe/_90/megacells/part/DecompressionModulePart.java b/common/src/main/java/gripe/_90/megacells/part/DecompressionModulePart.java new file mode 100644 index 00000000..f57ef386 --- /dev/null +++ b/common/src/main/java/gripe/_90/megacells/part/DecompressionModulePart.java @@ -0,0 +1,124 @@ +package gripe._90.megacells.part; + +import java.util.List; + +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; +import appeng.api.networking.GridFlags; +import appeng.api.networking.IGridNode; +import appeng.api.networking.crafting.ICraftingProvider; +import appeng.api.networking.security.IActionSource; +import appeng.api.networking.ticking.IGridTickable; +import appeng.api.networking.ticking.TickRateModulation; +import appeng.api.networking.ticking.TickingRequest; +import appeng.api.parts.IPartCollisionHelper; +import appeng.api.parts.IPartItem; +import appeng.api.parts.IPartModel; +import appeng.api.stacks.AEKey; +import appeng.api.stacks.KeyCounter; +import appeng.items.parts.PartModels; +import appeng.parts.AEBasePart; +import appeng.parts.PartModel; + +import gripe._90.megacells.crafting.DecompressionPatternDecoder; +import gripe._90.megacells.crafting.MEGADecompressionPattern; +import gripe._90.megacells.service.DecompressionService; +import gripe._90.megacells.util.Utils; + +public class DecompressionModulePart extends AEBasePart implements ICraftingProvider, IGridTickable { + @PartModels + public static final IPartModel MODEL = new PartModel(Utils.makeId("part/decompression_module")); + + private final List patterns = new ObjectArrayList<>(); + private final Object2LongMap outputs = new Object2LongOpenHashMap<>(); + + public DecompressionModulePart(IPartItem partItem) { + super(partItem); + getMainNode().setFlags(GridFlags.REQUIRE_CHANNEL) + .addService(IGridTickable.class, this) + .addService(ICraftingProvider.class, this) + .setIdlePowerUsage(10.0); + } + + @Override + public List getAvailablePatterns() { + return patterns; + } + + @Override + public int getPatternPriority() { + return Integer.MAX_VALUE; + } + + @Override + public boolean pushPattern(IPatternDetails patternDetails, KeyCounter[] inputHolder) { + if (!getMainNode().isActive() || !(patternDetails instanceof MEGADecompressionPattern pattern)) { + return false; + } + + var output = pattern.getPrimaryOutput(); + outputs.mergeLong(output.what(), output.amount(), Long::sum); + return true; + } + + @Override + public boolean isBusy() { + return !outputs.isEmpty(); + } + + @Override + public void getBoxes(IPartCollisionHelper bch) { + bch.addBox(3, 3, 12, 13, 13, 16); + bch.addBox(5, 5, 11, 11, 11, 12); + } + + @Override + public IPartModel getStaticModels() { + return MODEL; + } + + @Override + public TickingRequest getTickingRequest(IGridNode node) { + return new TickingRequest(1, 1, false, false); + } + + @Override + public TickRateModulation tickingRequest(IGridNode node, int ticksSinceLastCall) { + patterns.clear(); + var grid = getMainNode().getGrid(); + + if (grid != null) { + var decompressionService = grid.getService(DecompressionService.class); + + for (var chain : decompressionService.getDecompressionChains()) { + var patternItems = decompressionService.getDecompressionPatterns(chain); + var decodedPatterns = patternItems.stream() + .map(p -> DecompressionPatternDecoder.INSTANCE.decodePattern(p, getLevel())); + patterns.addAll(decodedPatterns.toList()); + } + + var storage = grid.getStorageService(); + + for (var output : outputs.object2LongEntrySet()) { + var what = output.getKey(); + var amount = output.getLongValue(); + var inserted = storage.getInventory().insert(what, amount, Actionable.MODULATE, + IActionSource.ofMachine(this)); + + 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/service/DecompressionService.java b/common/src/main/java/gripe/_90/megacells/service/DecompressionService.java new file mode 100644 index 00000000..78e3d2f8 --- /dev/null +++ b/common/src/main/java/gripe/_90/megacells/service/DecompressionService.java @@ -0,0 +1,163 @@ +package gripe._90.megacells.service; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.invoke.VarHandle; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import it.unimi.dsi.fastutil.objects.Object2IntLinkedOpenHashMap; +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet; + +import net.minecraft.world.item.ItemStack; + +import appeng.api.networking.IGridNode; +import appeng.api.networking.IGridService; +import appeng.api.networking.IGridServiceProvider; +import appeng.api.stacks.AEItemKey; +import appeng.api.storage.MEStorage; +import appeng.api.storage.cells.StorageCell; +import appeng.blockentity.storage.ChestBlockEntity; +import appeng.blockentity.storage.DriveBlockEntity; +import appeng.me.storage.DelegatingMEInventory; +import appeng.me.storage.DriveWatcher; + +import gripe._90.megacells.crafting.DecompressionPatternEncoding; +import gripe._90.megacells.definition.MEGAItems; +import gripe._90.megacells.item.cell.BulkCellInventory; + +public class DecompressionService implements IGridService, IGridServiceProvider { + private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); + private static final Class CHEST_MONITOR_CLASS; + private static final VarHandle CHEST_MONITOR_HANDLE; + private static final VarHandle CHEST_CELL_HANDLE; + private static final MethodHandle DRIVE_DELEGATE_HANDLE; + private static final VarHandle DRIVE_WATCHERS_HANDLE; + + static { + try { + CHEST_MONITOR_CLASS = Class.forName("appeng.blockentity.storage.ChestBlockEntity$ChestMonitorHandler"); + CHEST_MONITOR_HANDLE = MethodHandles.privateLookupIn(ChestBlockEntity.class, LOOKUP) + .findVarHandle(ChestBlockEntity.class, "cellHandler", CHEST_MONITOR_CLASS); + CHEST_CELL_HANDLE = MethodHandles.privateLookupIn(CHEST_MONITOR_CLASS, LOOKUP) + .findVarHandle(CHEST_MONITOR_CLASS, "cellInventory", StorageCell.class); + + DRIVE_WATCHERS_HANDLE = MethodHandles.privateLookupIn(DriveBlockEntity.class, LOOKUP) + .findVarHandle(DriveBlockEntity.class, "invBySlot", DriveWatcher[].class); + DRIVE_DELEGATE_HANDLE = MethodHandles.privateLookupIn(DelegatingMEInventory.class, LOOKUP) + .findVirtual(DelegatingMEInventory.class, "getDelegate", MethodType.methodType(MEStorage.class)); + } catch (NoSuchFieldException | NoSuchMethodException | ClassNotFoundException | IllegalAccessException e) { + throw new RuntimeException("Failed to create DecompressionService method handles", e); + } + } + + private final Set> decompressionChains = new ObjectLinkedOpenHashSet<>(); + private final List chests = new ObjectArrayList<>(); + private final List drives = new ObjectArrayList<>(); + + @Override + public void addNode(IGridNode node) { + if (node.getOwner()instanceof ChestBlockEntity chest) { + chests.add(chest); + } + + if (node.getOwner()instanceof DriveBlockEntity drive) { + drives.add(drive); + } + } + + @Override + public void removeNode(IGridNode node) { + if (node.getOwner()instanceof ChestBlockEntity chest) { + chests.remove(chest); + } + + if (node.getOwner()instanceof DriveBlockEntity drive) { + drives.remove(drive); + } + } + + @Override + public void onServerStartTick() { + decompressionChains.clear(); + + try { + for (var chest : chests) { + addChain(getCellByChest(chest)); + } + + for (var drive : drives) { + for (int i = 0; i < drive.getCellCount(); i++) { + addChain(getCellByDriveSlot(drive, i)); + } + } + } catch (Throwable e) { + throw new RuntimeException("Failed to invoke DecompressionService method handles", e); + } + } + + private StorageCell getCellByChest(ChestBlockEntity chest) { + var monitor = CHEST_MONITOR_HANDLE.get(chest); + return monitor != null ? (StorageCell) CHEST_CELL_HANDLE.get(monitor) : null; + } + + private StorageCell getCellByDriveSlot(DriveBlockEntity drive, int slot) throws Throwable { + var watchers = (DriveWatcher[]) DRIVE_WATCHERS_HANDLE.get(drive); + return watchers[slot] != null ? (StorageCell) DRIVE_DELEGATE_HANDLE.invoke(watchers[slot]) : null; + } + + private Object2IntMap getChain(BulkCellInventory cell) { + if (cell.compressionEnabled) { + return CompressionService.INSTANCE.getChain(cell.getStoredItem()).map(c -> { + var keys = new ObjectArrayList<>(c.keySet()); + Collections.reverse(keys); + + var decompressed = new Object2IntLinkedOpenHashMap(); + keys.forEach(k -> decompressed.put(k, c.getInt(k))); + return decompressed; + }).orElseGet(Object2IntLinkedOpenHashMap::new); + } + + return new Object2IntLinkedOpenHashMap<>(); + } + + private void addChain(StorageCell cell) { + if (!(cell instanceof BulkCellInventory bulkCell)) { + return; + } + + var chain = getChain(bulkCell); + + if (!chain.isEmpty()) { + decompressionChains.add(chain); + } + } + + public Set> getDecompressionChains() { + return decompressionChains; + } + + public Set getDecompressionPatterns(Object2IntMap compressionChain) { + var variants = new ObjectArrayList<>(compressionChain.keySet()); + var patterns = new ObjectLinkedOpenHashSet(); + + for (var variant : variants) { + if (variant == variants.get(variants.size() - 1)) { + continue; + } + + var pattern = new ItemStack(MEGAItems.DECOMPRESSION_PATTERN); + var decompressed = variants.get(variants.indexOf(variant) + 1); + var factor = compressionChain.getInt(decompressed); + + DecompressionPatternEncoding.encode(pattern.getOrCreateTag(), variant, decompressed, factor); + patterns.add(AEItemKey.of(pattern)); + } + + return patterns; + } +} diff --git a/common/src/main/resources/assets/megacells/models/item/decompression_module.json b/common/src/main/resources/assets/megacells/models/item/decompression_module.json new file mode 100644 index 00000000..d0a32f22 --- /dev/null +++ b/common/src/main/resources/assets/megacells/models/item/decompression_module.json @@ -0,0 +1,75 @@ +{ + "credit": "Made with Blockbench by Sea_Kerman", + "parent": "ae2:item/part_base", + "textures": { + "1": "megacells:part/decompression_module_side", + "front": "megacells:part/decompression_module", + "particle": "megacells:part/decompression_module_side" + }, + "elements": [ + { + "from": [3, 3, 5], + "to": [13, 13, 9], + "faces": { + "north": { "uv": [3, 3, 13, 13], "texture": "#front" }, + "east": { "uv": [0, 6, 4, 16], "rotation": 180, "texture": "#1" }, + "south": { "uv": [4, 6, 14, 16], "texture": "#1" }, + "west": { "uv": [0, 6, 4, 16], "texture": "#1" }, + "up": { "uv": [0, 6, 4, 16], "rotation": 90, "texture": "#1" }, + "down": { "uv": [0, 6, 4, 16], "rotation": 270, "texture": "#1" } + } + }, + { + "name": "base", + "from": [5, 5, 9], + "to": [11, 11, 10], + "faces": { + "north": { "uv": [10, 0, 16, 6], "texture": "#1" }, + "east": { "uv": [15, 0, 16, 6], "texture": "#1" }, + "south": { "uv": [10, 0, 16, 6], "texture": "#1" }, + "west": { "uv": [10, 0, 11, 6], "texture": "#1" }, + "up": { "uv": [10, 0, 16, 1], "texture": "#1" }, + "down": { "uv": [10, 5, 16, 6], "texture": "#1" } + } + }, + { + "name": "internals", + "from": [9, 5, 9], + "to": [7, 11, 10], + "faces": { + "east": { "uv": [15, 10, 16, 16], "texture": "#1" }, + "west": { "uv": [15, 10, 16, 16], "texture": "#1" } + } + }, + { + "name": "internals", + "from": [5, 9, 9], + "to": [11, 7, 10], + "faces": { + "up": { "uv": [15, 10, 16, 16], "rotation": 270, "texture": "#1" }, + "down": { "uv": [15, 10, 16, 16], "rotation": 270, "texture": "#1" } + } + }, + { + "name": "internals", + "from": [6, 6, 9], + "to": [10, 10, 10], + "faces": { + "east": { "uv": [14, 1, 15, 5], "texture": "#1" }, + "west": { "uv": [14, 1, 15, 5], "texture": "#1" }, + "up": { "uv": [14, 1, 15, 5], "rotation": 90, "texture": "#1" }, + "down": { "uv": [14, 1, 15, 5], "rotation": 90, "texture": "#1" } + } + } + ], + "groups": [ + 0, + 1, + { + "name": "base internals", + "origin": [0, 0, 0], + "color": 0, + "children": [2, 3, 4] + } + ] +} diff --git a/common/src/main/resources/assets/megacells/models/part/decompression_module.json b/common/src/main/resources/assets/megacells/models/part/decompression_module.json new file mode 100644 index 00000000..cba4684d --- /dev/null +++ b/common/src/main/resources/assets/megacells/models/part/decompression_module.json @@ -0,0 +1,90 @@ +{ + "credit": "Made with Blockbench by Sea_Kerman", + "textures": { + "front": "megacells:part/decompression_module", + "side": "megacells:part/decompression_module_side" + }, + "elements": [ + { + "name": "body", + "from": [3, 3, 0], + "to": [13, 13, 4], + "faces": { + "north": { "uv": [3, 3, 13, 13], "texture": "#front" }, + "east": { "uv": [0, 6, 4, 16], "rotation": 180, "texture": "#side" }, + "south": { "uv": [4, 6, 14, 16], "texture": "#side" }, + "west": { "uv": [0, 6, 4, 16], "texture": "#side" }, + "up": { "uv": [0, 6, 4, 16], "rotation": 90, "texture": "#side" }, + "down": { "uv": [0, 6, 4, 16], "rotation": 270, "texture": "#side" } + } + }, + { + "name": "base", + "from": [5, 5, 4], + "to": [11, 11, 5], + "faces": { + "north": { "uv": [10, 0, 16, 6], "texture": "#side" }, + "east": { "uv": [15, 0, 16, 6], "texture": "#side" }, + "south": { "uv": [10, 0, 16, 6], "texture": "#side" }, + "west": { "uv": [10, 0, 11, 6], "texture": "#side" }, + "up": { "uv": [10, 0, 16, 1], "texture": "#side" }, + "down": { "uv": [10, 5, 16, 6], "texture": "#side" } + } + }, + { + "name": "internals", + "from": [9, 5, 4], + "to": [7, 11, 5], + "faces": { + "east": { "uv": [15, 10, 16, 16], "texture": "#side" }, + "west": { "uv": [15, 10, 16, 16], "texture": "#side" } + } + }, + { + "name": "internals", + "from": [5, 9, 4], + "to": [11, 7, 5], + "faces": { + "up": { + "uv": [15, 10, 16, 16], + "rotation": 270, + "texture": "#side" + }, + "down": { + "uv": [15, 10, 16, 16], + "rotation": 270, + "texture": "#side" + } + } + }, + { + "name": "internals", + "from": [6, 6, 4], + "to": [10, 10, 5], + "faces": { + "east": { "uv": [14, 1, 15, 5], "texture": "#side" }, + "west": { "uv": [14, 1, 15, 5], "texture": "#side" }, + "up": { + "uv": [14, 1, 15, 5], + "rotation": 90, + "texture": "#side" + }, + "down": { + "uv": [14, 1, 15, 5], + "rotation": 90, + "texture": "#side" + } + } + } + ], + "groups": [ + 0, + 1, + { + "name": "base internals", + "origin": [0, 0, 0], + "color": 0, + "children": [2, 3, 4] + } + ] +} diff --git a/common/src/main/resources/assets/megacells/textures/part/decompression_module.png b/common/src/main/resources/assets/megacells/textures/part/decompression_module.png new file mode 100644 index 0000000000000000000000000000000000000000..fa280dc137e70a60f6cb60bb8eeeb587bac879a9 GIT binary patch literal 193 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPHF3h)VW1=8Z;;*ye*($dm0GBS#a zib_gKhK7dD&d$4b?K*ev+~v!cZ``tKP&Ye4V;HZ`eB(cRg*+ z_^~VVh=fFerorbr;bIPJiALu;%J+j-Dg|x+w$7D>({tsaXL%KjQ%X{9Jlmq2%CL21 zLGvmePWGZ;Kw L{an^LB{Ts54pw45 literal 0 HcmV?d00001 diff --git a/fabric/src/data/java/gripe/_90/megacells/datagen/ModelProvider.java b/fabric/src/data/java/gripe/_90/megacells/datagen/ModelProvider.java index 08454260..c0b9553b 100644 --- a/fabric/src/data/java/gripe/_90/megacells/datagen/ModelProvider.java +++ b/fabric/src/data/java/gripe/_90/megacells/datagen/ModelProvider.java @@ -34,6 +34,7 @@ import appeng.block.crafting.AbstractCraftingUnitBlock; import appeng.block.networking.EnergyCellBlock; import appeng.core.AppEng; +import appeng.core.definitions.AEItems; import gripe._90.megacells.block.MEGAPatternProviderBlock; import gripe._90.megacells.definition.MEGABlocks; @@ -104,6 +105,9 @@ public void generateItemModels(ItemModelGenerators generator) { generator.generateFlatItem(MEGAItems.GREATER_ENERGY_CARD.asItem(), ModelTemplates.FLAT_ITEM); generator.generateFlatItem(MEGAItems.COMPRESSION_CARD.asItem(), ModelTemplates.FLAT_ITEM); + generator.generateFlatItem(MEGAItems.DECOMPRESSION_PATTERN.asItem(), AEItems.CRAFTING_PATTERN.asItem(), + ModelTemplates.FLAT_ITEM); + var cells = Stream.concat( Stream.of(MEGAItems.getItemCells(), MEGAItems.getFluidCells(), AppBotItems.getCells()) .flatMap(Collection::stream), diff --git a/fabric/src/data/java/gripe/_90/megacells/datagen/RecipeProvider.java b/fabric/src/data/java/gripe/_90/megacells/datagen/RecipeProvider.java index e60dacc5..6e88c8df 100644 --- a/fabric/src/data/java/gripe/_90/megacells/datagen/RecipeProvider.java +++ b/fabric/src/data/java/gripe/_90/megacells/datagen/RecipeProvider.java @@ -140,13 +140,24 @@ protected void generateRecipes(@NotNull Consumer consumer) { .unlockedBy("has_advanced_card", has(AEItems.ADVANCED_CARD)) .unlockedBy("has_matter_ball", has(AEItems.MATTER_BALL)) .save(consumer, Utils.makeId("crafting/compression_card")); + ShapedRecipeBuilder.shaped(MEGAParts.DECOMPRESSION_MODULE) + .pattern("ICI") + .pattern("E#E") + .pattern("ILI") + .define('I', placeholderTag(IRON_INGOT)) + .define('C', AEItems.CALCULATION_PROCESSOR) + .define('E', AEItems.ENGINEERING_PROCESSOR) + .define('L', AEItems.LOGIC_PROCESSOR) + .define('#', MEGAItems.COMPRESSION_CARD) + .unlockedBy("has_compression_card", has(MEGAItems.COMPRESSION_CARD)) + .save(consumer, Utils.makeId("crafting/decompression_module")); ShapedRecipeBuilder.shaped(MEGABlocks.MEGA_PATTERN_PROVIDER) .pattern("ICI") .pattern("VPV") .pattern("ICI") .define('C', AEItems.CALCULATION_PROCESSOR) - .define('I', Items.IRON_INGOT) + .define('I', placeholderTag(IRON_INGOT)) .define('P', AEBlocks.PATTERN_PROVIDER) .define('V', AEBlocks.QUARTZ_VIBRANT_GLASS) .unlockedBy("has_pattern_provider", has(ConventionTags.PATTERN_PROVIDER)) diff --git a/fabric/src/main/java/gripe/_90/megacells/MEGACells.java b/fabric/src/main/java/gripe/_90/megacells/MEGACells.java index ad9e1649..44556214 100644 --- a/fabric/src/main/java/gripe/_90/megacells/MEGACells.java +++ b/fabric/src/main/java/gripe/_90/megacells/MEGACells.java @@ -4,9 +4,12 @@ import net.minecraft.core.Registry; import appeng.api.IAEAddonEntrypoint; +import appeng.api.crafting.PatternDetailsHelper; +import appeng.api.networking.GridServices; import appeng.core.AppEng; import gripe._90.megacells.block.MEGAPatternProviderBlock; +import gripe._90.megacells.crafting.DecompressionPatternDecoder; import gripe._90.megacells.definition.MEGABlockEntities; import gripe._90.megacells.definition.MEGABlocks; import gripe._90.megacells.definition.MEGAItems; @@ -15,6 +18,7 @@ import gripe._90.megacells.init.InitUpgrades; import gripe._90.megacells.integration.appbot.AppBotItems; import gripe._90.megacells.service.CompressionService; +import gripe._90.megacells.service.DecompressionService; import gripe._90.megacells.util.Utils; public class MEGACells implements IAEAddonEntrypoint { @@ -65,5 +69,8 @@ private void initCompression() { CompressionService.INSTANCE.loadRecipes(server.getRecipeManager()); } }); + + GridServices.register(DecompressionService.class, DecompressionService.class); + PatternDetailsHelper.registerDecoder(DecompressionPatternDecoder.INSTANCE); } } diff --git a/forge/src/main/java/gripe/_90/megacells/forge/MEGACells.java b/forge/src/main/java/gripe/_90/megacells/forge/MEGACells.java index 5ee88d81..d05383cc 100644 --- a/forge/src/main/java/gripe/_90/megacells/forge/MEGACells.java +++ b/forge/src/main/java/gripe/_90/megacells/forge/MEGACells.java @@ -12,9 +12,12 @@ import net.minecraftforge.registries.ForgeRegistries; import net.minecraftforge.registries.RegisterEvent; +import appeng.api.crafting.PatternDetailsHelper; +import appeng.api.networking.GridServices; import appeng.core.AppEng; import gripe._90.megacells.block.MEGAPatternProviderBlock; +import gripe._90.megacells.crafting.DecompressionPatternDecoder; import gripe._90.megacells.definition.MEGABlockEntities; import gripe._90.megacells.definition.MEGABlocks; import gripe._90.megacells.definition.MEGAItems; @@ -25,6 +28,7 @@ import gripe._90.megacells.integration.appmek.AppMekIntegration; import gripe._90.megacells.integration.appmek.AppMekItems; import gripe._90.megacells.service.CompressionService; +import gripe._90.megacells.service.DecompressionService; import gripe._90.megacells.util.Utils; @Mod(Utils.MODID) @@ -95,5 +99,8 @@ private void initCompression() { .loadRecipes(event.getServer().getRecipeManager())); MinecraftForge.EVENT_BUS.addListener((AddReloadListenerEvent event) -> CompressionService.INSTANCE .loadRecipes(event.getServerResources().getRecipeManager())); + + GridServices.register(DecompressionService.class, DecompressionService.class); + PatternDetailsHelper.registerDecoder(DecompressionPatternDecoder.INSTANCE); } }