Kaynağa Gözat

Basic cheating / deleting

Unknown 6 yıl önce
ebeveyn
işleme
41babce707

+ 1 - 1
build.gradle

@@ -6,7 +6,7 @@ sourceCompatibility = 1.8
 targetCompatibility = 1.8
 
 archivesBaseName = "RoughlyEnoughItems"
-version = "2.0-22"
+version = "2.0-23"
 
 minecraft {
 }

+ 50 - 10
src/main/java/me/shedaniel/rei/RoughlyEnoughItemsCore.java

@@ -1,27 +1,48 @@
 package me.shedaniel.rei;
 
+import me.shedaniel.rei.client.ClientHelper;
 import me.shedaniel.rei.listeners.ClientTick;
 import me.shedaniel.rei.listeners.IListener;
 import net.fabricmc.api.ClientModInitializer;
 import net.fabricmc.api.ModInitializer;
 import net.fabricmc.fabric.events.client.ClientTickEvent;
+import net.fabricmc.fabric.networking.CustomPayloadPacketRegistry;
+import net.minecraft.item.ItemStack;
+import net.minecraft.server.network.ServerPlayNetworkHandler;
+import net.minecraft.server.network.ServerPlayerEntity;
+import net.minecraft.sortme.ChatMessageType;
+import net.minecraft.text.TranslatableTextComponent;
+import net.minecraft.util.Identifier;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
 
 import java.util.ArrayList;
 import java.util.List;
 import java.util.stream.Collectors;
 
