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
2 changes: 1 addition & 1 deletion dependencies.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
dependencies {
// Published dependencies
api("codechicken:codechickenlib:3.2.3.358")
api("com.cleanroommc:modularui:3.0.4") { transitive = false }
api("com.cleanroommc:modularui:3.0.6") { transitive = false }
api("com.cleanroommc:groovyscript:1.2.0-hotfix1") { transitive = false }
api("curse.maven:inventory-bogosorter-632327:7102721-deobf-6717233-sources-6717234") // Inventory BogoSorter − v1.5.0
api("curse.maven:key-binding-patch-928895:5951859") // Key Binding Patch v1.3.3.3, needed by Inventory BogoSorter v1.5.0+
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,6 @@ public MultiblockUIFactory addScreenChildren(ScreenFunction function) {
*/
public @NotNull ModularPanel buildUI(PosGuiData guiData, PanelSyncManager panelSyncManager) {
var panel = GTGuis.createPanel(mte, width, height)
.debugName("root_panel")
.childIf(!disableDisplay, () -> createScreen(panelSyncManager));

// TODO createExtras() hook for overrides?
Expand Down
104 changes: 103 additions & 1 deletion src/main/java/gregtech/api/mui/GregTechGuiScreen.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,35 @@
package gregtech.api.mui;

import gregtech.api.GTValues;
import gregtech.client.ClientProxy;
import gregtech.integration.jei.JustEnoughItemsModule;

import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;

import com.cleanroommc.modularui.integration.recipeviewer.RecipeViewerRecipeTransferHandler;
import com.cleanroommc.modularui.screen.ModularPanel;
import com.cleanroommc.modularui.screen.ModularScreen;
import com.cleanroommc.modularui.value.sync.SyncHandler;
import com.cleanroommc.modularui.widget.Widget;
import it.unimi.dsi.fastutil.ints.Int2ObjectAVLTreeMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.IntComparators;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import mezz.jei.api.gui.IRecipeLayout;
import mezz.jei.api.recipe.transfer.IRecipeTransferError;
import org.jetbrains.annotations.NotNull;

import java.util.Map;

@SuppressWarnings("UnstableApiUsage")
@SideOnly(Side.CLIENT)
public class GregTechGuiScreen extends ModularScreen {
public class GregTechGuiScreen extends ModularScreen implements RecipeViewerRecipeTransferHandler {

// Stores lists of higher priority recipe receivers to the left of the tree
@SideOnly(Side.CLIENT)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The class is already annotated side only client, do we need to annotate this field as well?

private static final Int2ObjectMap<Map<String, IRecipeTransferReceiver>> registeredRecipeTransferReceivers = new Int2ObjectAVLTreeMap<>(
IntComparators.OPPOSITE_COMPARATOR);

public GregTechGuiScreen(ModularPanel mainPanel) {
this(mainPanel, GTGuiTheme.STANDARD);
Expand All @@ -27,4 +47,86 @@ public GregTechGuiScreen(String owner, ModularPanel mainPanel, String themeId) {
super(owner, mainPanel);
useTheme(themeId);
}

@Override
public void onClose() {
// Only clear all registered recipe receivers when the UI is truly closing, ie not just opening JEI over it.
if (ClientProxy.isGUIClosingPermanently) {
// Clear all registered recipe receivers on UI close, just in case.
registeredRecipeTransferReceivers.clear();
}
}

@Override
public IRecipeTransferError transferRecipe(IRecipeLayout recipeLayout, boolean maxTransfer, boolean simulate) {
// Receivers are sorted high to low on registration
for (Map<String, IRecipeTransferReceiver> subMap : registeredRecipeTransferReceivers.values()) {
for (IRecipeTransferReceiver receiver : subMap.values()) {
IRecipeTransferError result = receiver.receiveRecipe(recipeLayout, maxTransfer, simulate);
if (result != null && result.getType() == IRecipeTransferError.Type.INTERNAL) continue;
return result;
}
}

// No valid transfer handler was found
return JustEnoughItemsModule.transferHelper.createInternalError();
}

/**
* Register an {@link IRecipeTransferReceiver} to this screen. <br/>
* Recipe transfer handlers registered through this method will have a priority of {@code 0}. <br/>
* <b>Important:</b> ensure that you remove this handler with {@link #removeRecipeTransferHandler(String)} when it's
* disposed of! <br/>
* Remove it by calling {@link #removeRecipeTransferHandler(String)} from {@link Widget#dispose()} for widgets and
* {@link SyncHandler#dispose()} for sync handlers.
*
* @throws IllegalArgumentException if a receiver with the given key already exists.
*/
@SideOnly(Side.CLIENT)
public static void registerRecipeTransferHandler(@NotNull String key,
@NotNull IRecipeTransferReceiver transferReceiver) {
registerRecipeTransferHandler(key, transferReceiver, 0);
}

/**
* Register an {@link IRecipeTransferReceiver} to this screen with a certain priority. Higher numbers will be tried
* first. <br/>
* <b>Important:</b> ensure that you remove this handler with {@link #removeRecipeTransferHandler(String)} when it's
* disposed of! <br/>
* Remove it by calling {@link #removeRecipeTransferHandler(String)} from {@link Widget#dispose()} for widgets and
* {@link SyncHandler#dispose()} for sync handlers.
*
* @throws IllegalArgumentException if a receiver with the given key already exists.
*/
@SideOnly(Side.CLIENT)
public static void registerRecipeTransferHandler(@NotNull String key,
@NotNull IRecipeTransferReceiver transferReceiver,
int priority) {
for (Map<String, IRecipeTransferReceiver> subMap : registeredRecipeTransferReceivers.values()) {
if (subMap.containsKey(key)) {
throw new IllegalArgumentException(
"Tried to register a recipe transfer receiver to a key that's already used!");
}
}

registeredRecipeTransferReceivers.computeIfAbsent(priority, $ -> new Object2ObjectOpenHashMap<>())
.put(key, transferReceiver);
}

/**
* Remove a registered {@link IRecipeTransferReceiver} from this screen.
*
* @throws IllegalArgumentException if no receiver exists with the given key.
*/
@SideOnly(Side.CLIENT)
public static void removeRecipeTransferHandler(@NotNull String key) {
for (Map<String, IRecipeTransferReceiver> subMap : registeredRecipeTransferReceivers.values()) {
if (subMap.containsKey(key)) {
subMap.remove(key);
return;
}
}

throw new IllegalArgumentException("Tried to remove a recipe transfer receiver by a key that didn't exist!");
}
}
69 changes: 0 additions & 69 deletions src/main/java/gregtech/api/mui/GregTechGuiTransferHandler.java

This file was deleted.

53 changes: 53 additions & 0 deletions src/main/java/gregtech/api/mui/IRecipeTransferReceiver.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package gregtech.api.mui;

import gregtech.api.mui.sync.RecipeTransferSyncHandler;
import gregtech.integration.jei.JustEnoughItemsModule;

import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;

import com.cleanroommc.modularui.value.sync.SyncHandler;
import com.cleanroommc.modularui.widget.Widget;
import mezz.jei.api.gui.IRecipeLayout;
import mezz.jei.api.recipe.transfer.IRecipeTransferError;
import mezz.jei.api.recipe.transfer.IRecipeTransferHandlerHelper;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.Collection;

/**
* An interface for receiving a recipe from a recipe viewer to anything on the panel, such as a {@link Widget} or
* {@link SyncHandler}. <br/>
* Register it via {@link GregTechGuiScreen#registerRecipeTransferHandler(String, IRecipeTransferReceiver)} or
* {@link GregTechGuiScreen#registerRecipeTransferHandler(String, IRecipeTransferReceiver, int)} if you want to
* prioritize checking a certain handler first. <br/>
* If you're implementing this on a {@link SyncHandler}, it's recommended to extend {@link RecipeTransferSyncHandler}
* instead as registering and unregistering from {@link GregTechGuiScreen} is done for you.
*/
public interface IRecipeTransferReceiver {

/**
* Attempt or simulate transferring a recipe from a recipe viewer like JEI or HEI. <br/>
* A factory for default {@link IRecipeTransferError}s is available at {@link JustEnoughItemsModule#transferHelper}.
* There are three default options for errors: <br/>
* - {@link IRecipeTransferHandlerHelper#createInternalError()}: mark the recipe as invalid for transferring by
* graying out the + button. <br/>
* - {@link IRecipeTransferHandlerHelper#createUserErrorWithTooltip(String)}: the same as above, but also display a
* message when hovering over the + button. <br/>
* - {@link IRecipeTransferHandlerHelper#createUserErrorForSlots(String, Collection)}: the same as above, but
* additionally highlight certain slots in the recipe to, for example, mark missing ingredients. <b>Important: will
* throw {@link IllegalArgumentException} if the supplied {@link Collection} is empty!</b>
*
* @param recipeLayout the recipe layout that contains the recipe category, and the item and fluid stacks.
* @param maxTransfer if the receiver should try to move as many ingredients as possible to the crafting slots, ie
* shift clicking a recipe into a crafting table.
* @param simulate if this recipe should only simulate being transferred
* @return {@code null} if the transfer should succeed or an {@link IRecipeTransferError} if not. If there are
* multiple registered recipe transfer receivers on the same panel, returning an error with type
* {@link IRecipeTransferError.Type#INTERNAL} will skip this and attempt the next one.
*/
@Nullable
@SideOnly(Side.CLIENT)
IRecipeTransferError receiveRecipe(@NotNull IRecipeLayout recipeLayout, boolean maxTransfer, boolean simulate);
}
14 changes: 14 additions & 0 deletions src/main/java/gregtech/api/mui/sync/FixedIntArraySyncValue.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ public class FixedIntArraySyncValue extends ValueSyncHandler<int[]> {
private final Supplier<int[]> getter;
private final @Nullable Consumer<int[]> setter;

public FixedIntArraySyncValue(@NotNull Supplier<int[]> getter) {
this(getter, null);
}

public FixedIntArraySyncValue(@NotNull Supplier<int[]> getter, @Nullable Consumer<int[]> setter) {
this.getter = Objects.requireNonNull(getter);
this.setter = setter;
Expand Down Expand Up @@ -70,6 +74,11 @@ public boolean updateCacheFromSource(boolean isFirstSync) {
return false;
}

@Override
public void notifyUpdate() {
setValue(this.getter.get(), false, true);
}

@Override
public void write(@NotNull PacketBuffer buffer) throws IOException {
for (int i : cache) {
Expand All @@ -92,4 +101,9 @@ public int[] getValue() {
public int getValue(int index) {
return this.cache[index];
}

@Override
public Class<int[]> getValueType() {
return int[].class;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package gregtech.api.mui.sync;

import gregtech.api.mui.GregTechGuiScreen;
import gregtech.api.mui.IRecipeTransferReceiver;

import com.cleanroommc.modularui.value.sync.PanelSyncManager;
import com.cleanroommc.modularui.value.sync.SyncHandler;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.MustBeInvokedByOverriders;

/**
* A base class for to handle implementing {@link IRecipeTransferReceiver} on a {@link SyncHandler}s to automatically
* register and unregister it from the map of valid handlers in {@link GregTechGuiScreen}.
*/
public abstract class RecipeTransferSyncHandler extends SyncHandler implements IRecipeTransferReceiver {

@ApiStatus.OverrideOnly
@MustBeInvokedByOverriders
@Override
public void init(String key, PanelSyncManager syncManager) {
super.init(key, syncManager);
if (syncManager.isClient()) {
GregTechGuiScreen.registerRecipeTransferHandler(getKey(), this, getTransferHandlerPriority());
}
}

protected int getTransferHandlerPriority() {
return 0;
}

@ApiStatus.OverrideOnly
@MustBeInvokedByOverriders
@Override
public void dispose() {
if (getSyncManager().isClient()) {
GregTechGuiScreen.removeRecipeTransferHandler(getKey());
}
super.dispose();
}
}
8 changes: 8 additions & 0 deletions src/main/java/gregtech/client/ClientProxy.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.text.TextFormatting;
import net.minecraftforge.client.GuiIngameForge;
import net.minecraftforge.client.event.GuiOpenEvent;
import net.minecraftforge.client.event.ModelBakeEvent;
import net.minecraftforge.client.event.ModelRegistryEvent;
import net.minecraftforge.client.event.MouseEvent;
Expand Down Expand Up @@ -86,6 +87,8 @@
@Mod.EventBusSubscriber(Side.CLIENT)
public class ClientProxy extends CommonProxy {

public static boolean isGUIClosingPermanently = false;

public void onPreLoad() {
super.onPreLoad();

Expand Down Expand Up @@ -428,4 +431,9 @@ private static void renderToolbeltHotbar(GuiIngameForge gui, ItemStack stack, It
GlStateManager.disableBlend();
}
}

@SubscribeEvent
public static void onGuiChange(GuiOpenEvent event) {
isGUIClosingPermanently = (event.getGui() == null);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ public int getProgressBarCount() {

@Override
public void registerBars(List<UnaryOperator<TemplateBarBuilder>> bars, PanelSyncManager syncManager) {
FixedIntArraySyncValue fuelValue = new FixedIntArraySyncValue(this::getFuelAmount, null);
FixedIntArraySyncValue fuelValue = new FixedIntArraySyncValue(this::getFuelAmount);
syncManager.syncValue("fuel_amount", fuelValue);
StringSyncValue fuelNameValue = new StringSyncValue(() -> {
FluidStack stack = ((MultiblockFuelRecipeLogic) recipeMapWorkable).getInputFluidStack();
Expand All @@ -264,9 +264,9 @@ public void registerBars(List<UnaryOperator<TemplateBarBuilder>> bars, PanelSync
return fluid.getName();
});
syncManager.syncValue("fuel_name", fuelNameValue);
FixedIntArraySyncValue lubricantValue = new FixedIntArraySyncValue(this::getLubricantAmount, null);
FixedIntArraySyncValue lubricantValue = new FixedIntArraySyncValue(this::getLubricantAmount);
syncManager.syncValue("lubricant_amount", lubricantValue);
FixedIntArraySyncValue oxygenValue = new FixedIntArraySyncValue(this::getOxygenAmount, null);
FixedIntArraySyncValue oxygenValue = new FixedIntArraySyncValue(this::getOxygenAmount);
syncManager.syncValue("oxygen_amount", oxygenValue);
BooleanSyncValue boostValue = new BooleanSyncValue(this::isBoostAllowed);
syncManager.syncValue("boost_allowed", boostValue);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ public int getProgressBarCount() {

@Override
public void registerBars(List<UnaryOperator<TemplateBarBuilder>> bars, PanelSyncManager syncManager) {
FixedIntArraySyncValue fuelValue = new FixedIntArraySyncValue(this::getFuelAmount, null);
FixedIntArraySyncValue fuelValue = new FixedIntArraySyncValue(this::getFuelAmount);
StringSyncValue fuelNameValue = new StringSyncValue(() -> {
FluidStack stack = ((MultiblockFuelRecipeLogic) recipeMapWorkable).getInputFluidStack();
if (stack == null) {
Expand Down
Loading
Loading