Skip to content

Commit

Permalink
Bring back old multipliers but pad the overall chain to fix bugs
Browse files Browse the repository at this point in the history
The longs approach was a fucking disaster
  • Loading branch information
62832 committed Aug 29, 2023
1 parent 74d7780 commit c82be30
Show file tree
Hide file tree
Showing 7 changed files with 159 additions and 92 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
import net.minecraft.data.PackOutput;
import net.minecraft.data.tags.IntrinsicHolderTagsProvider;
import net.minecraft.data.tags.ItemTagsProvider;
import net.minecraft.tags.BlockTags;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Items;
import net.minecraft.world.level.block.Block;

import appeng.api.features.P2PTunnelAttunement;
Expand All @@ -18,8 +19,8 @@
import gripe._90.megacells.definition.MEGATags;

public class CommonTagProvider {
public static class Items extends ItemTagsProvider {
public Items(
public static class ItemTags extends ItemTagsProvider {
public ItemTags(
PackOutput output,
CompletableFuture<HolderLookup.Provider> registries,
CompletableFuture<TagLookup<Block>> blockTags) {
Expand All @@ -36,11 +37,20 @@ protected void addTags(HolderLookup.Provider provider) {
tag(MEGATags.MEGA_PATTERN_PROVIDER)
.add(MEGABlocks.MEGA_PATTERN_PROVIDER.asItem())
.add(MEGAItems.MEGA_PATTERN_PROVIDER.asItem());

tag(MEGATags.COMPRESSION_OVERRIDES)
.add(Items.QUARTZ)
.add(Items.AMETHYST_SHARD)
.add(Items.GLOWSTONE_DUST)
.add(Items.CLAY_BALL)
.add(Items.MELON_SLICE)
.add(Items.MAGMA_CREAM)
.addOptionalTag(new ResourceLocation("functionalstorage", "ignore_crafting_check"));
}
}

public static class Blocks extends IntrinsicHolderTagsProvider<Block> {
public Blocks(PackOutput packOutput, CompletableFuture<HolderLookup.Provider> registries) {
public static class BlockTags extends IntrinsicHolderTagsProvider<Block> {
public BlockTags(PackOutput packOutput, CompletableFuture<HolderLookup.Provider> registries) {
super(packOutput, Registries.BLOCK, registries, block -> BuiltInRegistries.BLOCK
.getResourceKey(block)
.orElseThrow());
Expand All @@ -49,7 +59,7 @@ public Blocks(PackOutput packOutput, CompletableFuture<HolderLookup.Provider> re
@Override
protected void addTags(HolderLookup.Provider provider) {
for (var block : MEGABlocks.getBlocks()) {
tag(BlockTags.MINEABLE_WITH_PICKAXE).add(block.block());
tag(net.minecraft.tags.BlockTags.MINEABLE_WITH_PICKAXE).add(block.block());
}

tag(MEGATags.SKY_STEEL_BLOCK).add(MEGABlocks.SKY_STEEL_BLOCK.block());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,7 @@ public final class MEGATags {

public static final TagKey<Item> MEGA_PATTERN_PROVIDER =
TagKey.create(Registries.ITEM, MEGACells.makeId("mega_pattern_provider"));

public static final TagKey<Item> COMPRESSION_OVERRIDES =
TagKey.create(Registries.ITEM, MEGACells.makeId("compression_overrides"));
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
import appeng.api.storage.cells.CellState;
import appeng.api.storage.cells.ISaveProvider;
import appeng.api.storage.cells.StorageCell;
import appeng.util.prioritylist.IPartitionList;

import gripe._90.megacells.item.MEGABulkCell;
import gripe._90.megacells.service.CompressionService;
Expand All @@ -38,7 +37,6 @@ public class BulkCellInventory implements StorageCell {

private AEItemKey storedItem;
private final AEItemKey filterItem;
private final IPartitionList partitionList;

private final Object2IntMap<AEItemKey> compressed;
private final Object2IntMap<AEItemKey> decompressed;
Expand All @@ -53,13 +51,7 @@ public BulkCellInventory(MEGABulkCell cell, ItemStack o, ISaveProvider container
this.i = o;
this.container = container;

var config = cell.getConfigInventory(i);
this.filterItem = (AEItemKey) config.getKey(0);

var builder = IPartitionList.builder();
builder.addAll(config.keySet());
this.partitionList = builder.build();

this.filterItem = (AEItemKey) cell.getConfigInventory(i).getKey(0);
this.compressed = CompressionService.getVariants(filterItem, false);
this.decompressed = CompressionService.getVariants(filterItem, true);
this.unitFactor = decompressed.values().intStream().asLongStream().reduce(1, Math::multiplyExact);
Expand Down Expand Up @@ -125,12 +117,12 @@ public long insert(AEKey what, long amount, Actionable mode, IActionSource sourc
return 0;
}

if (!compressionEnabled && (!partitionList.isListed(what) || storedItem != null && !storedItem.equals(what))) {
if (!compressionEnabled && (!filterItem.equals(what) || storedItem != null && !storedItem.equals(what))) {
return 0;
}

if (compressionEnabled
&& !partitionList.isListed(what)
&& !filterItem.equals(what)
&& !compressed.containsKey(item)
&& !decompressed.containsKey(item)) {
return 0;
Expand Down Expand Up @@ -288,7 +280,7 @@ public void getAvailableStacks(KeyCounter out) {
@Override
public boolean isPreferredStorageFor(AEKey what, IActionSource source) {
return what instanceof AEItemKey item
&& (partitionList.isListed(item) || compressed.containsKey(item) || decompressed.containsKey(item));
&& (filterItem.equals(what) || compressed.containsKey(item) || decompressed.containsKey(item));
}

@Override
Expand Down
200 changes: 131 additions & 69 deletions common/src/main/java/gripe/_90/megacells/service/CompressionService.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package gripe._90.megacells.service;

import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

import it.unimi.dsi.fastutil.Pair;
import it.unimi.dsi.fastutil.objects.Object2IntLinkedOpenHashMap;
Expand All @@ -13,19 +17,26 @@

import net.minecraft.core.RegistryAccess;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.CraftingRecipe;
import net.minecraft.world.item.crafting.RecipeManager;
import net.minecraft.world.item.crafting.RecipeType;

import appeng.api.stacks.AEItemKey;

import gripe._90.megacells.definition.MEGATags;

public class CompressionService {
// Each chain is an ordered map with the items themselves as the keys and the values being the amount of either:
// - the item itself, needed to compress to its next variant
// - the next variant, when decompressing the item
// This value is typically either 4 or 9 for any given item.
// Each chain is an ordered map with the items themselves as the keys and the values being how much of the smallest
// "unit" item in the chain makes up each subsequent variant item.
// e.g. 1 nugget -> 9 nuggets per ingot -> 81 nuggets per block -> etc.
private static final Set<Object2IntMap<AEItemKey>> compressionChains = new ObjectLinkedOpenHashSet<>();

// It may be desirable for some items to be included as variants in a chain in spite of any recipes involving those
// items not being reversible. Hence, we override any reversibility checks and generate a variant for such an item
// based on its usually irreversible recipe.
private static final Set<Override> overrides = new ObjectLinkedOpenHashSet<>();

public static Optional<Object2IntMap<AEItemKey>> getChain(AEItemKey key) {
return compressionChains.stream()
.filter(chain -> chain.containsKey(key))
Expand All @@ -43,9 +54,14 @@ public static Object2IntMap<AEItemKey> getVariants(AEItemKey key, boolean decomp
}

// Split variant chain into separate compressed/decompressed chains, omitting the initial variant
// provided
// provided but retaining the appropriate multipliers in the case of decompression
var variants = new Object2IntLinkedOpenHashMap<AEItemKey>();
keys.subList(keys.indexOf(key) + 1, keys.size()).forEach(k -> variants.put(k, chain.getInt(k)));

for (var i = keys.indexOf(key) + 1; i < keys.size(); i++) {
var multiplierIndex = i - (decompress ? 1 : 0);
variants.put(keys.get(i), chain.getInt(keys.get(multiplierIndex)));
}

return variants;
})
.orElseGet(Object2IntLinkedOpenHashMap::new);
Expand All @@ -54,6 +70,7 @@ public static Object2IntMap<AEItemKey> getVariants(AEItemKey key, boolean decomp
public static void loadRecipes(RecipeManager recipeManager, RegistryAccess access) {
// Clear old variant cache in case of the server restarting or recipes being reloaded
compressionChains.clear();
overrides.clear();

// Retrieve all available "compression" and "decompression" recipes from the current server's recipe manager
var allRecipes = recipeManager.getAllRecipesFor(RecipeType.CRAFTING);
Expand All @@ -74,33 +91,119 @@ public static void loadRecipes(RecipeManager recipeManager, RegistryAccess acces
.toList();

// Pull all available compression chains from the recipe shortlist and add these to the cache
for (var recipe : compressed) {
for (var recipe :
Stream.concat(compressed.stream(), decompressed.stream()).toList()) {
var baseVariant = recipe.getResultItem(access).getItem();

if (compressionChains.stream().noneMatch(chain -> chain.containsKey(AEItemKey.of(baseVariant)))) {
var newChain = generateChain(baseVariant, compressed, decompressed, access);
compressionChains.add(generateChain(baseVariant, compressed, decompressed, access));
}
}
}

private static Object2IntMap<AEItemKey> generateChain(
Item baseVariant,
List<CraftingRecipe> compressed,
List<CraftingRecipe> decompressed,
RegistryAccess access) {
var variants = new LinkedList<AEItemKey>();
var multipliers = new LinkedList<Integer>();

variants.addFirst(AEItemKey.of(baseVariant));

for (var lower = getNextVariant(baseVariant, decompressed, false, access); lower != null; ) {
variants.addFirst(AEItemKey.of(lower.key()));
multipliers.addFirst(lower.value());
lower = getNextVariant(lower.key(), decompressed, false, access);
}

multipliers.addFirst(1);

var chain = IntStream.range(0, variants.size())
.boxed()
.collect(Collectors.toMap(
variants::get, multipliers::get, (i, j) -> i, Object2IntLinkedOpenHashMap<AEItemKey>::new));

for (var higher = getNextVariant(baseVariant, compressed, true, access); higher != null; ) {
chain.put(AEItemKey.of(higher.key()), (int) higher.value());
higher = getNextVariant(higher.key(), compressed, true, access);
}

if (!newChain.isEmpty()) {
compressionChains.add(newChain);
return chain;
}

private static Pair<Item, Integer> getNextVariant(
Item item, List<CraftingRecipe> recipes, boolean compressed, RegistryAccess access) {
for (var override : overrides) {
if (override.smaller.equals(item) && compressed) {
return Pair.of(override.larger, override.factor);
}

if (override.larger.equals(item) && !compressed) {
return Pair.of(override.smaller, override.factor);
}
}

for (var recipe : recipes) {
for (var input : recipe.getIngredients().get(0).getItems()) {
if (input.getItem().equals(item)) {
return Pair.of(
recipe.getResultItem(access).getItem(),
compressed
? recipe.getIngredients().size()
: recipe.getResultItem(access).getCount());
}
}
}

return null;
}

private static boolean isDecompressionRecipe(CraftingRecipe recipe, RegistryAccess access) {
return recipe.getIngredients().size() == 1
&& Set.of(4, 9).contains(recipe.getResultItem(access).getCount());
}

private static boolean isCompressionRecipe(CraftingRecipe recipe, RegistryAccess access) {
return (recipe.getIngredients().size() == 4 || recipe.getIngredients().size() == 9)
&& recipe.getIngredients().stream().distinct().count() <= 1
&& recipe.getResultItem(access).getCount() == 1;
return sameIngredient(recipe)
&& recipe.getResultItem(access).getCount() == 1
&& Set.of(4, 9).contains(recipe.getIngredients().size());
}

private static boolean isDecompressionRecipe(CraftingRecipe recipe, RegistryAccess access) {
return (recipe.getResultItem(access).getCount() == 4
|| recipe.getResultItem(access).getCount() == 9)
&& recipe.getIngredients().size() == 1;
// All this for some fucking melons.
private static boolean sameIngredient(CraftingRecipe recipe) {
var ingredients = new ObjectArrayList<>(recipe.getIngredients());

if (ingredients.isEmpty()) {
return false;
}

var first = ingredients.remove(0).getItems();
if (first.length == 0) return false;

for (var ingredient : ingredients) {
var stacks = ingredient.getItems();

if (stacks.length != first.length) {
return false;
}

for (var i = 0; i < stacks.length; i++) {
if (!ItemStack.isSameItemSameTags(stacks[i], first[i])) {
return false;
}
}
}

return true;
}

private static boolean isReversibleRecipe(
CraftingRecipe recipe, List<CraftingRecipe> candidates, RegistryAccess access) {
if (overrideRecipe(recipe, access)) {
return true;
}

var compressible = false;
var decompressible = false;

Expand All @@ -126,61 +229,20 @@ private static boolean isReversibleRecipe(
return false;
}

private static Object2IntMap<AEItemKey> generateChain(
Item baseVariant,
List<CraftingRecipe> compressed,
List<CraftingRecipe> decompressed,
RegistryAccess access) {
var decompressionChain = new Object2IntLinkedOpenHashMap<AEItemKey>();

for (var lower = getNextVariant(baseVariant, decompressed, access); lower != null; ) {
decompressionChain.put(AEItemKey.of(lower.first()), (int) lower.second());
lower = getNextVariant(lower.first(), decompressed, access);
}

var compressionChain = new Object2IntLinkedOpenHashMap<AEItemKey>();

for (var higher = getNextVariant(baseVariant, compressed, access); higher != null; ) {
compressionChain.put(AEItemKey.of(higher.first()), (int) higher.second());
higher = getNextVariant(higher.first(), compressed, access);
}

// Collate decompression and compression chains together with base variant
var fullChain = new Object2IntLinkedOpenHashMap<AEItemKey>();

// In theory this shouldn't even be happening by this point
if (compressionChain.isEmpty() && decompressionChain.isEmpty()) return fullChain;
private static boolean overrideRecipe(CraftingRecipe recipe, RegistryAccess access) {
for (var item : recipe.getIngredients().get(0).getItems()) {
if (item.is(MEGATags.COMPRESSION_OVERRIDES)) {
var variant = recipe.getResultItem(access);
var compressed = isCompressionRecipe(recipe, access);
var factor = compressed ? recipe.getIngredients().size() : variant.getCount();

// By default, full chains go from the smallest "unit" variant to the most compressed, so reverse the
// decompression chain and add it first
var decompressionKeys = new ObjectArrayList<>(decompressionChain.keySet());
Collections.reverse(decompressionKeys);
decompressionKeys.forEach(k -> fullChain.put(k, decompressionChain.getInt(k)));

// Retrieve appropriate multiplier for base variant for completion's sake
fullChain.put(
AEItemKey.of(baseVariant),
fullChain.isEmpty()
? compressionChain.getInt(compressionChain.firstKey())
: fullChain.getInt(fullChain.lastKey()));

fullChain.putAll(compressionChain);
return fullChain;
}

private static Pair<Item, Integer> getNextVariant(Item item, List<CraftingRecipe> recipes, RegistryAccess access) {
for (var recipe : recipes) {
for (var input : recipe.getIngredients().get(0).getItems()) {
if (input.getItem().equals(item)) {
return Pair.of(
recipe.getResultItem(access).getItem(),
isCompressionRecipe(recipe, access)
? recipe.getIngredients().size()
: recipe.getResultItem(access).getCount());
}
overrides.add(new Override(item.getItem(), variant.getItem(), compressed, factor));
return true;
}
}

return null;
return false;
}

private record Override(Item smaller, Item larger, boolean compressed, int factor) {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ public Set<AEItemKey> getDecompressionPatterns(Object2IntMap<AEItemKey> compress

var pattern = new ItemStack(MEGAItems.DECOMPRESSION_PATTERN);
var decompressed = variants.get(variants.indexOf(variant) + 1);
var factor = compressionChain.getInt(decompressed);
var factor = compressionChain.getInt(variant);

DecompressionPatternEncoding.encode(pattern.getOrCreateTag(), variant, decompressed, factor);
patterns.add(AEItemKey.of(pattern));
Expand Down
Loading

0 comments on commit c82be30

Please sign in to comment.