Skip to content

Commit

Permalink
Add MEGA Decompression Module (#52)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
62832 authored Jun 1, 2023
1 parent 7e055d0 commit 54242d9
Show file tree
Hide file tree
Showing 15 changed files with 656 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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<ItemStack> list) {
// Don't show in creative mode, this isn't meant to be used as an item anyway
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -81,6 +82,7 @@ public static List<ItemDefinition<?>> getItems() {

public static final ItemDefinition<EnergyCardItem> GREATER_ENERGY_CARD = item("Greater Energy Card", "greater_energy_card", p -> new EnergyCardItem(p, 8));
public static final ItemDefinition<UpgradeCardItem> COMPRESSION_CARD = item("Compression Card", "compression_card", UpgradeCardItem::new);
public static final ItemDefinition<MEGADecompressionPattern.Item> DECOMPRESSION_PATTERN = item("Decompression Pattern", "decompression_pattern", MEGADecompressionPattern.Item::new);
// spotless:on

public static List<ItemDefinition<?>> getItemCells() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -21,6 +22,7 @@ public static void init() {

// spotless:off
public static final ItemDefinition<PartItem<MEGAPatternProviderPart>> MEGA_PATTERN_PROVIDER = customPart("MEGA Pattern Provider", "cable_mega_pattern_provider", MEGAPatternProviderPart.class, MEGAPatternProviderPart.Item::new);
public static final ItemDefinition<PartItem<DecompressionModulePart>> DECOMPRESSION_MODULE = part("MEGA Decompression Module", "decompression_module", DecompressionModulePart.class, DecompressionModulePart::new);
// spotless:on

private static <T extends IPart> ItemDefinition<PartItem<T>> part(String englishName, String id, Class<T> partClass,
Expand Down
Original file line number Diff line number Diff line change
@@ -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<IPatternDetails> patterns = new ObjectArrayList<>();
private final Object2LongMap<AEKey> 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<IPatternDetails> 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;
}
}
Loading

0 comments on commit 54242d9

Please sign in to comment.