Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package com.glodblock.github.extendedae.util;

import appeng.api.stacks.GenericStack;
import appeng.api.upgrades.IUpgradeableObject;
import appeng.menu.implementations.UpgradeableMenu;
import appeng.menu.slot.FakeSlot;

import com.google.common.math.LongMath;

import net.minecraft.world.inventory.Slot;
import net.minecraft.world.item.ItemStack;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
// Note: this class is also in a PR to base AE2 at https://github.com/AppliedEnergistics/Applied-Energistics-2/pull/8915
// Should that PR be accepted, it will no longer be necessary in ExtendedAE.
public class FakeSlotTransferHelper<T extends UpgradeableMenu<? extends IUpgradeableObject>> {
private static void addOrMerge(List<GenericStack> stacks, GenericStack newStack) {
for (int i = 0; i < stacks.size(); i++) {
var existingStack = stacks.get(i);
if (Objects.equals(existingStack.what(), newStack.what())) {
// Add the new amount onto the existing amount
long newAmount = LongMath.saturatedAdd(existingStack.amount(), newStack.amount());
stacks.set(i, new GenericStack(newStack.what(), newAmount));

// Determine if the addition overflowed. If it did, add the remainder as a new
// stack.
long overflow = newStack.amount() - (newAmount - existingStack.amount());
if (overflow > 0) {
stacks.add(new GenericStack(newStack.what(), overflow));
}
return;
}
}
stacks.add(newStack);
}

private List<FakeSlot> getFakeSlots(T menu) {
List<FakeSlot> slots = new ArrayList<>();
for (Slot slot : menu.slots) {
if (slot instanceof FakeSlot fs) {
slots.add(fs);
}
}
return slots;
}

public void transfer(T menu, List<List<GenericStack>> recipeInputs) {
ArrayList<GenericStack> inputsToSet = new ArrayList<>();
for (List<GenericStack> genericIngredient : recipeInputs) {
if (!genericIngredient.isEmpty()) {
// Taking the first possible ingredient from the stack is a bit lazy, but
// the number of use cases for autofilling tagged recipe inputs into an interface
// has got to be in the single digits...
addOrMerge(inputsToSet, genericIngredient.getFirst());
}
}
List<FakeSlot> interfaceConfig = getFakeSlots(menu);
int i = 0;
for (FakeSlot slot : interfaceConfig) {
var filter =
(i < inputsToSet.size())
? GenericStack.wrapInItemStack(inputsToSet.get(i))
: ItemStack.EMPTY;

slot.setFilterTo(filter);
i++;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.glodblock.github.extendedae.xmod.jei;
import appeng.api.upgrades.IUpgradeableObject;
import appeng.client.integrations.jei.GenericEntryStackHelper;
import appeng.client.integrations.jei.transfer.AbstractTransferHandler;
import appeng.menu.implementations.UpgradeableMenu;

import com.glodblock.github.extendedae.util.FakeSlotTransferHelper;
import mezz.jei.api.gui.ingredient.IRecipeSlotsView;
import mezz.jei.api.recipe.transfer.IRecipeTransferError;
import mezz.jei.api.recipe.transfer.IRecipeTransferHandlerHelper;
import mezz.jei.api.recipe.transfer.IUniversalRecipeTransferHandler;

import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.MenuType;


import java.util.*;

import javax.annotation.Nullable;
// Note: this class is also in a PR to base AE2 at https://github.com/AppliedEnergistics/Applied-Energistics-2/pull/8915
// Should that PR be accepted, it will no longer be necessary in ExtendedAE.
public class FakeSlotTransferHandler<T extends UpgradeableMenu<? extends IUpgradeableObject>>
extends AbstractTransferHandler implements IUniversalRecipeTransferHandler<T> {

private final Class<T> menuClass;

public FakeSlotTransferHandler(MenuType<T> menuType, Class<T> menuClass, IRecipeTransferHandlerHelper helper){

this.menuClass = menuClass;

}
@Nullable
@Override
public IRecipeTransferError transferRecipe(T menu, Object recipeBase, IRecipeSlotsView slotsView, Player player, boolean maxTransfer, boolean doTransfer){
if (doTransfer) {
var recipeInputs = GenericEntryStackHelper.ofInputs(slotsView);
FakeSlotTransferHelper<T> helper = new FakeSlotTransferHelper<>();
helper.transfer(menu, recipeInputs);
}
return null;
}

// Returning empty means the handler will not lock itself to a single MenuType from any given container class.
@Override
public Optional<MenuType<T>> getMenuType() {
return Optional.empty();
}

@Override
public Class<? extends T> getContainerClass() {
return menuClass;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@
import com.glodblock.github.extendedae.client.gui.GuiCrystalAssembler;
import com.glodblock.github.extendedae.client.gui.pattern.GuiPattern;
import com.glodblock.github.extendedae.common.EAESingletons;
import com.glodblock.github.extendedae.container.ContainerExInterface;
import com.glodblock.github.extendedae.container.ContainerExIOBus;
import com.glodblock.github.extendedae.container.ContainerThresholdExportBus;
import com.glodblock.github.extendedae.container.ContainerPreciseExportBus;
import com.glodblock.github.extendedae.container.ContainerPreciseStorageBus;
import com.glodblock.github.extendedae.container.pattern.ContainerPattern;
import com.glodblock.github.extendedae.recipe.CircuitCutterRecipe;
import com.glodblock.github.extendedae.recipe.CrystalAssemblerRecipe;
Expand All @@ -28,6 +33,7 @@
import mezz.jei.api.registration.IRecipeCatalystRegistration;
import mezz.jei.api.registration.IRecipeCategoryRegistration;
import mezz.jei.api.registration.IRecipeRegistration;
import mezz.jei.api.registration.IRecipeTransferRegistration;
import mezz.jei.api.runtime.IClickableIngredient;
import mezz.jei.api.runtime.IJeiRuntime;
import net.minecraft.client.Minecraft;
Expand Down Expand Up @@ -137,6 +143,27 @@ public void registerGuiHandlers(@NotNull IGuiHandlerRegistration registry) {
);
}

@Override
public void registerRecipeTransferHandlers(@NotNull IRecipeTransferRegistration registration) {
var helper = registration.getTransferHelper();

registration.addUniversalRecipeTransferHandler(
new FakeSlotTransferHandler<>(
ContainerExInterface.TYPE, ContainerExInterface.class, helper));
registration.addUniversalRecipeTransferHandler(
new FakeSlotTransferHandler<>(
ContainerExIOBus.EXPORT_TYPE, ContainerExIOBus.class, helper));
registration.addUniversalRecipeTransferHandler(
new FakeSlotTransferHandler<>(
ContainerThresholdExportBus.TYPE, ContainerThresholdExportBus.class, helper));
registration.addUniversalRecipeTransferHandler(
new FakeSlotTransferHandler<>(
ContainerPreciseExportBus.TYPE, ContainerPreciseExportBus.class, helper));
registration.addUniversalRecipeTransferHandler(
new FakeSlotTransferHandler<>(
ContainerPreciseStorageBus.TYPE, ContainerPreciseStorageBus.class, helper));
}

private <I extends RecipeInput, T extends Recipe<@NotNull I>> List<RecipeHolder<@NotNull T>> getRecipes(RecipeType<@NotNull T> type) {
var recipes = AppEngClient.instance().getRecipeMapForType(Minecraft.getInstance().level, type);
return List.copyOf(recipes.byType(type));
Expand Down
7 changes: 6 additions & 1 deletion xmod/module/emi/EMIPlugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,12 @@ public void register(EmiRegistry registry) {
registry.addCategory(EMICrystalFixerRecipe.CATEGORY);
registry.addWorkstation(EMICrystalFixerRecipe.CATEGORY, EmiStack.of(EAESingletons.CRYSTAL_FIXER));
adaptRecipeType(registry, CrystalFixerRecipe.TYPE, EMICrystalFixerRecipe::new);

registry.addRecipeHandler(ContainerExInterface.TYPE, new FakeSlotTransferHandler<>());
registry.addRecipeHandler(ContainerExInterface.TYPE_OVERSIZE, new FakeSlotTransferHandler<>());
registry.addRecipeHandler(ContainerExIOBus.EXPORT_TYPE, new FakeSlotTransferHandler<>());
registry.addRecipeHandler(ContainerPreciseExportBus.TYPE, new FakeSlotTransferHandler<>());
registry.addRecipeHandler(ContainerPreciseStorageBus.TYPE, new FakeSlotTransferHandler<>());
registry.addRecipeHandler(ContainerThresholdExportBus.TYPE, new FakeSlotTransferHandler<>());
addInfo(registry, EAESingletons.ENTRO_CRYSTAL, Component.translatable("emi.extendedae.desc.entro_crystal"));
addInfo(registry, EAESingletons.ENTRO_SEED, Component.translatable("emi.extendedae.desc.entro_seed"));
}
Expand Down
107 changes: 107 additions & 0 deletions xmod/module/emi/FakeSlotTransferHandler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package com.glodblock.github.extendedae.xmod.emi;

import appeng.api.stacks.GenericStack;
import appeng.api.upgrades.IUpgradeableObject;
import appeng.integration.modules.emi.EmiStackHelper;
import appeng.menu.implementations.UpgradeableMenu;

import com.glodblock.github.extendedae.util.FakeSlotTransferHelper;

import dev.emi.emi.api.recipe.EmiPlayerInventory;
import dev.emi.emi.api.recipe.EmiRecipe;
import dev.emi.emi.api.recipe.handler.EmiCraftContext;
import dev.emi.emi.api.recipe.handler.EmiRecipeHandler;
import dev.emi.emi.api.widget.Widget;

import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
import net.minecraft.client.gui.screens.inventory.tooltip.ClientTooltipComponent;

import java.util.ArrayList;
import java.util.List;
// Note: this class is also in a PR to base AE2 at https://github.com/AppliedEnergistics/Applied-Energistics-2/pull/8915
// Should that PR be accepted, it will no longer be necessary in ExtendedAE.
public class FakeSlotTransferHandler<T extends UpgradeableMenu<? extends IUpgradeableObject>>
implements EmiRecipeHandler<T> {

/**
* @param screen
* @return An inventory with the stacks the player can use for crafting. Craftables can only
* ever be discovered if the inventory contains one of its ingredients. A changed inventory
* indicates that EMI needs to refresh craftables.
*/
@Override
public EmiPlayerInventory getInventory(AbstractContainerScreen<T> screen) {
return new EmiPlayerInventory(new ArrayList<>());
}

/**
* @param recipe
* @return Whether the handler is applicable for the provided recipe.
*/
@Override
public boolean supportsRecipe(EmiRecipe recipe) {
return true;
}

/**
* @param recipe
* @return Whether the recipe should always display the ability to be filled if supported by
* this handler. When returning true, the recipe screen will always display a grayed out
* fill button in all contexts. Useful for recipe handlers which support nearly every
* recipe, and do not want to pollute the recipe screen.
*/
@Override
public boolean alwaysDisplaySupport(EmiRecipe recipe) {
return true;
}

/**
* @param recipe
* @param context
* @return The tooltip describing status for crafting the recipe
*/
@Override
public List<ClientTooltipComponent> getTooltip(EmiRecipe recipe, EmiCraftContext<T> context) {
return EmiRecipeHandler.super.getTooltip(recipe, context);
}

/**
* Render feedback about the status of the current fill. Common use is to render an overlay on
* missing ingredients
*
* @param recipe
* @param context
* @param widgets
* @param draw
*/
@Override
public void render(
EmiRecipe recipe, EmiCraftContext<T> context, List<Widget> widgets, GuiGraphics draw) {
EmiRecipeHandler.super.render(recipe, context, widgets, draw);
}

/**
* @param recipe
* @param context
* @return Whether the handler can craft the provided recipe with the given context
*/
@Override
public boolean canCraft(EmiRecipe recipe, EmiCraftContext<T> context) {
return true;
}

/**
* @param recipe
* @param context
* @return Whether the craft was successful
*/
@Override
public boolean craft(EmiRecipe recipe, EmiCraftContext<T> context) {
T menu = context.getScreen().getMenu();
List<List<GenericStack>> recipeInputs = EmiStackHelper.ofInputs(recipe);
FakeSlotTransferHelper<T> helper = new FakeSlotTransferHelper<>();
helper.transfer(menu, recipeInputs);
return true;
}
}