Skip to content

Commit

Permalink
Add REI support
Browse files Browse the repository at this point in the history
  • Loading branch information
rubensworks committed Oct 22, 2024
1 parent f28ca5d commit 048a631
Show file tree
Hide file tree
Showing 8 changed files with 347 additions and 3 deletions.
10 changes: 9 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ repositories {
name "EMI"
url "https://maven.terraformersmc.com/"
}
maven {
name "REI"
url "https://maven.shedaniel.me/"
}
}

dependencies {
Expand Down Expand Up @@ -144,7 +148,11 @@ dependencies {
runtimeOnly fg.deobf("top.theillusivec4.curios:curios-forge:${project.curios_version}") // https://maven.theillusivec4.top/top/theillusivec4/curios/curios-forge/
compileOnly fg.deobf("top.theillusivec4.curios:curios-forge:${project.curios_version}:api")

implementation fg.deobf("dev.emi:emi-forge:${emi_version}")
compileOnly fg.deobf("dev.emi:emi-forge:${emi_version}") // https://maven.terraformersmc.com/

compileOnly fg.deobf("me.shedaniel:RoughlyEnoughItems-forge:${rei_version}") // https://maven.shedaniel.me/me/shedaniel/RoughlyEnoughItems-forge/
implementation fg.deobf("me.shedaniel.cloth:cloth-config-forge:$cloth_config_version");
implementation fg.deobf("dev.architectury:architectury-forge:$architectury_version");
}

minecraft {
Expand Down
5 changes: 4 additions & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
mod_version=1.0.0
minecraft_version=1.19.2
forge_version=43.0.8
forge_version=43.4.4
cyclopscore_version=1.19.4-495
release_type=release
fingerprint=bd0353b3e8a2810d60dd584e256e364bc3bedd44
Expand All @@ -11,6 +11,9 @@ commoncapabilities_version=1.19.2-2.9.0-88
curios_version=1.19.2-5.1.1.0
jei_version=1.19.2-forge:11.5.2.1007
emi_version=1.1.16+1.19.2
rei_version=9.2.779
cloth_config_version=8.3.134
architectury_version=6.6.92

# Workaround for Spotless bug
# https://github.com/diffplug/spotless/issues/834
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public RecipeTransferResult(Collection<T> slotsMissing, Collection<T> slotsCraft
} else if (slotsMissing.isEmpty()) {
// Missing items, but they are all craftable
this.message.add(Component.translatable("gui.integratedterminalscompat.terminal_storage.jei.transfer.craftable").withStyle(ChatFormatting.RED));
this.message.add(Component.translatable("gui.integratedterminalscompat.terminal_storage.jei.transfer.craft.info").withStyle(ChatFormatting.ITALIC));
this.message.add(Component.translatable(HAS_CMD ? "gui.integratedterminalscompat.terminal_storage.jei.transfer.craft.info_cmd" : "gui.integratedterminalscompat.terminal_storage.jei.transfer.craft.info").withStyle(ChatFormatting.ITALIC));
this.color = HIGHLIGHT_COLOR_CRAFTABLE;
} else if (!slotsCraftable.isEmpty()) {
// Missing items, but only some of them are craftable
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package org.cyclops.integratedterminalscompat.modcompat.rei;

import com.google.common.collect.Lists;
import me.shedaniel.rei.api.common.entry.EntryIngredient;
import net.minecraft.world.item.ItemStack;
import org.cyclops.integratedterminalscompat.modcompat.common.RecipeInputSlot;
import org.jetbrains.annotations.NotNull;

import java.util.Iterator;

/**
* @author rubensworks
*/
public class RecipeInputSlotRei implements RecipeInputSlot {

private final EntryIngredient ingredient;
private final boolean input;
private final int index;

public RecipeInputSlotRei(EntryIngredient ingredient, boolean input, int index) {
this.ingredient = ingredient;
this.input = input;
this.index = index;
}

public int getIndex() {
return index;
}

@Override
public boolean isEmpty() {
return !this.input || this.ingredient.isEmpty();
}

@NotNull
@Override
public Iterator<ItemStack> iterator() {
if (!this.input) {
return Lists.<ItemStack>newArrayList().iterator();
}
return this.ingredient.stream()
.map(i -> i.<ItemStack>castValue())
.iterator();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package org.cyclops.integratedterminalscompat.modcompat.rei;

import me.shedaniel.rei.api.client.REIRuntime;
import me.shedaniel.rei.api.client.plugins.REIClientPlugin;
import me.shedaniel.rei.api.client.registry.screen.ScreenRegistry;
import me.shedaniel.rei.api.client.registry.transfer.TransferHandlerRegistry;
import me.shedaniel.rei.api.common.entry.comparison.ItemComparatorRegistry;
import me.shedaniel.rei.forge.REIPluginClient;
import net.minecraft.client.Minecraft;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.client.event.ScreenEvent;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import org.cyclops.commoncapabilities.api.capability.itemhandler.ItemMatch;
import org.cyclops.cyclopscore.client.gui.component.input.WidgetTextFieldExtended;
import org.cyclops.integratedterminals.api.terminalstorage.event.TerminalStorageScreenSizeEvent;
import org.cyclops.integratedterminals.api.terminalstorage.event.TerminalStorageTabClientLoadButtonsEvent;
import org.cyclops.integratedterminals.api.terminalstorage.event.TerminalStorageTabClientSearchFieldUpdateEvent;
import org.cyclops.integratedterminals.client.gui.container.ContainerScreenTerminalStorage;
import org.cyclops.integratedterminalscompat.modcompat.common.button.TerminalButtonItemStackCraftingGridSearchSync;
import org.cyclops.integratedterminalscompat.modcompat.rei.terminalstorage.TerminalStorageReiFocusedStackProvider;
import org.cyclops.integratedterminalscompat.modcompat.rei.terminalstorage.TerminalStorageReiTransferHandler;

/**
* @author rubensworks
*/
@REIPluginClient
public class ReiIntegratedTerminalsConfig implements REIClientPlugin {

private static ItemComparatorRegistry itemComparatorRegistry;

public static int getItemStackMatchCondition(ItemStack itemStack) {
// By default, REI ignores NBT when matching items, unless sub type info is set.

// Ideally, we would have to do a plain item extraction, and filter the items that match the subtype string,
// but just using the heuristic that the existence of sub type info implies NBT matching seems to work out so far.
// So if we would run into problems with this, this filtering is what we'd need to do.

return !itemComparatorRegistry.containsComparator(itemStack.getItem()) ? ItemMatch.ITEM : ItemMatch.ITEM | ItemMatch.TAG;
}

private boolean loaded = false;
private boolean wasReiVisible = false;

public ReiIntegratedTerminalsConfig() {
MinecraftForge.EVENT_BUS.register(this);
}

@Override
public void registerItemComparators(ItemComparatorRegistry registry) {
itemComparatorRegistry = registry;
}

@Override
public void registerScreens(ScreenRegistry registry) {
loaded = true;
registry.registerFocusedStack(new TerminalStorageReiFocusedStackProvider());
}

@Override
public void registerTransferHandlers(TransferHandlerRegistry registry) {
registry.register(new TerminalStorageReiTransferHandler());
}

@SubscribeEvent
public void onTerminalStorageButtons(TerminalStorageTabClientLoadButtonsEvent event) {
if (this.loaded && !event.getButtons().stream()
.anyMatch((button) -> button instanceof TerminalButtonItemStackCraftingGridSearchSync)) {
event.getButtons().add(new TerminalButtonItemStackCraftingGridSearchSync(
"rei", event.getContainer().getGuiState(), event.getClientTab()));
}
}

@SubscribeEvent
public void onTerminalStorageScreenSize(TerminalStorageScreenSizeEvent event) {
if (this.loaded) {
try {
boolean isOpen = REIRuntime.getInstance().getOverlay().isPresent();
boolean wasJeiVisiblePrevious = wasReiVisible;
if (isOpen) {
wasReiVisible = true;
event.setWidth(event.getWidth() - 170);
} else {
wasReiVisible = false;
}

// Re-init screen if JEI was just made (in)visible
if (wasJeiVisiblePrevious != wasReiVisible) {
((ContainerScreenTerminalStorage) Minecraft.getInstance().screen).init();
}
} catch (NoClassDefFoundError | ClassCastException e) {
// Do nothing when we detect some JEI API issues
}
}
}

@SubscribeEvent
public void onSearchFieldUpdated(TerminalStorageTabClientSearchFieldUpdateEvent event) {
// Copy the terminal search box contents into the JEI search box.
if (REIRuntime.getInstance().getOverlay().isPresent() && TerminalButtonItemStackCraftingGridSearchSync.isSearchSynced(event.getClientTab())) {
REIRuntime.getInstance().getSearchTextField().setText(event.getSearchString() + "");
}
}

@SubscribeEvent
public void onKeyTyped(ScreenEvent.KeyReleased.Post event) {
// Copy the JEI search box contents into the terminal search box.
if (event.getScreen() instanceof ContainerScreenTerminalStorage) {
ContainerScreenTerminalStorage<?, ?> gui = ((ContainerScreenTerminalStorage<?, ?>) event.getScreen());
if (REIRuntime.getInstance().getOverlay().isPresent() && REIRuntime.getInstance().getSearchTextField().isFocused()) {
gui.getSelectedClientTab().ifPresent(tab -> {
if (TerminalButtonItemStackCraftingGridSearchSync.isSearchSynced(tab)) {
WidgetTextFieldExtended fieldSearch = gui.getFieldSearch();
fieldSearch.setValue(REIRuntime.getInstance().getSearchTextField().getText());
tab.setInstanceFilter(gui.getMenu().getSelectedChannel(), fieldSearch.getValue() + "");
}
});
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package org.cyclops.integratedterminalscompat.modcompat.rei.terminalstorage;

import dev.architectury.event.CompoundEventResult;
import me.shedaniel.math.Point;
import me.shedaniel.rei.api.client.registry.screen.FocusedStackProvider;
import me.shedaniel.rei.api.common.entry.EntryStack;
import me.shedaniel.rei.api.common.util.EntryStacks;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.fluids.FluidStack;
import org.cyclops.integratedterminals.client.gui.container.ContainerScreenTerminalStorage;
import org.cyclops.integratedterminals.core.terminalstorage.TerminalStorageTabIngredientComponentClient;

import java.util.Optional;

/**
* @author rubensworks
*/
public class TerminalStorageReiFocusedStackProvider implements FocusedStackProvider {
@Override
public CompoundEventResult<EntryStack<?>> provide(Screen screen, Point point) {
if (screen instanceof ContainerScreenTerminalStorage containerScreen) {
return createClickableIngredient(containerScreen, point.x, point.y);
}
return CompoundEventResult.pass();
}

private <T> CompoundEventResult<EntryStack<?>> createClickableIngredient(
ContainerScreenTerminalStorage<T, ?> containerScreen, int mouseX, int mouseY) {
int slotIndex = containerScreen.getStorageSlotIndexAtPosition(mouseX, mouseY);
@SuppressWarnings("unchecked") // Cast is safe due to filter
Optional<TerminalStorageTabIngredientComponentClient<T, ?>> tabOptional = containerScreen.getSelectedClientTab()
.filter(TerminalStorageTabIngredientComponentClient.class::isInstance)
.map(TerminalStorageTabIngredientComponentClient.class::cast);
if(slotIndex >= 0 && tabOptional.isPresent()) {
TerminalStorageTabIngredientComponentClient<T, ?> tab = tabOptional.get();
int channel = containerScreen.getMenu().getSelectedChannel();
Optional<T> instanceOptional = tab.getSlotInstance(channel, slotIndex);
return instanceOptional
.map(instance -> CompoundEventResult.interruptTrue(instance instanceof ItemStack itemStack ? EntryStacks.of(itemStack) : (instance instanceof FluidStack fluidStack ? EntryStacks.of(fluidStack.getFluid(), fluidStack.getAmount()) : EntryStack.empty())))
.orElse(CompoundEventResult.pass());
}
return CompoundEventResult.pass();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package org.cyclops.integratedterminalscompat.modcompat.rei.terminalstorage;

import com.google.common.collect.Lists;
import me.shedaniel.math.Rectangle;
import me.shedaniel.rei.api.client.gui.widgets.Slot;
import me.shedaniel.rei.api.client.gui.widgets.Widget;
import me.shedaniel.rei.api.client.registry.transfer.TransferHandler;
import me.shedaniel.rei.api.common.entry.EntryIngredient;
import me.shedaniel.rei.plugin.common.BuiltinPlugin;
import me.shedaniel.rei.plugin.common.displays.crafting.DefaultCraftingDisplay;
import net.minecraft.client.gui.GuiComponent;
import net.minecraft.network.chat.Component;
import org.cyclops.integratedterminals.api.terminalstorage.ITerminalStorageTabCommon;
import org.cyclops.integratedterminals.core.terminalstorage.TerminalStorageTabIngredientComponentClient;
import org.cyclops.integratedterminals.core.terminalstorage.TerminalStorageTabIngredientComponentItemStackCrafting;
import org.cyclops.integratedterminals.core.terminalstorage.TerminalStorageTabIngredientComponentItemStackCraftingCommon;
import org.cyclops.integratedterminals.inventory.container.ContainerTerminalStorageBase;
import org.cyclops.integratedterminalscompat.modcompat.common.RecipeTransferHelpers;
import org.cyclops.integratedterminalscompat.modcompat.common.RecipeTransferResult;
import org.cyclops.integratedterminalscompat.modcompat.rei.RecipeInputSlotRei;
import org.cyclops.integratedterminalscompat.modcompat.rei.ReiIntegratedTerminalsConfig;

import java.util.Collection;
import java.util.Objects;

/**
* @author rubensworks
*/
public class TerminalStorageReiTransferHandler implements TransferHandler {

private int previousChangeId;

@Override
public Result handle(Context context) {
if (context.getDisplay().getCategoryIdentifier().equals(BuiltinPlugin.CRAFTING) &&
context.getDisplay() instanceof DefaultCraftingDisplay<?> displayCrafting &&
context.getMenu() instanceof ContainerTerminalStorageBase<?> container &&
Objects.equals(container.getSelectedTab(), TerminalStorageTabIngredientComponentItemStackCrafting.NAME.toString())) {
ITerminalStorageTabCommon tabCommon = container.getTabCommon(container.getSelectedTab());
TerminalStorageTabIngredientComponentItemStackCraftingCommon tabCommonCrafting =
(TerminalStorageTabIngredientComponentItemStackCraftingCommon) tabCommon;

if (context.isActuallyCrafting()) {
RecipeTransferHelpers.transferRecipe(
container,
getRecipeInputSlots(displayCrafting),
context.getMinecraft().player,
tabCommonCrafting,
ReiIntegratedTerminalsConfig::getItemStackMatchCondition,
context.isStackedCrafting()
);
return Result.createSuccessful().blocksFurtherHandling();
} else {
TerminalStorageTabIngredientComponentClient tabClient = (TerminalStorageTabIngredientComponentClient)
container.getTabClient(container.getSelectedTab());
return RecipeTransferHelpers.getMissingItems(
displayCrafting.getOptionalRecipe().orElse(null),
container,
getRecipeInputSlots(displayCrafting),
context.getMinecraft().player,
tabCommonCrafting,
tabClient,
ReiIntegratedTerminalsConfig::getItemStackMatchCondition,
() -> previousChangeId,
id -> previousChangeId = id
).map(transferResult -> Result.createSuccessful()
.color(transferResult.getButtonHighlightColor())
.tooltip(transferResult.getMessage().stream().reduce(Component.empty(), (c1, c2) -> c1.copy().append("\n").append(c2)))
.renderer((poseStack, mouseX, mouseY, v, widgets, rectangle, display) -> {
int index = 0;
for (Widget widget : widgets) {
if (widget instanceof Slot widgetSlot && widgetSlot.getNoticeMark() == Slot.INPUT) {
// Determine if this slot requires any highlighting
int color = -1;
for (RecipeInputSlotRei slot : transferResult.getSlotsMissing()) {
if (slot.getIndex() == index) {
color = RecipeTransferResult.SLOT_COLOR_MISSING;
break;
}
}
for (RecipeInputSlotRei slot : transferResult.getSlotsCraftable()) {
if (slot.getIndex() == index) {
color = RecipeTransferResult.SLOT_COLOR_CRAFTABLE;
break;
}
}

// Draw the highlight
if (color != -1) {
Rectangle bounds = widgetSlot.getInnerBounds();
poseStack.pushPose();
poseStack.translate(0, 0, 20);
GuiComponent.fill(poseStack, bounds.x, bounds.y, bounds.getMaxX(), bounds.getMaxY(), color);
poseStack.popPose();
}

index++;
}
}

})
.blocksFurtherHandling())
.orElse(Result.createSuccessful().blocksFurtherHandling());
}
}
return Result.createNotApplicable();
}

private Collection<RecipeInputSlotRei> getRecipeInputSlots(DefaultCraftingDisplay<?> context) {
Collection<RecipeInputSlotRei> recipeInputSlots = Lists.newArrayList();
for (EntryIngredient outputEntry : context.getOutputEntries()) {
recipeInputSlots.add(new RecipeInputSlotRei(outputEntry, false, 0));
}
int i = 0;
for (EntryIngredient outputEntry : context.getOrganisedInputEntries(3, 3)) {
recipeInputSlots.add(new RecipeInputSlotRei(outputEntry, true, i++));
}
return recipeInputSlots;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
"gui.integratedterminalscompat.terminal_storage.craftinggrid.jeisync.info": "Synchronize search box with JEI.",
"gui.integratedterminalscompat.terminal_storage.craftinggrid.emisync": "EMI Search Sync",
"gui.integratedterminalscompat.terminal_storage.craftinggrid.emisync.info": "Synchronize search box with EMI.",
"gui.integratedterminalscompat.terminal_storage.craftinggrid.reisync": "REI Search Sync",
"gui.integratedterminalscompat.terminal_storage.craftinggrid.reisync.info": "Synchronize search box with REI.",
"gui.integratedterminalscompat.terminal_storage.jei.transfer.craftable": "Auto-craftable items",
"gui.integratedterminalscompat.terminal_storage.jei.transfer.craftable_partial": "Partially auto-craftable items",
"gui.integratedterminalscompat.terminal_storage.jei.transfer.missing": "Missing items",
Expand Down

0 comments on commit 048a631

Please sign in to comment.