-public class RoughlyEnoughItemsCore implements ClientModInitializer {
+public class RoughlyEnoughItemsCore implements ClientModInitializer, ModInitializer {
     
-    private static List<IListener> listeners = new ArrayList<>();
+    public static final Logger LOGGER = LogManager.getFormatterLogger("REI");
+    public static final Identifier DELETE_ITEMS_PACKET = new Identifier("roughlyenoughitems", "deleteitem");
+    public static final Identifier CREATE_ITEMS_PACKET = new Identifier("roughlyenoughitems", "createitem");
+    private static final List<IListener> listeners = new ArrayList<>();
+    
+    public static <T> List<T> getListeners(Class<T> listenerClass) {
+        return listeners.stream().filter(listener -> {
+            return listenerClass.isAssignableFrom(listener.getClass());
+        }).map(listener -> {
+            return listenerClass.cast(listener);
+        }).collect(Collectors.toList());
+    }
     
     @Override
     public void onInitializeClient() {
-        registerFabricEvents();
         registerREIListeners();
+        registerFabricEvents();
     }
     
     private void registerREIListeners() {
-    
+        registerListener(new ClientHelper());
     }
     
     private IListener registerListener(IListener listener) {
@@ -29,12 +50,11 @@ public class RoughlyEnoughItemsCore implements ClientModInitializer {
         return listener;
     }
     
-    public static <T> List<T> getListeners(Class<T> listenerClass) {
-        return listeners.stream().filter(listener -> {
-            return listenerClass.isAssignableFrom(listener.getClass());
-        }).map(listener -> {
-            return listenerClass.cast(listener);
-        }).collect(Collectors.toList());
+    private boolean removeListener(IListener listener) {
+        if (!listeners.contains(listener))
+            return false;
+        listeners.remove(listener);
+        return true;
     }
     
     private void registerFabricEvents() {
@@ -43,4 +63,24 @@ public class RoughlyEnoughItemsCore implements ClientModInitializer {
         });
     }
     
+    @Override
+    public void onInitialize() {
+        registerFabricPackets();
+    }
+    
+    private void registerFabricPackets() {
+        CustomPayloadPacketRegistry.SERVER.register(DELETE_ITEMS_PACKET, (packetContext, packetByteBuf) -> {
+            ServerPlayerEntity player = (ServerPlayerEntity) packetContext.getPlayer();
+            if (!player.inventory.getCursorStack().isEmpty())
+                player.inventory.setCursorStack(ItemStack.EMPTY);
+        });
+        CustomPayloadPacketRegistry.SERVER.register(CREATE_ITEMS_PACKET, (packetContext, packetByteBuf) -> {
+            ServerPlayerEntity player = (ServerPlayerEntity) packetContext.getPlayer();
+            ItemStack stack = packetByteBuf.readItemStack();
+            if (player.inventory.insertStack(stack.copy()))
+                player.sendChatMessage(new TranslatableTextComponent("text.rei.cheat_items", stack.getDisplayName().getFormattedText(), stack.getAmount(), player.getEntityName()), ChatMessageType.SYSTEM);
+            else player.sendChatMessage(new TranslatableTextComponent("text.rei.failed_cheat_items"), ChatMessageType.SYSTEM);
+        });
+    }
+    
 }

+ 124 - 0
src/main/java/me/shedaniel/rei/client/ClientHelper.java

@@ -0,0 +1,124 @@
+package me.shedaniel.rei.client;
+
+import com.google.common.collect.Lists;
+import io.netty.buffer.Unpooled;
+import me.shedaniel.rei.RoughlyEnoughItemsCore;
+import me.shedaniel.rei.listeners.ClientLoaded;
+import net.fabricmc.api.ClientModInitializer;
+import net.fabricmc.loader.FabricLoader;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.Mouse;
+import net.minecraft.enchantment.Enchantment;
+import net.minecraft.enchantment.EnchantmentHelper;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+import net.minecraft.item.Items;
+import net.minecraft.server.network.packet.CustomPayloadServerPacket;
+import net.minecraft.util.Identifier;
+import net.minecraft.util.PacketByteBuf;
+import net.minecraft.util.registry.Registry;
+
+import java.awt.*;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class ClientHelper implements ClientLoaded, ClientModInitializer {
+    
+    private static List<ItemStack> itemList;
+    private static boolean cheating;
+    
+    public ClientHelper() {
+        this.itemList = Lists.newLinkedList();
+    }
+    
+    public static String getModFromItemStack(ItemStack stack) {
+        if (!stack.isEmpty()) {
+            Identifier location = Registry.ITEM.getId(stack.getItem());
+            assert location != null;
+            String modid = location.getNamespace();
+            if (modid.equalsIgnoreCase("minecraft"))
+                return "Minecraft";
+            return FabricLoader.INSTANCE.getModContainers().stream()
+                    .map(modContainer -> {
+                        return modContainer.getInfo();
+                    })
+                    .filter(modInfo -> modInfo.getId().equals(modid) || (modInfo.getName() != null && modInfo.getName().equals(modid)))
+                    .findFirst().map(modInfo -> {
+                        if (modInfo.getName() != null)
+                            return modInfo.getName();
+                        return modid;
+                    }).orElse(modid);
+        }
+        return "";
+    }
+    
+    public static List<ItemStack> getItemList() {
+        return itemList;
+    }
+    
+    public static Point getMouseLocation() {
+        MinecraftClient client = MinecraftClient.getInstance();
+        Mouse mouse = client.mouse;
+        double double_1 = mouse.getX() * (double) client.window.getScaledWidth() / (double) client.window.method_4480();
+        double double_2 = mouse.getY() * (double) client.window.getScaledHeight() / (double) client.window.method_4507();
+        return new Point((int) double_1, (int) double_2);
+    }
+    
+    public static boolean isCheating() {
+        return cheating;
+    }
+    
+    public static void sendDeletePacket() {
+        PacketByteBuf buf = new PacketByteBuf(Unpooled.buffer());
+        MinecraftClient.getInstance().getNetworkHandler().sendPacket(new CustomPayloadServerPacket(RoughlyEnoughItemsCore.DELETE_ITEMS_PACKET, buf));
+    }
+    
+    public static boolean tryCheatingStack(ItemStack cheatedStack) {
+        try {
+            PacketByteBuf buf = new PacketByteBuf(Unpooled.buffer());
+            buf.writeItemStack(cheatedStack.copy());
+            MinecraftClient.getInstance().getNetworkHandler().sendPacket(new CustomPayloadServerPacket(RoughlyEnoughItemsCore.CREATE_ITEMS_PACKET, buf));
+            return true;
+        } catch (Exception e) {
+            return false;
+        }
+    }
+    
+    public static boolean executeRecipeKeyBind() {
+        return false;
+    }
+    
+    public static boolean executeUsageKeyBind() {
+        return false;
+    }
+    
+    @Override
+    public void clientLoaded() {
+        Registry.ITEM.forEach(this::registerItem);
+        Registry.ENCHANTMENT.forEach(enchantment -> {
+            for(int i = enchantment.getMinimumLevel(); i < enchantment.getMaximumLevel(); i++) {
+                Map<Enchantment, Integer> map = new HashMap<>();
+                map.put(enchantment, i);
+                ItemStack itemStack = new ItemStack(Items.ENCHANTED_BOOK);
+                EnchantmentHelper.set(map, itemStack);
+                registerItemStack(itemStack);
+            }
+        });
+    }
+    
+    public void registerItem(Item item) {
+        registerItemStack(item.getDefaultStack());
+    }
+    
+    public void registerItemStack(ItemStack stack) {
+        if (!stack.getItem().equals(Items.AIR))
+            itemList.add(stack);
+    }
+    
+    @Override
+    public void onInitializeClient() {
+        this.cheating = true;
+    }
+    
+}

+ 81 - 14
src/main/java/me/shedaniel/rei/gui/ContainerGuiOverlay.java

@@ -1,13 +1,18 @@
 package me.shedaniel.rei.gui;
 
+import me.shedaniel.rei.RoughlyEnoughItemsCore;
+import me.shedaniel.rei.client.ClientHelper;
 import me.shedaniel.rei.gui.widget.IWidget;
+import me.shedaniel.rei.gui.widget.ItemListOverlay;
 import me.shedaniel.rei.gui.widget.LabelWidget;
-import me.shedaniel.rei.mixin.IMixinContainerGui;
+import me.shedaniel.rei.gui.widget.QueuedTooltip;
+import me.shedaniel.rei.listeners.IMixinContainerGui;
 import net.minecraft.client.MinecraftClient;
 import net.minecraft.client.gui.ContainerGui;
 import net.minecraft.client.gui.Gui;
 import net.minecraft.client.gui.widget.ButtonWidget;
 import net.minecraft.client.util.Window;
+import net.minecraft.util.math.MathHelper;
 
 import java.awt.*;
 import java.util.ArrayList;
@@ -18,41 +23,80 @@ public class ContainerGuiOverlay extends Gui {
     private Rectangle rectangle;
     private IMixinContainerGui containerGui;
     private Window window;
-    private int page;
-    private List<IWidget> widgets;
+    private static int page = 0;
+    private final List<IWidget> widgets;
+    private ItemListOverlay itemListOverlay;
+    private ButtonWidget buttonLeft, buttonRight;
+    private final List<QueuedTooltip> queuedTooltips;
     
     public ContainerGuiOverlay(ContainerGui containerGui) {
+        this.queuedTooltips = new ArrayList<>();
         this.containerGui = (IMixinContainerGui) containerGui;
+        this.widgets = new ArrayList<>();
     }
     
-    @Override
-    protected void onInitialized() {
+    public void onInitialized() {
         //Update Variables
-        this.widgets = new ArrayList<>();
+        this.widgets.clear();
         this.window = MinecraftClient.getInstance().window;
         if (MinecraftClient.getInstance().currentGui instanceof ContainerGui)
             this.containerGui = (IMixinContainerGui) MinecraftClient.getInstance().currentGui;
-        this.page = 0;
+        this.rectangle = calculateBoundary();
+        widgets.add(this.itemListOverlay = new ItemListOverlay(this, containerGui, page));
         
-        rectangle = calculateBoundary();
-        addButton(new ButtonWidget(-1, rectangle.x, rectangle.y + 3, 16, 20, "<") {
+        this.itemListOverlay.updateList(getItemListArea(), page);
+        addButton(buttonLeft = new ButtonWidget(-1, rectangle.x, rectangle.y + 3, 16, 20, "<") {
             @Override
             public void onPressed(double double_1, double double_2) {
-                //Left Page
+                page--;
+                if (page < 0)
+                    page = getTotalPage();
+                itemListOverlay.updateList(getItemListArea(), page);
             }
         });
-        addButton(new ButtonWidget(-1, rectangle.x + rectangle.width - 18, rectangle.y + 3, 16, 20, ">") {
+        addButton(buttonRight = new ButtonWidget(-1, rectangle.x + rectangle.width - 18, rectangle.y + 3, 16, 20, ">") {
             @Override
             public void onPressed(double double_1, double double_2) {
-                //Right Page
+                page++;
+                if (page > getTotalPage())
+                    page = 0;
+                itemListOverlay.updateList(getItemListArea(), page);
+            }
+        });
+        this.widgets.add(new LabelWidget(rectangle.x + (rectangle.width / 2), rectangle.y + 10, "") {
+            @Override
+            public void draw(int mouseX, int mouseY, float partialTicks) {
+                this.text = String.format("%s/%s", page + 1, getTotalPage() + 1);
+                super.draw(mouseX, mouseY, partialTicks);
             }
         });
-        widgets.add(new LabelWidget(rectangle.x + (rectangle.width / 2), rectangle.y + 10, String.format("%s/%s", page + 1, getTotalPage() + 1)));
+        
+        this.listeners.addAll(widgets);
+    }
+    
+    private Rectangle getItemListArea() {
+        return new Rectangle(rectangle.x + 2, rectangle.y + 24, rectangle.width - 4, rectangle.height - 27);
+    }
+    
+    public Rectangle getRectangle() {
+        return rectangle;
+    }
+    
+    public void render(int mouseX, int mouseY, float partialTicks) {
+        draw(mouseX, mouseY, partialTicks);
+        queuedTooltips.forEach(queuedTooltip -> containerGui.getContainerGui().drawTooltip(queuedTooltip.text, queuedTooltip.mouse.x, queuedTooltip.mouse.y));
+        queuedTooltips.clear();
+    }
+    
+    public void addTooltip(QueuedTooltip queuedTooltip) {
+        queuedTooltips.add(queuedTooltip);
     }
     
     @Override
     public void draw(int int_1, int int_2, float float_1) {
         widgets.forEach(widget -> widget.draw(int_1, int_2, float_1));
+        itemListOverlay.draw(int_1, int_2, float_1);
+        super.draw(int_1, int_2, float_1);
     }
     
     private Rectangle calculateBoundary() {
@@ -62,7 +106,30 @@ public class ContainerGuiOverlay extends Gui {
     }
     
     private int getTotalPage() {
-        return 10;
+        return MathHelper.ceil(ClientHelper.getItemList().size() / itemListOverlay.getTotalSlotsPerPage());
     }
     
+    @Override
+    public boolean mouseScrolled(double amount) {
+        if (rectangle.contains(ClientHelper.getMouseLocation())) {
+            if (amount > 0 && buttonLeft.enabled)
+                buttonLeft.onPressed(0, 0);
+            else if (amount < 0 && buttonRight.enabled)
+                buttonRight.onPressed(0, 0);
+            else return false;
+            return true;
+        }
+        for(IWidget widget : widgets)
+            if (widget.mouseScrolled(amount))
+                return true;
+        return false;
+    }
+    
+    @Override
+    public boolean mouseClicked(double double_1, double double_2, int int_1) {
+        for(IWidget widget : widgets)
+            if (widget.mouseClicked(double_1, double_2, int_1))
+                return true;
+        return false;
+    }
 }

+ 13 - 0
src/main/java/me/shedaniel/rei/gui/widget/HighlightableWidget.java

@@ -0,0 +1,13 @@
+package me.shedaniel.rei.gui.widget;
+
+import java.awt.*;
+
+public interface HighlightableWidget extends IWidget {
+    
+    public Rectangle getBounds();
+    
+    default boolean isHighlighted(int mouseX, int mouseY) {
+        return getBounds().contains(new Point(mouseX, mouseY));
+    }
+    
+}

+ 33 - 0
src/main/java/me/shedaniel/rei/gui/widget/IWidget.java

@@ -1,9 +1,42 @@
 package me.shedaniel.rei.gui.widget;
 
+import com.google.common.collect.Lists;
 import net.minecraft.client.gui.GuiEventListener;
 
+import java.util.List;
+
 public interface IWidget extends GuiEventListener {
     
+    public List<IWidget> getListeners();
+    
     public void draw(int mouseX, int mouseY, float partialTicks);
     
+    @Override
+    default boolean mouseClicked(double double_1, double double_2, int int_1) {
+        if (onMouseClick(int_1, double_1, double_2))
+            return true;
+        for(IWidget widget : getListeners())
+            if (widget.mouseClicked(double_1, double_2, int_1))
+                return true;
+        return false;
+    }
+    
+    default boolean onMouseClick(int button, double mouseX, double mouseY) {
+        return false;
+    }
+    
+    default boolean onMouseScrolled(double amount) {
+        return false;
+    }
+    
+    @Override
+    default boolean mouseScrolled(double amount) {
+        if (onMouseScrolled(amount))
+            return true;
+        for(IWidget widget : getListeners())
+            if (widget.mouseScrolled(amount))
+                return true;
+        return false;
+    }
+    
 }

+ 126 - 0
src/main/java/me/shedaniel/rei/gui/widget/ItemListOverlay.java

@@ -0,0 +1,126 @@
+package me.shedaniel.rei.gui.widget;
+
+import com.google.common.collect.Lists;
+import me.shedaniel.rei.RoughlyEnoughItemsCore;
+import me.shedaniel.rei.client.ClientHelper;
+import me.shedaniel.rei.gui.ContainerGuiOverlay;
+import me.shedaniel.rei.listeners.ClientLoaded;
+import me.shedaniel.rei.listeners.IMixinContainerGui;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.gui.Drawable;
+import net.minecraft.client.network.ClientPlayerEntity;
+import net.minecraft.client.resource.language.I18n;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.math.MathHelper;
+
+import java.awt.*;
+import java.util.ArrayList;
+import java.util.List;
+
+public class ItemListOverlay extends Drawable implements IWidget {
+    
+    private ContainerGuiOverlay containerGuiOverlay;
+    private IMixinContainerGui containerGui;
+    private List<IWidget> widgets = new ArrayList<>();
+    private int width, height, page;
+    private Rectangle rectangle;
+    
+    public ItemListOverlay(ContainerGuiOverlay containerGuiOverlay, IMixinContainerGui containerGui, int page) {
+        this.containerGuiOverlay = containerGuiOverlay;
+        this.containerGui = containerGui;
+        this.width = 0;
+        this.height = 0;
+        this.page = page;
+    }
+    
+    public int getTotalSlotsPerPage() {
+        return width * height;
+    }
+    
+    @Override
+    public void draw(int int_1, int int_2, float float_1) {
+        widgets.forEach(widget -> widget.draw(int_1, int_2, float_1));
+    }
+    
+    public void updateList(int page) {
+        updateList(rectangle, page);
+    }
+    
+    public void updateList(Rectangle rect, int page) {
+        this.rectangle = rect;
+        if (ClientHelper.getItemList().isEmpty())
+            RoughlyEnoughItemsCore.getListeners(ClientLoaded.class).forEach(ClientLoaded::clientLoaded);
+        List<ItemStack> stacks = ClientHelper.getItemList();
+        this.widgets.clear();
+        this.page = page;
+        calculateListSize(rect);
+        double startX = rect.getCenterX() - width * 9;
+        double startY = rect.getCenterY() - height * 9;
+        for(int i = 0; i < getTotalSlotsPerPage(); i++) {
+            int j = i + page * getTotalSlotsPerPage();
+            if (j >= stacks.size())
+                break;
+            widgets.add(new ItemSlotWidget((int) (startX + (i % width) * 18), (int) (startY + MathHelper.floor(i / width) * 18),
+                    stacks.get(j), false, true, containerGui) {
+                @Override
+                protected List<String> getTooltip(ItemStack itemStack) {
+                    if (!ClientHelper.isCheating() || MinecraftClient.getInstance().player.inventory.getCursorStack().isEmpty())
+                        return super.getTooltip(itemStack);
+                    List<String> list = Lists.newArrayList();
+                    list.add(I18n.translate("text.rei.delete_items"));
+                    return list;
+                }
+                
+                @Override
+                public boolean onMouseClick(int button, double mouseX, double mouseY) {
+                    ClientPlayerEntity player = MinecraftClient.getInstance().player;
+                    if (getBounds().contains(mouseX, mouseY)) {
+                        if (ClientHelper.isCheating() && !player.inventory.getCursorStack().isEmpty()) {
+                            ClientHelper.sendDeletePacket();
+                            return true;
+                        }
+                        if (!player.inventory.getCursorStack().isEmpty())
+                            return false;
+                        if (ClientHelper.isCheating()) {
+                            if (getCurrentStack() != null && !getCurrentStack().isEmpty()) {
+                                ItemStack cheatedStack = getCurrentStack().copy();
+                                cheatedStack.setAmount(button == 0 ? 1 : button == 1 ? cheatedStack.getMaxAmount() : cheatedStack.getAmount());
+                                return ClientHelper.tryCheatingStack(cheatedStack);
+                            }
+                        } else {
+                            if (button == 0)
+                                return ClientHelper.executeRecipeKeyBind();
+                            else if (button == 1)
+                                return ClientHelper.executeUsageKeyBind();
+                        }
+                    }
+                    return false;
+                }
+            });
+        }
+    }
+    
+    private void calculateListSize(Rectangle rect) {
+        int xOffset = 0, yOffset = 0;
+        this.width = 0;
+        this.height = 0;
+        while (true) {
+            xOffset += 18;
+            if (height == 0)
+                width++;
+            if (xOffset + 19 > rect.width) {
+                xOffset = 0;
+                yOffset += 18;
+                height++;
+            }
+            if (yOffset + 19 > rect.height)
+                break;
+        }
+    }
+    
+    @Override
+    public List<IWidget> getListeners() {
+        return widgets;
+    }
+    
+}

+ 101 - 0
src/main/java/me/shedaniel/rei/gui/widget/ItemSlotWidget.java

@@ -0,0 +1,101 @@
+package me.shedaniel.rei.gui.widget;
+
+import com.google.common.collect.Lists;
+import me.shedaniel.rei.client.ClientHelper;
+import me.shedaniel.rei.listeners.IMixinContainerGui;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.gui.ContainerGui;
+import net.minecraft.client.gui.Drawable;
+import net.minecraft.client.render.GuiLighting;
+import net.minecraft.client.render.item.ItemRenderer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.Identifier;
+import net.minecraft.util.math.MathHelper;
+
+import java.awt.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class ItemSlotWidget extends Drawable implements HighlightableWidget {
+    
+    private static final Identifier RECIPE_GUI = new Identifier("roughlyenoughitems", "textures/gui/recipecontainer.png");
+    private List<ItemStack> itemList = new LinkedList<>();
+    private boolean drawBackground, showToolTips;
+    private int x, y;
+    private IMixinContainerGui containerGui;
+    
+    public ItemSlotWidget(int x, int y, ItemStack itemStack, boolean drawBackground, boolean showToolTips, IMixinContainerGui containerGui) {
+        this(x, y, Arrays.asList(itemStack), drawBackground, showToolTips, containerGui);
+    }
+    
+    public ItemSlotWidget(int x, int y, List<ItemStack> itemList, boolean drawBackground, boolean showToolTips, IMixinContainerGui containerGui) {
+        this.itemList = itemList;
+        this.drawBackground = drawBackground;
+        this.showToolTips = showToolTips;
+        this.x = x;
+        this.y = y;
+        this.containerGui = containerGui;
+    }
+    
+    public boolean isDrawBackground() {
+        return drawBackground;
+    }
+    
+    @Override
+    public List<IWidget> getListeners() {
+        return new ArrayList<>();
+    }
+    
+    @Override
+    public void draw(int mouseX, int mouseY, float partialTicks) {
+        final ItemStack itemStack = getCurrentStack();
+        if (drawBackground) {
+            MinecraftClient.getInstance().getTextureManager().bindTexture(RECIPE_GUI);
+            drawTexturedRect(this.x - 1, this.y - 1, 0, 222, 18, 18);
+        }
+        if (itemStack.isEmpty())
+            return;
+        GuiLighting.enableForItems();
+        ItemRenderer itemRenderer = MinecraftClient.getInstance().getItemRenderer();
+        itemRenderer.zOffset = 200.0F;
+        itemRenderer.renderItemAndGlowInGui(itemStack, x, y);
+        assert containerGui != null;
+        if (containerGui.getDraggedStack().isEmpty())
+            itemRenderer.renderItemOverlaysInGUIWithText(MinecraftClient.getInstance().fontRenderer, itemStack, x, y - 0, getItemCountOverlay(itemStack));
+        else
+            itemRenderer.renderItemOverlaysInGUIWithText(MinecraftClient.getInstance().fontRenderer, itemStack, x, y - 8, getItemCountOverlay(itemStack));
+        itemRenderer.zOffset = 0.0F;
+        if (isHighlighted(mouseX, mouseY) && showToolTips) {
+            List<String> toolTip = getTooltip(itemStack);
+            containerGui.getOverlay().addTooltip(new QueuedTooltip(new Point(mouseX, mouseY), toolTip));
+        }
+    }
+    
+    protected List<String> getTooltip(ItemStack itemStack) {
+        final String modString = "§9§o" + ClientHelper.getModFromItemStack(itemStack);
+        MinecraftClient mc = MinecraftClient.getInstance();
+        List<String> toolTip = Lists.newArrayList();
+        if (containerGui != null)
+            toolTip = containerGui.getContainerGui().getStackTooltip(itemStack).stream().filter(s -> !s.equals(modString)).collect(Collectors.toList());
+        else toolTip.add(itemStack.getDisplayName().getFormattedText());
+        toolTip.add("§9§o" + ClientHelper.getModFromItemStack(itemStack));
+        return toolTip;
+    }
+    
+    protected String getItemCountOverlay(ItemStack currentStack) {
+        return "";
+    }
+    
+    protected ItemStack getCurrentStack() {
+        return itemList.get(MathHelper.clamp((int) (System.currentTimeMillis() / 500) % itemList.size(), 0, itemList.size() - 1));
+    }
+    
+    @Override
+    public Rectangle getBounds() {
+        return new Rectangle(this.x, this.y, 18, 18);
+    }
+    
+}

+ 8 - 0
src/main/java/me/shedaniel/rei/gui/widget/LabelWidget.java

@@ -4,6 +4,9 @@ import net.minecraft.client.MinecraftClient;
 import net.minecraft.client.gui.Drawable;
 import net.minecraft.client.gui.GuiEventListener;
 
+import java.util.ArrayList;
+import java.util.List;
+
 public class LabelWidget extends Drawable implements IWidget {
     
     public int x;
@@ -16,6 +19,11 @@ public class LabelWidget extends Drawable implements IWidget {
         this.text = text;
     }
     
+    @Override
+    public List<IWidget> getListeners() {
+        return new ArrayList<>();
+    }
+    
     @Override
     public void draw(int mouseX, int mouseY, float partialTicks) {
         drawStringCentered(MinecraftClient.getInstance().fontRenderer, text, x, y, -1);

+ 18 - 0
src/main/java/me/shedaniel/rei/gui/widget/QueuedTooltip.java

@@ -0,0 +1,18 @@
+package me.shedaniel.rei.gui.widget;
+
+
+import java.awt.*;
+import java.util.LinkedList;
+import java.util.List;
+
+public class QueuedTooltip {
+    
+    public Point mouse;
+    public List<String> text;
+    
+    public QueuedTooltip(Point mouse, List<String> text) {
+        this.mouse = mouse;
+        this.text = new LinkedList<>(text);
+    }
+    
+}

+ 7 - 0
src/main/java/me/shedaniel/rei/listeners/ClientLoaded.java

@@ -0,0 +1,7 @@
+package me.shedaniel.rei.listeners;
+
+public interface ClientLoaded extends IListener {
+    
+    public void clientLoaded();
+    
+}

+ 9 - 1
src/main/java/me/shedaniel/rei/mixin/IMixinContainerGui.java → src/main/java/me/shedaniel/rei/listeners/IMixinContainerGui.java

@@ -1,6 +1,10 @@
-package me.shedaniel.rei.mixin;
+package me.shedaniel.rei.listeners;
 
 import me.shedaniel.rei.gui.ContainerGuiOverlay;
+import net.minecraft.client.gui.ContainerGui;
+import net.minecraft.item.ItemStack;
+
+import java.util.List;
 
 public interface IMixinContainerGui {
     
@@ -12,6 +16,10 @@ public interface IMixinContainerGui {
     
     public int getContainerHeight();
     
+    public ItemStack getDraggedStack();
+    
     public ContainerGuiOverlay getOverlay();
     
+    public ContainerGui getContainerGui();
+    
 }

+ 34 - 9
src/main/java/me/shedaniel/rei/mixin/MixinContainerGui.java

@@ -1,10 +1,14 @@
 package me.shedaniel.rei.mixin;
 
 import me.shedaniel.rei.gui.ContainerGuiOverlay;
+import me.shedaniel.rei.listeners.IMixinContainerGui;
 import net.minecraft.client.MinecraftClient;
 import net.minecraft.client.gui.ContainerGui;
 import net.minecraft.client.gui.Gui;
+import net.minecraft.client.gui.GuiEventListener;
+import net.minecraft.item.ItemStack;
 import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Overwrite;
 import org.spongepowered.asm.mixin.Shadow;
 import org.spongepowered.asm.mixin.injection.At;
 import org.spongepowered.asm.mixin.injection.Inject;
@@ -13,19 +17,18 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
 @Mixin(ContainerGui.class)
 public class MixinContainerGui extends Gui implements IMixinContainerGui {
     
-    private ContainerGuiOverlay overlay;
-    
     @Shadow
     protected int left;
-    
     @Shadow
     protected int top;
-    
     @Shadow
     protected int containerWidth;
-    
     @Shadow
     protected int containerHeight;
+    private ContainerGuiOverlay overlay;
+    private ContainerGui lastGui;
+    @Shadow
+    private ItemStack field_2782;
     
     @Override
     public int getContainerLeft() {
@@ -49,19 +52,41 @@ public class MixinContainerGui extends Gui implements IMixinContainerGui {
     
     @Override
     public ContainerGuiOverlay getOverlay() {
-        if (overlay == null)
-            overlay = new ContainerGuiOverlay((ContainerGui) MinecraftClient.getInstance().currentGui);
-        return overlay;
+        if (this.overlay == null)
+            this.overlay = new ContainerGuiOverlay(lastGui);
+        return this.overlay;
     }
     
     @Inject(method = "onInitialized()V", at = @At("RETURN"))
     protected void onInitialized(CallbackInfo info) {
+        this.overlay = null;
         this.listeners.add(getOverlay());
+        getOverlay().onInitialized();
     }
     
     @Inject(method = "draw(IIF)V", at = @At("RETURN"))
     public void draw(int int_1, int int_2, float float_1, CallbackInfo info) {
-        getOverlay().draw(int_1, int_2, float_1);
+        if (MinecraftClient.getInstance().currentGui instanceof ContainerGui)
+            this.lastGui = (ContainerGui) MinecraftClient.getInstance().currentGui;
+        getOverlay().render(int_1, int_2, float_1);
+    }
+    
+    @Override
+    public ItemStack getDraggedStack() {
+        return this.field_2782;
+    }
+    
+    @Override
+    public ContainerGui getContainerGui() {
+        return lastGui;
+    }
+    
+    @Override
+    public boolean mouseScrolled(double double_1) {
+        for(GuiEventListener entry : this.getEntries())
+            if (entry.mouseScrolled(double_1))
+                return true;
+        return false;
     }
     
 }

+ 2 - 1
src/main/resources/assets/roughlyenoughitems/lang/en_us.json

@@ -33,5 +33,6 @@
   "text.rei.enable_craftable_only.button": "Craftable Only: %s%b",
   "text.rei.enable_craftable_only.tooltip": "Please resize the window after editing\nthis config to apply the changes",
   "text.rei.showing_craftable": "Showing Craftable",
-  "text.rei.showing_all": "Showing All"
+  "text.rei.showing_all": "Showing All",
+  "text.rei.delete_items": "§cDelete Item"
 }

+ 3 - 2
src/main/resources/fabric.mod.json

@@ -2,13 +2,14 @@
   "id": "roughlyenoughitems",
   "name": "RoughlyEnoughItems",
   "description": "To allow players to view items and recipes.",
-  "version": "1.5.1",
+  "version": "2.0",
   "side": "client",
   "authors": [
     "Danielshe"
   ],
   "initializers": [
-    "me.shedaniel.rei.RoughlyEnoughItemsCore"
+    "me.shedaniel.rei.RoughlyEnoughItemsCore",
+    "me.shedaniel.rei.client.ClientHelper"
   ],
   "requires": {
     "fabric": "*"