shedaniel 5 жил өмнө
parent
commit
ffd17c633d

+ 2 - 3
build.gradle

@@ -54,9 +54,10 @@ dependencies {
         transitive = false
     }
     modImplementation("me.shedaniel.cloth:config-2:${cloth_config_version}") {
-        transitive = false
+//        include group: "me.shedaniel.cloth", name: "basic-math"
     }
     modApi("me.sargunvohra.mcmods:autoconfig1u:${project.autoconfig1u}")
+    modApi ("org.jetbrains:annotations:18.0.0")
     if (includeDep) {
         afterEvaluate {
             def listAdded = []
@@ -87,8 +88,6 @@ dependencies {
     modRuntime("io.github.prospector:modmenu:${modmenu_version}") {
         transitive = false
     }
-    compile "org.lwjgl:lwjgl-jemalloc:3.2.1"
-    compileOnly 'org.jetbrains:annotations:18.0.0'
     modRuntime("com.lettuce.fudge:notenoughcrashes:1.1.5+1.15.1") {
         transitive = false
     }

+ 3 - 3
gradle.properties

@@ -1,11 +1,11 @@
-mod_version=3.3.12
+mod_version=3.3.13
 minecraft_version=1.15.1
 yarn_version=1.15.1+build.1
 fabricloader_version=0.7.2+build.174
 cloth_events_version=1.1.0
-cloth_config_version=2.7
+cloth_config_version=2.8.3
 modmenu_version=1.8.0+build.16
 fabric_api=0.4.27+build.286-1.15
 autoconfig1u=1.2.4
-api_include=me.shedaniel.cloth:cloth-events,me.shedaniel.cloth:config-2,me.sargunvohra.mcmods:autoconfig1u
+api_include=me.shedaniel.cloth:cloth-events,me.shedaniel.cloth:config-2,me.sargunvohra.mcmods:autoconfig1u,org.jetbrains:annotations
 #api_include=me.shedaniel.cloth:cloth-events,me.shedaniel.cloth:config-2,me.sargunvohra.mcmods:autoconfig1u,net.fabricmc.fabric-api:fabric-

+ 3 - 1
src/main/java/me/shedaniel/rei/api/ConfigObject.java

@@ -125,9 +125,11 @@ public interface ConfigObject {
     
     ModifierKeyCode getCopyRecipeIdentifierKeybind();
     
+    ModifierKeyCode getExportImageKeybind();
+    
     double getEntrySize();
     
-    @Deprecated
+    @ApiStatus.Internal
     ConfigObjectImpl.General getGeneral();
     
     boolean isUsingCompactTabs();

+ 201 - 0
src/main/java/me/shedaniel/rei/gui/RecipeDisplayExporter.java

@@ -0,0 +1,201 @@
+/*
+ * Roughly Enough Items by Danielshe.
+ * Licensed under the MIT License.
+ */
+
+package me.shedaniel.rei.gui;
+
+import com.mojang.blaze3d.platform.GlStateManager;
+import com.mojang.blaze3d.systems.RenderSystem;
+import me.shedaniel.math.api.Rectangle;
+import me.shedaniel.rei.gui.widget.Widget;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.gl.Framebuffer;
+import net.minecraft.client.gui.Element;
+import net.minecraft.client.render.DiffuseLighting;
+import net.minecraft.client.render.OverlayTexture;
+import net.minecraft.client.render.VertexConsumerProvider;
+import net.minecraft.client.render.model.BakedModel;
+import net.minecraft.client.render.model.json.ModelTransformation;
+import net.minecraft.client.texture.NativeImage;
+import net.minecraft.client.texture.SpriteAtlasTexture;
+import net.minecraft.client.util.math.MatrixStack;
+import net.minecraft.item.ItemStack;
+import net.minecraft.item.Items;
+import net.minecraft.resource.ResourceImpl;
+import org.jetbrains.annotations.ApiStatus;
+import org.lwjgl.opengl.GL11;
+
+import java.io.File;
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.List;
+
+@ApiStatus.Internal
+@ApiStatus.Experimental
+public final class RecipeDisplayExporter extends Widget {
+    private static final RecipeDisplayExporter INSTANCE = new RecipeDisplayExporter();
+    
+    private RecipeDisplayExporter() {}
+    
+    public static void exportRecipeDisplay(Rectangle rectangle, List<Widget> widgets) {
+        INSTANCE.exportRecipe(rectangle, widgets);
+    }
+    
+    private void exportRecipe(Rectangle rectangle, List<Widget> widgets) {
+        Framebuffer framebuffer = new Framebuffer(rectangle.width * 8, rectangle.height * 8, true, MinecraftClient.IS_SYSTEM_MAC);
+        framebuffer.setClearColor(0, 0, 0, 0);
+        //        int color = ScreenHelper.isDarkModeEnabled() ? -13750738 : -3750202;
+        //        framebuffer.setClearColor(((color >> 16) & 0xFF) / 255f, ((color >> 8) & 0xFF) / 255f, (color & 0xFF) / 255f, ((color >> 24) & 0xFF) / 255f);
+        framebuffer.clear(MinecraftClient.IS_SYSTEM_MAC);
+        RenderSystem.pushMatrix();
+        //        RenderSystem.clear(16640, MinecraftClient.IS_SYSTEM_MAC);
+        framebuffer.beginWrite(true);
+        
+        // Fresh matrices
+        //        RenderSystem.clear(256, MinecraftClient.IS_SYSTEM_MAC);
+        RenderSystem.matrixMode(GL11.GL_PROJECTION);
+        RenderSystem.pushMatrix();
+        RenderSystem.loadIdentity();
+        //        RenderSystem.ortho(0.0D, rectangle.width * 8, rectangle.height * 8, 0.0D, -1, 1);
+        //        RenderSystem.scalef(1, -1,0);
+        //        RenderSystem.ortho(-1, 1, 1, -1, -1, 1);
+        //        RenderSystem.ortho(-1, 1, 1, -1, -1, 1);
+        RenderSystem.matrixMode(GL11.GL_MODELVIEW);
+        RenderSystem.pushMatrix();
+        RenderSystem.loadIdentity();
+        //        RenderSystem.translatef(0.0F, 0.0F, -2000.0F);
+        //        RenderSystem.rotatef(180, 1, 0, 0);
+        RenderSystem.scalef(2f / rectangle.width, -2f / rectangle.height, 0);
+        //        RenderSystem.scalef(1f / rectangle.width, -1f / rectangle.height, 0);
+        //        RenderSystem.translatef(10,10,0);
+        RenderSystem.translatef(-rectangle.x, -rectangle.y, 0);
+        //        RenderSystem.translatef(rectangle.x, rectangle.y, 0);
+        RenderSystem.translatef(-rectangle.width / 2f, -rectangle.height / 2f, 0);
+        
+        //        RenderSystem.enableAlphaTest();
+        //        RenderSystem.alphaFunc(516, 0.1F);
+        //        RenderSystem.enableBlend();
+        //        RenderSystem.blendFuncSeparate(GlStateManager.SrcFactor.ONE_MINUS_DST_ALPHA, GlStateManager.DstFactor.DST_ALPHA, GlStateManager.SrcFactor.ONE, GlStateManager.DstFactor.ONE_MINUS_SRC_ALPHA);
+        RenderSystem.enableAlphaTest();
+        RenderSystem.defaultAlphaFunc();
+        RenderSystem.enableBlend();
+        RenderSystem.enableDepthTest();
+        RenderSystem.blendFunc(GlStateManager.SrcFactor.SRC_ALPHA, GlStateManager.DstFactor.ONE_MINUS_SRC_ALPHA);
+        RenderSystem.depthMask(false);
+        RenderSystem.disableCull();
+        RenderSystem.pushLightingAttributes();
+        for (Widget widget : widgets) {
+            widget.render(-1, -1, minecraft.getTickDelta());
+        }
+        {
+            ItemStack stack = new ItemStack(Items.OAK_STAIRS);
+            final BakedModel model = minecraft.getItemRenderer().getHeldItemModel(stack, minecraft.world, minecraft.player);
+            minecraft.getTextureManager().bindTexture(SpriteAtlasTexture.BLOCK_ATLAS_TEX);
+            minecraft.getTextureManager().getTexture(SpriteAtlasTexture.BLOCK_ATLAS_TEX).setFilter(false, false);
+            
+            RenderSystem.enableRescaleNormal();
+            RenderSystem.enableAlphaTest();
+            RenderSystem.defaultAlphaFunc();
+            RenderSystem.enableBlend();
+            RenderSystem.enableDepthTest();
+            RenderSystem.blendFunc(GlStateManager.SrcFactor.SRC_ALPHA, GlStateManager.DstFactor.ONE_MINUS_SRC_ALPHA);
+            
+            RenderSystem.color4f(1.0F, 1.0F, 1.0F, 1.0F);
+            MatrixStack matrixStack = new MatrixStack();
+            
+            matrixStack.translate(rectangle.x + 8, rectangle.y + 8, 0);
+            matrixStack.scale(16, -16, 1F);
+            
+            boolean disableGuiLight = !model.hasDepthInGui();
+            if (disableGuiLight) {
+                DiffuseLighting.disableGuiDepthLighting();
+            }
+            
+            VertexConsumerProvider.Immediate immediate = MinecraftClient.getInstance().getBufferBuilders().getEntityVertexConsumers();
+            minecraft.getItemRenderer().renderItem(stack, ModelTransformation.Type.GUI, false, matrixStack, immediate, 15728880, OverlayTexture.DEFAULT_UV, model);
+            immediate.draw();
+            
+            RenderSystem.enableDepthTest();
+            
+            if (disableGuiLight) {
+                DiffuseLighting.enableGuiDepthLighting();
+            }
+            
+            RenderSystem.disableAlphaTest();
+            RenderSystem.disableRescaleNormal();
+        }
+        //        fillGradient(0, 0, 10, 10, -1, -1);
+        //        fillGradient(rectangle.x, rectangle.y, rectangle.x + 10, rectangle.y + 10, -16777216, -16777216);
+        RenderSystem.depthMask(true);
+        RenderSystem.disableAlphaTest();
+        RenderSystem.disableRescaleNormal();
+        RenderSystem.disableDepthTest();
+        //        RenderSystem.disableAlphaTest();
+        //        RenderSystem.disableBlend();
+        
+        // Reset matrices
+        RenderSystem.matrixMode(GL11.GL_PROJECTION);
+        RenderSystem.popMatrix();
+        RenderSystem.matrixMode(GL11.GL_MODELVIEW);
+        RenderSystem.popMatrix();
+        
+        framebuffer.endWrite();
+        RenderSystem.popMatrix();
+        RenderSystem.viewport(0, 0, minecraft.getWindow().getFramebufferWidth(), minecraft.getWindow().getFramebufferHeight());
+        
+        NativeImage nativeImage = new NativeImage(rectangle.width * 8, rectangle.height * 8, false);
+        RenderSystem.bindTexture(framebuffer.colorAttachment);
+        nativeImage.loadFromTextureImage(0, false);
+        {
+            int width = rectangle.width * 8;
+            int height = rectangle.height * 8;
+            for (int y = 0; y < height; ++y) {
+                for (int x = 0; x < width; ++x) {
+                    if (x > 24 && x < width - 24 && y > 24 && y < height - 24)
+                        nativeImage.setPixelRgba(x, y, nativeImage.getPixelRgba(x, y) | 255 << NativeImage.Format.RGBA.getAlphaChannelOffset());
+                }
+            }
+        }
+        
+        nativeImage.mirrorVertically();
+        ResourceImpl.RESOURCE_IO_EXECUTOR.execute(() -> {
+            try {
+                File export = new File(minecraft.runDirectory, "export");
+                //noinspection ResultOfMethodCallIgnored
+                export.mkdirs();
+                nativeImage.writeFile(getExportFilename(export));
+            } catch (IOException e) {
+                e.printStackTrace();
+            } finally {
+                nativeImage.close();
+                RenderSystem.recordRenderCall(framebuffer::delete);
+            }
+        });
+    }
+    
+    private static File getExportFilename(File directory) {
+        String string = new SimpleDateFormat("yyyy-MM-dd_HH.mm.ss").format(new Date());
+        int i = 1;
+        
+        while (true) {
+            File file = new File(directory, "REI_" + string + (i == 1 ? "" : "_" + i) + ".png");
+            if (!file.exists()) {
+                return file;
+            }
+            
+            ++i;
+        }
+    }
+    
+    @Override
+    public void render(int mouseX, int mouseY, float delta) {
+    
+    }
+    
+    @Override
+    public List<? extends Element> children() {
+        return null;
+    }
+}

+ 50 - 2
src/main/java/me/shedaniel/rei/gui/RecipeViewingScreen.java

@@ -6,7 +6,9 @@
 package me.shedaniel.rei.gui;
 
 import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
 import com.mojang.blaze3d.systems.RenderSystem;
+import me.shedaniel.clothconfig2.api.ModifierKeyCode;
 import me.shedaniel.math.api.Point;
 import me.shedaniel.math.api.Rectangle;
 import me.shedaniel.math.impl.PointHelper;
@@ -17,9 +19,14 @@ import me.shedaniel.rei.utils.CollectionUtils;
 import net.minecraft.client.MinecraftClient;
 import net.minecraft.client.gui.Element;
 import net.minecraft.client.gui.screen.Screen;
+import net.minecraft.client.render.Tessellator;
+import net.minecraft.client.render.VertexConsumerProvider;
 import net.minecraft.client.resource.language.I18n;
 import net.minecraft.client.sound.PositionedSoundInstance;
+import net.minecraft.client.util.InputUtil;
 import net.minecraft.client.util.Window;
+import net.minecraft.client.util.math.Matrix4f;
+import net.minecraft.client.util.math.MatrixStack;
 import net.minecraft.sound.SoundEvents;
 import net.minecraft.text.LiteralText;
 import net.minecraft.util.Formatting;
@@ -37,6 +44,7 @@ public class RecipeViewingScreen extends Screen {
     public static final Identifier CHEST_GUI_TEXTURE = new Identifier("roughlyenoughitems", "textures/gui/recipecontainer.png");
     private final List<Widget> preWidgets;
     private final List<Widget> widgets;
+    private final Map<Rectangle, List<Widget>> recipeBounds;
     private final List<TabWidget> tabs;
     private final Map<RecipeCategory<?>, List<RecipeDisplay>> categoriesMap;
     private final List<RecipeCategory<?>> categories;
@@ -58,6 +66,7 @@ public class RecipeViewingScreen extends Screen {
         this.categoryPages = 0;
         this.preWidgets = Lists.newArrayList();
         this.widgets = Lists.newArrayList();
+        this.recipeBounds = Maps.newHashMap();
         Window window = MinecraftClient.getInstance().getWindow();
         this.bounds = new Rectangle(window.getScaledWidth() / 2 - guiWidth / 2, window.getScaledHeight() / 2 - guiHeight / 2, 176, 150);
         this.categoriesMap = categoriesMap;
@@ -150,6 +159,7 @@ public class RecipeViewingScreen extends Screen {
         boolean isCompactTabs = ConfigObject.getInstance().isUsingCompactTabs();
         int tabSize = isCompactTabs ? 24 : 28;
         this.children.clear();
+        this.recipeBounds.clear();
         this.tabs.clear();
         this.preWidgets.clear();
         this.widgets.clear();
@@ -313,12 +323,13 @@ public class RecipeViewingScreen extends Screen {
         int recipeHeight = selectedCategory.getDisplayHeight();
         List<RecipeDisplay> currentDisplayed = getCurrentDisplayed();
         for (int i = 0; i < currentDisplayed.size(); i++) {
-            int finalI = i;
-            final Supplier<RecipeDisplay> displaySupplier = () -> currentDisplayed.get(finalI);
+            final RecipeDisplay display = currentDisplayed.get(i);
+            final Supplier<RecipeDisplay> displaySupplier = () -> display;
             int displayWidth = selectedCategory.getDisplayWidth(displaySupplier.get());
             final Rectangle displayBounds = new Rectangle(getBounds().getCenterX() - displayWidth / 2, getBounds().y - 2 + 36 + recipeHeight * i + 4 * i, displayWidth, recipeHeight);
             List<Widget> setupDisplay = selectedCategory.setupDisplay(displaySupplier, displayBounds);
             transformNotice(setupDisplay, mainStackToNotice);
+            recipeBounds.put(displayBounds, setupDisplay);
             this.widgets.addAll(setupDisplay);
             if (supplier.isPresent() && supplier.get().get(displayBounds) != null)
                 this.widgets.add(new AutoCraftingButtonWidget(displayBounds, supplier.get().get(displayBounds), supplier.get().getButtonText(), displaySupplier, setupDisplay, selectedCategory));
@@ -429,6 +440,28 @@ public class RecipeViewingScreen extends Screen {
         }
         ScreenHelper.getLastOverlay().render(mouseX, mouseY, delta);
         ScreenHelper.getLastOverlay().lateRender(mouseX, mouseY, delta);
+        {
+            ModifierKeyCode export = ConfigObject.getInstance().getExportImageKeybind();
+            if (export.matchesCurrentKey()) {
+                for (Map.Entry<Rectangle, List<Widget>> entry : recipeBounds.entrySet()) {
+                    Rectangle bounds = entry.getKey();
+                    setBlitOffset(470);
+                    if (bounds.contains(mouseX, mouseY)) {
+                        fillGradient(bounds.x, bounds.y, bounds.getMaxX(), bounds.getMaxY(), 1744822402, 1744822402);
+                        String s = I18n.translate("text.rei.release_export", export.getLocalizedName());
+                        MatrixStack matrixStack_1 = new MatrixStack();
+                        VertexConsumerProvider.Immediate immediate = VertexConsumerProvider.immediate(Tessellator.getInstance().getBuffer());
+                        matrixStack_1.translate(0.0D, 0.0D, 480);
+                        Matrix4f matrix4f_1 = matrixStack_1.peek().getModel();
+                        font.draw(s, bounds.getCenterX() - font.getStringWidth(s) / 2f, bounds.getCenterY() - 4.5f, 0xff000000, false, matrix4f_1, immediate, false, 0, 15728880);
+                        immediate.draw();
+                    } else {
+                        fillGradient(bounds.x, bounds.y, bounds.getMaxX(), bounds.getMaxY(), 1744830463, 1744830463);
+                    }
+                    setBlitOffset(0);
+                }
+            }
+        }
         if (choosePageActivated) {
             setBlitOffset(500);
             this.fillGradient(0, 0, this.width, this.height, -1072689136, -804253680);
@@ -437,6 +470,21 @@ public class RecipeViewingScreen extends Screen {
         }
     }
     
+    @Override
+    public boolean keyReleased(int keyCode, int scanCode, int modifiers) {
+        ModifierKeyCode export = ConfigObject.getInstance().getExportImageKeybind();
+        if (export.matchesKey(keyCode, scanCode)) {
+            for (Map.Entry<Rectangle, List<Widget>> entry : recipeBounds.entrySet()) {
+                Rectangle bounds = entry.getKey();
+                if (bounds.contains(PointHelper.fromMouse())) {
+                    RecipeDisplayExporter.exportRecipeDisplay(bounds, entry.getValue());
+                    break;
+                }
+            }
+        }
+        return super.keyReleased(keyCode, scanCode, modifiers);
+    }
+    
     public int getTotalPages(RecipeCategory<RecipeDisplay> category) {
         return MathHelper.ceil(categoriesMap.get(category).size() / (double) (getRecipesPerPage() + 1));
     }

+ 4 - 17
src/main/java/me/shedaniel/rei/gui/widget/AutoCraftingButtonWidget.java

@@ -12,35 +12,22 @@ import me.shedaniel.math.api.Rectangle;
 import me.shedaniel.math.impl.PointHelper;
 import me.shedaniel.rei.api.*;
 import me.shedaniel.rei.gui.toast.CopyRecipeIdentifierToast;
+import me.shedaniel.rei.impl.ClientHelperImpl;
 import me.shedaniel.rei.impl.ScreenHelper;
-import net.minecraft.client.MinecraftClient;
+import me.shedaniel.rei.utils.CollectionUtils;
 import net.minecraft.client.gui.screen.ingame.AbstractContainerScreen;
 import net.minecraft.client.resource.language.I18n;
 import net.minecraft.util.Formatting;
 import net.minecraft.util.Identifier;
-import net.minecraft.util.Lazy;
 import org.jetbrains.annotations.ApiStatus;
 
-import java.time.LocalDateTime;
 import java.util.List;
 import java.util.Optional;
-import java.util.UUID;
 import java.util.function.Supplier;
-import java.util.stream.Collectors;
 
 @ApiStatus.Internal
 public class AutoCraftingButtonWidget extends ButtonWidget {
     
-    private static final Lazy<Boolean> IS_YOG = new Lazy<>(() -> {
-        try {
-            if (MinecraftClient.getInstance().getSession().getProfile().getId().equals(UUID.fromString("f9546389-9415-4358-9c29-2c26b25bff5b")))
-                return true;
-            LocalDateTime now = LocalDateTime.now();
-            return now.getMonthValue() == 4 && now.getDayOfMonth() == 1;
-        } catch (Throwable throwable) {
-            return false;
-        }
-    });
     private final Supplier<RecipeDisplay> displaySupplier;
     private String extraTooltip;
     private List<String> errorTooltip;
@@ -156,14 +143,14 @@ public class AutoCraftingButtonWidget extends ButtonWidget {
     public Optional<String> getTooltips() {
         String str = "";
         if (errorTooltip == null) {
-            if (IS_YOG.get())
+            if (((ClientHelperImpl) ClientHelper.getInstance()).isYog.get())
                 str += I18n.translate("text.auto_craft.move_items.yog");
             else
                 str += I18n.translate("text.auto_craft.move_items");
         } else {
             if (errorTooltip.size() > 1)
                 str += Formatting.RED.toString() + I18n.translate("error.rei.multi.errors") + "\n";
-            str += errorTooltip.stream().map(s -> Formatting.RED.toString() + (errorTooltip.size() > 1 ? "- " : "") + I18n.translate(s)).collect(Collectors.joining("\n"));
+            str += CollectionUtils.mapAndJoinToString(errorTooltip, s -> Formatting.RED.toString() + (errorTooltip.size() > 1 ? "- " : "") + I18n.translate(s), "\n");
         }
         if (this.minecraft.options.advancedItemTooltips) {
             str += extraTooltip;

+ 16 - 5
src/main/java/me/shedaniel/rei/gui/widget/EntryListWidget.java

@@ -247,10 +247,12 @@ public class EntryListWidget extends WidgetWithBounds {
                         if (notSteppingOnExclusionZones(entry.getBounds().x, entry.getBounds().y, innerBounds)) {
                             entry.entry(stack);
                             entry.isFavorites = false;
-                            size++;
-                            long l = System.nanoTime();
-                            entry.render(mouseX, mouseY, delta);
-                            time += (System.nanoTime() - l);
+                            if (!entry.getCurrentEntry().isEmpty()) {
+                                size++;
+                                long l = System.nanoTime();
+                                entry.render(mouseX, mouseY, delta);
+                                time += (System.nanoTime() - l);
+                            }
                             nextIndex++;
                             break;
                         } else {
@@ -339,13 +341,14 @@ public class EntryListWidget extends WidgetWithBounds {
                         if (first instanceof OptimalEntryStack) {
                             OptimalEntryStack firstStack = (OptimalEntryStack) first;
                             firstStack.optimisedRenderStart(delta);
-                            size += list.size();
                             long l = System.nanoTime();
                             for (EntryListEntry listEntry : list) {
                                 EntryStack currentEntry = listEntry.getCurrentEntry();
                                 currentEntry.setZ(100);
                                 listEntry.drawBackground(mouseX, mouseY, delta);
                                 ((OptimalEntryStack) currentEntry).optimisedRenderBase(listEntry.getInnerBounds(), mouseX, mouseY, delta);
+                                if (!currentEntry.isEmpty())
+                                    size++;
                             }
                             for (EntryListEntry listEntry : list) {
                                 EntryStack currentEntry = listEntry.getCurrentEntry();
@@ -359,6 +362,8 @@ public class EntryListWidget extends WidgetWithBounds {
                             firstStack.optimisedRenderEnd(delta);
                         } else {
                             for (EntryListEntry listEntry : list) {
+                                if (listEntry.getCurrentEntry().isEmpty())
+                                    continue;
                                 size++;
                                 long l = System.nanoTime();
                                 listEntry.render(mouseX, mouseY, delta);
@@ -368,6 +373,8 @@ public class EntryListWidget extends WidgetWithBounds {
                     }
                 } else {
                     for (EntryListEntry entry : entries) {
+                        if (entry.getCurrentEntry().isEmpty())
+                            continue;
                         size++;
                         long l = System.nanoTime();
                         entry.render(mouseX, mouseY, delta);
@@ -418,12 +425,16 @@ public class EntryListWidget extends WidgetWithBounds {
                             firstStack.optimisedRenderEnd(delta);
                         } else {
                             for (EntryListEntry listEntry : list) {
+                                if (listEntry.getCurrentEntry().isEmpty())
+                                    continue;
                                 listEntry.render(mouseX, mouseY, delta);
                             }
                         }
                     }
                 } else {
                     for (EntryListEntry listEntry : entries) {
+                        if (listEntry.getCurrentEntry().isEmpty())
+                            continue;
                         listEntry.render(mouseX, mouseY, delta);
                     }
                 }

+ 27 - 10
src/main/java/me/shedaniel/rei/impl/ClientHelperImpl.java

@@ -5,9 +5,9 @@
 
 package me.shedaniel.rei.impl;
 
-import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Maps;
 import io.netty.buffer.Unpooled;
+import me.sargunvohra.mcmods.autoconfig1u.annotation.ConfigEntry;
 import me.shedaniel.clothconfig2.api.FakeModifierKeyCodeAdder;
 import me.shedaniel.clothconfig2.api.ModifierKeyCode;
 import me.shedaniel.rei.RoughlyEnoughItemsCore;
@@ -31,21 +31,41 @@ import net.minecraft.item.Item;
 import net.minecraft.item.ItemStack;
 import net.minecraft.item.Items;
 import net.minecraft.text.TranslatableText;
-import net.minecraft.util.DefaultedList;
 import net.minecraft.util.Formatting;
 import net.minecraft.util.Identifier;
+import net.minecraft.util.Lazy;
 import net.minecraft.util.PacketByteBuf;
 import net.minecraft.util.registry.Registry;
 import org.jetbrains.annotations.ApiStatus;
 
 import java.lang.reflect.Field;
+import java.time.LocalDateTime;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
+import java.util.UUID;
 
 @ApiStatus.Internal
 public class ClientHelperImpl implements ClientHelper, ClientModInitializer {
     
+    @ApiStatus.Internal public final Lazy<Boolean> isYog = new Lazy<>(() -> {
+        try {
+            if (MinecraftClient.getInstance().getSession().getProfile().getId().equals(UUID.fromString("f9546389-9415-4358-9c29-2c26b25bff5b")))
+                return true;
+        } catch (Throwable ignored) {
+        }
+        return false;
+    });
+    @ApiStatus.Internal public final Lazy<Boolean> ok = new Lazy<>(() -> {
+        try {
+            if (isYog.get())
+                return true;
+            LocalDateTime now = LocalDateTime.now();
+            return now.getMonthValue() == 4 && now.getDayOfMonth() == 1;
+        } catch (Throwable ignored) {
+        }
+        return false;
+    });
     public static ClientHelperImpl instance;
     private final Map<String, String> modNameCache = Maps.newHashMap();
     
@@ -150,12 +170,9 @@ public class ClientHelperImpl implements ClientHelper, ClientModInitializer {
     
     @Override
     public List<ItemStack> getInventoryItemsTypes() {
-        List<DefaultedList<ItemStack>> field_7543 = ImmutableList.of(MinecraftClient.getInstance().player.inventory.main, MinecraftClient.getInstance().player.inventory.armor, MinecraftClient.getInstance().player.inventory.offHand);
-        List<ItemStack> inventoryStacks = new ArrayList<>();
-        field_7543.forEach(itemStacks -> itemStacks.forEach(itemStack -> {
-            if (!itemStack.isEmpty())
-                inventoryStacks.add(itemStack);
-        }));
+        List<ItemStack> inventoryStacks = new ArrayList<>(MinecraftClient.getInstance().player.inventory.main);
+        inventoryStacks.addAll(MinecraftClient.getInstance().player.inventory.armor);
+        inventoryStacks.addAll(MinecraftClient.getInstance().player.inventory.offHand);
         return inventoryStacks;
     }
     
@@ -233,9 +250,9 @@ public class ClientHelperImpl implements ClientHelper, ClientModInitializer {
         if (!FabricLoader.getInstance().isModLoaded("amecs")) {
             try {
                 ConfigObjectImpl.General general = ConfigObject.getInstance().getGeneral();
-                ConfigObjectImpl.General instance = general.getClass().newInstance();
+                ConfigObjectImpl.General instance = general.getClass().getConstructor().newInstance();
                 for (Field declaredField : general.getClass().getDeclaredFields()) {
-                    if (declaredField.getType() == ModifierKeyCode.class) {
+                    if (declaredField.getType() == ModifierKeyCode.class && !declaredField.isAnnotationPresent(ConfigEntry.Gui.Excluded.class)) {
                         declaredField.setAccessible(true);
                         FakeModifierKeyCodeAdder.INSTANCE.registerModifierKeyCode(category, "config.roughlyenoughitems." + declaredField.getName(), () -> {
                             try {

+ 3 - 6
src/main/java/me/shedaniel/rei/impl/ConfigManagerImpl.java

@@ -11,6 +11,7 @@ import com.google.gson.GsonBuilder;
 import com.google.gson.JsonElement;
 import io.github.prospector.modmenu.ModMenu;
 import me.sargunvohra.mcmods.autoconfig1u.AutoConfig;
+import me.sargunvohra.mcmods.autoconfig1u.annotation.ConfigEntry;
 import me.sargunvohra.mcmods.autoconfig1u.gui.ConfigScreenProvider;
 import me.sargunvohra.mcmods.autoconfig1u.gui.registry.GuiRegistry;
 import me.sargunvohra.mcmods.autoconfig1u.serializer.JanksonConfigSerializer;
@@ -96,15 +97,12 @@ public class ConfigManagerImpl implements ConfigManager {
         //            return Collections.singletonList(entry);
         //        }, field -> field.getType() == InputUtil.KeyCode.class);
         guiRegistry.registerPredicateProvider((i13n, field, config, defaults, guiProvider) -> {
+            if (field.isAnnotationPresent(ConfigEntry.Gui.Excluded.class))
+                return Collections.emptyList();
             KeyCodeEntry entry = ConfigEntryBuilder.create().startModifierKeyCodeField(i13n, getUnsafely(field, config, ModifierKeyCode.unknown())).setModifierDefaultValue(() -> getUnsafely(field, defaults)).setModifierSaveConsumer(newValue -> setUnsafely(field, config, newValue)).build();
             entry.setAllowMouse(false);
             return Collections.singletonList(entry);
         }, field -> field.getType() == ModifierKeyCode.class);
-        guiRegistry.registerAnnotationProvider((i13n, field, config, defaults, guiProvider) -> {
-            KeyCodeEntry entry = ConfigEntryBuilder.create().startModifierKeyCodeField(i13n, getUnsafely(field, config, ModifierKeyCode.unknown())).setModifierDefaultValue(() -> getUnsafely(field, defaults)).setModifierSaveConsumer(newValue -> setUnsafely(field, config, newValue)).build();
-            entry.setAllowMouse(false);
-            return Collections.singletonList(entry);
-        }, field -> field.getType() == ModifierKeyCode.class, ConfigObject.UsePercentage.class);
         guiRegistry.registerAnnotationProvider((i13n, field, config, defaults, guiProvider) -> {
             ConfigObject.UsePercentage bounds = field.getAnnotation(ConfigObject.UsePercentage.class);
             return Collections.singletonList(ConfigEntryBuilder.create().startIntSlider(i13n, MathHelper.ceil(Utils.getUnsafely(field, config, 0.0) * 100), MathHelper.ceil(bounds.min() * 100), MathHelper.ceil(bounds.max() * 100)).setDefaultValue(() -> MathHelper.ceil((double) Utils.getUnsafely(field, defaults) * 100)).setSaveConsumer((newValue) -> {
@@ -112,7 +110,6 @@ public class ConfigManagerImpl implements ConfigManager {
             }).setTextGetter(integer -> String.format("Size: %d%%", integer)).build());
         }, (field) -> field.getType() == Double.TYPE || field.getType() == Double.class, ConfigObject.UsePercentage.class);
         
-        
         guiRegistry.registerAnnotationProvider((i13n, field, config, defaults, guiProvider) -> {
             int width = 220;
             return Collections.singletonList(new TooltipListEntry<RecipeScreenType>(i13n, null) {

+ 6 - 0
src/main/java/me/shedaniel/rei/impl/ConfigObjectImpl.java

@@ -252,6 +252,11 @@ public class ConfigObjectImpl implements ConfigObject, ConfigData {
         return general.copyRecipeIdentifierKeybind == null ? ModifierKeyCode.unknown() : general.copyRecipeIdentifierKeybind;
     }
     
+    @Override
+    public ModifierKeyCode getExportImageKeybind() {
+        return general.exportImageKeybind == null ? ModifierKeyCode.unknown() : general.exportImageKeybind;
+    }
+    
     @Override
     public double getEntrySize() {
         return appearance.entrySize;
@@ -281,6 +286,7 @@ public class ConfigObjectImpl implements ConfigObject, ConfigData {
         private ModifierKeyCode focusSearchFieldKeybind = ModifierKeyCode.unknown();
         private ModifierKeyCode copyRecipeIdentifierKeybind = ModifierKeyCode.unknown();
         private ModifierKeyCode favoriteKeybind = ModifierKeyCode.of(InputUtil.Type.KEYSYM.createFromCode(65), Modifier.none());
+        @ConfigEntry.Gui.Excluded private ModifierKeyCode exportImageKeybind = ModifierKeyCode.unknown();
     }
     
     public static class Appearance {

+ 12 - 16
src/main/java/me/shedaniel/rei/impl/ItemEntryStack.java

@@ -13,7 +13,6 @@ import me.shedaniel.rei.gui.widget.QueuedTooltip;
 import net.minecraft.client.MinecraftClient;
 import net.minecraft.client.render.OverlayTexture;
 import net.minecraft.client.render.VertexConsumerProvider;
-import net.minecraft.client.render.item.ItemRenderer;
 import net.minecraft.client.render.model.BakedModel;
 import net.minecraft.client.render.model.json.ModelTransformation;
 import net.minecraft.client.texture.SpriteAtlasTexture;
@@ -31,8 +30,7 @@ import java.util.Optional;
 @ApiStatus.Internal
 public class ItemEntryStack extends AbstractEntryStack implements OptimalEntryStack {
     
-    private static final MatrixStack matrices = new MatrixStack();
-    private final ItemRenderer itemRenderer = MinecraftClient.getInstance().getItemRenderer();
+    private static final MatrixStack MATRICES = new MatrixStack();
     private ItemStack itemStack;
     private int hash = -1;
     
@@ -129,7 +127,7 @@ public class ItemEntryStack extends AbstractEntryStack implements OptimalEntrySt
         //        }
         return hash;
     }
-
+    
     @Nullable
     @Override
     public QueuedTooltip getTooltip(int mouseX, int mouseY) {
@@ -171,12 +169,12 @@ public class ItemEntryStack extends AbstractEntryStack implements OptimalEntrySt
     }
     
     private BakedModel getModelFromStack(ItemStack stack) {
-        BakedModel model = itemRenderer.getModels().getModel(stack);
+        BakedModel model = MinecraftClient.getInstance().getItemRenderer().getModels().getModel(stack);
         if (stack.getItem().hasPropertyGetters())
             model = model.getItemPropertyOverrides().apply(model, stack, null, null);
         if (model != null)
             return model;
-        return itemRenderer.getModels().getModelManager().getMissingModel();
+        return MinecraftClient.getInstance().getItemRenderer().getModels().getModelManager().getMissingModel();
     }
     
     @Override
@@ -184,21 +182,19 @@ public class ItemEntryStack extends AbstractEntryStack implements OptimalEntrySt
         if (!isEmpty() && get(Settings.RENDER).get()) {
             ItemStack stack = getItemStack();
             ((ItemStackHook) (Object) stack).rei_setRenderEnchantmentGlint(get(Settings.Item.RENDER_ENCHANTMENT_GLINT).get());
-            itemRenderer.zOffset = getZ();
-            matrices.push();
-            matrices.translate(bounds.getCenterX(), bounds.getCenterY(), 100.0F + getZ());
-            matrices.scale(bounds.getWidth(), (bounds.getWidth() + bounds.getHeight()) / -2f, bounds.getHeight());
+            MATRICES.push();
+            MATRICES.translate(bounds.getCenterX(), bounds.getCenterY(), 100.0F + getZ());
+            MATRICES.scale(bounds.getWidth(), (bounds.getWidth() + bounds.getHeight()) / -2f, bounds.getHeight());
             VertexConsumerProvider.Immediate immediate = MinecraftClient.getInstance().getBufferBuilders().getEntityVertexConsumers();
             BakedModel model = getModelFromStack(stack);
             boolean bl = !model.hasDepthInGui();
             if (bl)
                 GlStateManager.method_24221();
-            itemRenderer.renderItem(stack, ModelTransformation.Type.GUI, false, matrices, immediate, 15728880, OverlayTexture.DEFAULT_UV, model);
+            MinecraftClient.getInstance().getItemRenderer().renderItem(stack, ModelTransformation.Type.GUI, false, MATRICES, immediate, 15728880, OverlayTexture.DEFAULT_UV, model);
             immediate.draw();
             if (bl)
                 GlStateManager.method_24222();
-            itemRenderer.zOffset = 0.0F;
-            matrices.pop();
+            MATRICES.pop();
             ((ItemStackHook) (Object) stack).rei_setRenderEnchantmentGlint(false);
         }
     }
@@ -206,9 +202,9 @@ public class ItemEntryStack extends AbstractEntryStack implements OptimalEntrySt
     @Override
     public void optimisedRenderOverlay(Rectangle bounds, int mouseX, int mouseY, float delta) {
         if (!isEmpty() && get(Settings.RENDER).get()) {
-            itemRenderer.zOffset = getZ();
-            itemRenderer.renderGuiItemOverlay(MinecraftClient.getInstance().textRenderer, getItemStack(), bounds.x, bounds.y, get(Settings.RENDER_COUNTS).get() ? get(Settings.COUNTS).apply(this) : "");
-            itemRenderer.zOffset = 0.0F;
+            MinecraftClient.getInstance().getItemRenderer().zOffset = getZ();
+            MinecraftClient.getInstance().getItemRenderer().renderGuiItemOverlay(MinecraftClient.getInstance().textRenderer, getItemStack(), bounds.x, bounds.y, get(Settings.RENDER_COUNTS).get() ? get(Settings.COUNTS).apply(this) : "");
+            MinecraftClient.getInstance().getItemRenderer().zOffset = 0.0F;
         }
     }
 }

+ 23 - 0
src/main/java/me/shedaniel/rei/plugin/DefaultPlugin.java

@@ -15,6 +15,9 @@ import me.shedaniel.rei.api.plugins.REIPluginV0;
 import me.shedaniel.rei.gui.RecipeViewingScreen;
 import me.shedaniel.rei.gui.VillagerRecipeViewingScreen;
 import me.shedaniel.rei.gui.widget.CategoryBaseWidget;
+import me.shedaniel.rei.gui.widget.QueuedTooltip;
+import me.shedaniel.rei.impl.ClientHelperImpl;
+import me.shedaniel.rei.impl.RenderingEntry;
 import me.shedaniel.rei.impl.ScreenHelper;
 import me.shedaniel.rei.plugin.blasting.DefaultBlastingDisplay;
 import me.shedaniel.rei.plugin.brewing.DefaultBrewingCategory;
@@ -55,6 +58,7 @@ import net.minecraft.recipe.*;
 import net.minecraft.util.Identifier;
 import net.minecraft.util.math.MathHelper;
 import net.minecraft.util.registry.Registry;
+import org.jetbrains.annotations.Nullable;
 
 import java.util.*;
 
@@ -138,6 +142,25 @@ public class DefaultPlugin implements REIPluginV0 {
             if (!fluid.getDefaultState().isEmpty() && fluid.getDefaultState().isStill())
                 entryRegistry.registerEntry(EntryStack.create(fluid));
         }
+        entryRegistry.registerEntry(new RenderingEntry() {
+            private Identifier id = new Identifier("roughlyenoughitems", "textures/gui/kirb.png");
+            
+            @Override
+            public void render(Rectangle bounds, int mouseX, int mouseY, float delta) {
+                MinecraftClient.getInstance().getTextureManager().bindTexture(id);
+                innerBlit(bounds.x, bounds.getMaxX(), bounds.y, bounds.getMaxY(), getBlitOffset(), 0, 1, 0, 1);
+            }
+            
+            @Override
+            public boolean isEmpty() {
+                return !((ClientHelperImpl) ClientHelper.getInstance()).ok.get();
+            }
+            
+            @Override
+            public @Nullable QueuedTooltip getTooltip(int mouseX, int mouseY) {
+                return QueuedTooltip.create("Kibby");
+            }
+        });
     }
     
     @Override

+ 4 - 3
src/main/java/me/shedaniel/rei/plugin/composting/DefaultCompostingCategory.java

@@ -75,8 +75,9 @@ public class DefaultCompostingCategory implements RecipeCategory<DefaultComposti
         widgets.add(new RecipeBaseWidget(bounds) {
             @Override
             public void render(int mouseX, int mouseY, float partialTicks) {
+                fillGradient(getBounds().x, getBounds().y, getBounds().getMaxX(), getBounds().getMaxY(), getInnerColor(), getInnerColor());
                 MinecraftClient.getInstance().getTextureManager().bindTexture(DefaultPlugin.getDisplayTexture());
-                this.blit(startingPoint.x, startingPoint.y, 28, 221, 55, 26);
+                this.blit(startingPoint.x, startingPoint.y + 3, 28, 221, 55, 26);
             }
         });
         List<EntryStack> stacks = new LinkedList<>(recipeDisplaySupplier.get().getItemsByOrder());
@@ -92,10 +93,10 @@ public class DefaultCompostingCategory implements RecipeCategory<DefaultComposti
                             break;
                         }
                     }
-                widgets.add(EntryWidget.create(bounds.getCenterX() - 72 + x * 18, bounds.y + y * 18).entry(entryStack));
+                widgets.add(EntryWidget.create(bounds.getCenterX() - 72 + x * 18, bounds.y + 3 + y * 18).entry(entryStack));
                 i++;
             }
-        widgets.add(EntryWidget.create(startingPoint.x + 34, startingPoint.y + 5).entries(recipeDisplaySupplier.get().getOutputEntries()).noBackground());
+        widgets.add(EntryWidget.create(startingPoint.x + 33, startingPoint.y + 8).entries(recipeDisplaySupplier.get().getOutputEntries()).noBackground());
         return widgets;
     }
     

+ 2 - 1
src/main/java/me/shedaniel/rei/server/RecipeFinder.java

@@ -15,6 +15,7 @@ import net.minecraft.util.registry.Registry;
 import org.jetbrains.annotations.ApiStatus;
 import org.jetbrains.annotations.Nullable;
 
+import java.util.ArrayList;
 import java.util.BitSet;
 import java.util.Iterator;
 import java.util.List;
@@ -106,7 +107,7 @@ public class RecipeFinder {
         
         public Filter(DefaultedList<Ingredient> ingredientsInput) {
             this.ingredientsInput = ingredientsInput;
-            this.ingredients.addAll(ingredientsInput.stream().collect(Collectors.toList()));
+            this.ingredients.addAll(new ArrayList<>(ingredientsInput));
             this.ingredients.removeIf(Ingredient::isEmpty);
             this.ingredientCount = this.ingredients.size();
             this.usableIngredientItemIds = this.getUsableIngredientItemIds();

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

@@ -80,6 +80,7 @@
   "text.rei.favorites_tooltip": " \n§7Press %s to add this to favorites.",
   "text.rei.remove_favorites_tooltip": " \n§7Press %s to remove this from favorites.",
   "text.rei.working_station": "Working Station",
+  "text.rei.release_export": "§lRelease %s to export",
   "text.rei.recipe_id": "\n%sRecipe Id: %s",
   "text.rei.recipe_screen_type.selection": "Recipe Screen Type Selection",
   "text.rei.recipe_screen_type.selection.sub": "You can always edit this setting again via the config screen.",

BIN
src/main/resources/assets/roughlyenoughitems/textures/gui/kirb.png