浏览代码

from aei but like jei now

Unknown 6 年之前
父节点
当前提交
d1e292ca25
共有 66 个文件被更改,包括 3048 次插入0 次删除
  1. 26 0
      .gitignore
  2. 50 0
      build.gradle
  3. 二进制
      gradle/wrapper/gradle-wrapper.jar
  4. 6 0
      gradle/wrapper/gradle-wrapper.properties
  5. 172 0
      gradlew
  6. 84 0
      gradlew.bat
  7. 2 0
      settings.gradle
  8. 66 0
      src/main/java/me/shedaniel/ClientListener.java
  9. 33 0
      src/main/java/me/shedaniel/Core.java
  10. 9 0
      src/main/java/me/shedaniel/api/IAEIPlugin.java
  11. 27 0
      src/main/java/me/shedaniel/api/IDisplayCategory.java
  12. 7 0
      src/main/java/me/shedaniel/api/IDisplayHelper.java
  13. 10 0
      src/main/java/me/shedaniel/api/IDrawable.java
  14. 15 0
      src/main/java/me/shedaniel/api/IRecipe.java
  15. 20 0
      src/main/java/me/shedaniel/api/IRecipeManager.java
  16. 8 0
      src/main/java/me/shedaniel/api/TriBooleanProducer.java
  17. 235 0
      src/main/java/me/shedaniel/gui/AEIRenderHelper.java
  18. 33 0
      src/main/java/me/shedaniel/gui/Drawable.java
  19. 264 0
      src/main/java/me/shedaniel/gui/GuiItemList.java
  20. 13 0
      src/main/java/me/shedaniel/gui/RecipeContainer.java
  21. 218 0
      src/main/java/me/shedaniel/gui/RecipeGui.java
  22. 181 0
      src/main/java/me/shedaniel/gui/widget/AEISlot.java
  23. 72 0
      src/main/java/me/shedaniel/gui/widget/Button.java
  24. 96 0
      src/main/java/me/shedaniel/gui/widget/Control.java
  25. 10 0
      src/main/java/me/shedaniel/gui/widget/IFocusable.java
  26. 71 0
      src/main/java/me/shedaniel/gui/widget/TextBox.java
  27. 38 0
      src/main/java/me/shedaniel/gui/widget/WidgetArrow.java
  28. 157 0
      src/main/java/me/shedaniel/impl/AEIRecipeManager.java
  29. 58 0
      src/main/java/me/shedaniel/library/KeyBindManager.java
  30. 6 0
      src/main/java/me/shedaniel/library/Sink.java
  31. 9 0
      src/main/java/me/shedaniel/listenerdefinitions/CharInput.java
  32. 8 0
      src/main/java/me/shedaniel/listenerdefinitions/DoneLoading.java
  33. 10 0
      src/main/java/me/shedaniel/listenerdefinitions/DrawContainer.java
  34. 8 0
      src/main/java/me/shedaniel/listenerdefinitions/GuiCickListener.java
  35. 9 0
      src/main/java/me/shedaniel/listenerdefinitions/GuiKeyDown.java
  36. 14 0
      src/main/java/me/shedaniel/listenerdefinitions/IMixinGuiContainer.java
  37. 8 0
      src/main/java/me/shedaniel/listenerdefinitions/MinecraftResize.java
  38. 5 0
      src/main/java/me/shedaniel/listenerdefinitions/MouseScrollListener.java
  39. 9 0
      src/main/java/me/shedaniel/listenerdefinitions/PreLoadOptions.java
  40. 7 0
      src/main/java/me/shedaniel/listenerdefinitions/RecipeLoadListener.java
  41. 60 0
      src/main/java/me/shedaniel/listeners/DrawContainerListener.java
  42. 22 0
      src/main/java/me/shedaniel/listeners/InitListener.java
  43. 14 0
      src/main/java/me/shedaniel/listeners/ResizeListener.java
  44. 22 0
      src/main/java/me/shedaniel/mixins/MixinDoneLoading.java
  45. 98 0
      src/main/java/me/shedaniel/mixins/MixinGuiContainer.java
  46. 93 0
      src/main/java/me/shedaniel/mixins/MixinGuiContainerCreative.java
  47. 27 0
      src/main/java/me/shedaniel/mixins/MixinKeyboardListener.java
  48. 23 0
      src/main/java/me/shedaniel/mixins/MixinMinecraftResize.java
  49. 26 0
      src/main/java/me/shedaniel/mixins/MixinRecipeManager.java
  50. 44 0
      src/main/java/me/shedaniel/mixins/SettingsMixin.java
  51. 45 0
      src/main/java/me/shedaniel/network/CheatPacket.java
  52. 32 0
      src/main/java/me/shedaniel/network/DeletePacket.java
  53. 43 0
      src/main/java/me/shedaniel/plugin/VanillaPlugin.java
  54. 106 0
      src/main/java/me/shedaniel/plugin/crafting/VanillaCraftingCategory.java
  55. 16 0
      src/main/java/me/shedaniel/plugin/crafting/VanillaCraftingRecipe.java
  56. 55 0
      src/main/java/me/shedaniel/plugin/crafting/VanillaShapedCraftingRecipe.java
  57. 57 0
      src/main/java/me/shedaniel/plugin/crafting/VanillaShapelessCraftingRecipe.java
  58. 83 0
      src/main/java/me/shedaniel/plugin/furnace/VanillaFurnaceCategory.java
  59. 42 0
      src/main/java/me/shedaniel/plugin/furnace/VanillaFurnaceRecipe.java
  60. 0 0
      src/main/resources/assets/almostenoughitems/.modassetroot
  61. 8 0
      src/main/resources/assets/almostenoughitems/lang/en_us.json
  62. 8 0
      src/main/resources/assets/almostenoughitems/lang/fr_fr.json
  63. 二进制
      src/main/resources/assets/almostenoughitems/textures/gui/recipecontainer.png
  64. 18 0
      src/main/resources/mixins.roughlyenoughitems.json
  65. 6 0
      src/main/resources/pack.mcmeta
  66. 26 0
      src/main/resources/riftmod.json

+ 26 - 0
.gitignore

@@ -0,0 +1,26 @@
+# Compiled nonsense that does not belong in *source* control
+/build
+/bin
+/.gradle
+/minecraft
+/out
+/run
+/classes
+
+# IDE nonsense that could go in source control but really shouldn't
+.classpath
+.project
+.metadata
+.settings
+*.launch
+*.iml
+.idea
+*.ipr
+*.iws
+
+# Sekrit files
+private.properties
+
+# Files from bad operating systems :^)
+Thumbs.db
+.DS_Store

+ 50 - 0
build.gradle

@@ -0,0 +1,50 @@
+buildscript {
+    repositories {
+        jcenter()
+        maven { url 'https://www.jitpack.io' }
+        maven { url 'http://repo.spongepowered.org/maven' }
+        maven { url 'http://files.minecraftforge.net/maven' }
+    }
+    dependencies {
+        classpath 'com.github.Chocohead:ForgeGradle:moderniser-SNAPSHOT'
+        classpath 'org.spongepowered:mixingradle:0.6-SNAPSHOT'
+    }
+}
+
+apply plugin: 'net.minecraftforge.gradle.tweaker-client'
+apply plugin: 'org.spongepowered.mixin'
+apply plugin: 'java'
+
+group 'me.shedaniel'
+version '1.0b-SNAPSHOT'
+archivesBaseName = 'RoughlyEnoughItems'
+
+sourceCompatibility = 1.8
+targetCompatibility = 1.8
+
+repositories {
+    mavenCentral()
+    maven { url 'https://www.dimdev.org/maven/' }
+    maven { url 'https://repo.spongepowered.org/maven/' }
+    maven { url 'https://www.jitpack.io' }
+    maven { url "http://repo.strezz.org/artifactory/list/Strezz-Central" }
+}
+
+
+
+mixin {
+    defaultObfuscationEnv notch
+    add sourceSets.main, 'mixins.roughlyenoughitems.refmap.json'
+}
+
+minecraft {
+    version = '1.13.2'
+    mappings = 'snapshot_20181130'
+    runDir = 'run'
+    tweakClass = 'org.dimdev.riftloader.launch.RiftLoaderClientTweaker'
+}
+
+dependencies {
+    implementation 'com.github.Chocohead:Rift:f76cf44d887d290782590c99770876393c924333:dev'
+    implementation 'com.google.code.gson:gson:2.8.5'
+}

二进制
gradle/wrapper/gradle-wrapper.jar


+ 6 - 0
gradle/wrapper/gradle-wrapper.properties

@@ -0,0 +1,6 @@
+#Fri Dec 21 19:34:34 HKT 2018
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.8-all.zip

+ 172 - 0
gradlew

@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+    echo "$*"
+}
+
+die () {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+  NONSTOP* )
+    nonstop=true
+    ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+    JAVACMD=`cygpath --unix "$JAVACMD"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=$((i+1))
+    done
+    case $i in
+        (0) set -- ;;
+        (1) set -- "$args0" ;;
+        (2) set -- "$args0" "$args1" ;;
+        (3) set -- "$args0" "$args1" "$args2" ;;
+        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Escape application args
+save () {
+    for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+    echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+  cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"

+ 84 - 0
gradlew.bat

@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem  Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega

+ 2 - 0
settings.gradle

@@ -0,0 +1,2 @@
+rootProject.name = 'RoughlyEnoughItems'
+

+ 66 - 0
src/main/java/me/shedaniel/ClientListener.java

@@ -0,0 +1,66 @@
+package me.shedaniel;
+
+import me.shedaniel.api.IAEIPlugin;
+import me.shedaniel.gui.AEIRenderHelper;
+import me.shedaniel.impl.AEIRecipeManager;
+import me.shedaniel.library.KeyBindManager;
+import me.shedaniel.listenerdefinitions.DoneLoading;
+import me.shedaniel.listenerdefinitions.RecipeLoadListener;
+import net.minecraft.client.settings.KeyBinding;
+import net.minecraft.init.Items;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemGroup;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.NonNullList;
+import net.minecraft.util.registry.IRegistry;
+
+import java.awt.event.KeyEvent;
+import java.util.ArrayList;
+import java.util.List;
+
+public class ClientListener implements DoneLoading, RecipeLoadListener {
+    public static KeyBinding recipeKeybind;
+    public static KeyBinding hideKeybind;
+    public static KeyBinding useKeybind;
+    
+    private List<IAEIPlugin> plugins;
+    public static List<ItemStack> stackList;
+    
+    @Override
+    public void onDoneLoading() {
+        
+        plugins = new ArrayList<>();
+        stackList = new ArrayList<>();
+        
+        recipeKeybind = KeyBindManager.createKeybinding("key.aei.recipe", KeyEvent.VK_R, "key.aei.category", AEIRenderHelper::recipeKeybind);
+        hideKeybind = KeyBindManager.createKeybinding("key.aei.hide", KeyEvent.VK_O, "key.aei.category", AEIRenderHelper::hideKeybind);
+        useKeybind = KeyBindManager.createKeybinding("key.aei.use", KeyEvent.VK_U, "key.aei.category", AEIRenderHelper::useKeybind);
+        
+        buildItemList();
+    }
+    
+    private void buildItemList() {
+        if (!IRegistry.ITEM.isEmpty()) {
+            IRegistry.ITEM.forEach(item -> processItem((Item) item));
+        }
+        
+    }
+    
+    private void processItem(Item item) {
+        NonNullList<ItemStack> items = NonNullList.create();
+        try {
+            item.fillItemGroup(item.getGroup(), items);
+            items.forEach(stackList::add);
+        } catch (NullPointerException e) {
+            if (item == Items.ENCHANTED_BOOK) {
+                item.fillItemGroup(ItemGroup.TOOLS, items);
+                items.forEach(stackList::add);
+            }
+        }
+    }
+    
+    @Override
+    public void recipesLoaded(net.minecraft.item.crafting.RecipeManager recipeManager) {
+        AEIRecipeManager.instance().RecipesLoaded(recipeManager);
+    }
+}

+ 33 - 0
src/main/java/me/shedaniel/Core.java

@@ -0,0 +1,33 @@
+package me.shedaniel;
+
+import me.shedaniel.network.CheatPacket;
+import me.shedaniel.network.DeletePacket;
+import net.minecraft.network.EnumPacketDirection;
+import org.dimdev.rift.listener.PacketAdder;
+
+/**
+ * Created by James on 7/27/2018.
+ */
+public class Core implements PacketAdder {
+    @Override
+    public void registerHandshakingPackets(PacketRegistrationReceiver receiver) {
+    }
+    
+    @Override
+    public void registerPlayPackets(PacketRegistrationReceiver receiver) {
+        receiver.registerPacket(EnumPacketDirection.SERVERBOUND, CheatPacket.class);
+        receiver.registerPacket(EnumPacketDirection.SERVERBOUND, DeletePacket.class);
+    }
+    
+    @Override
+    public void registerStatusPackets(PacketRegistrationReceiver receiver) {
+    
+    }
+    
+    @Override
+    public void registerLoginPackets(PacketRegistrationReceiver receiver) {
+    
+    }
+    
+    
+}

+ 9 - 0
src/main/java/me/shedaniel/api/IAEIPlugin.java

@@ -0,0 +1,9 @@
+package me.shedaniel.api;
+
+/**
+ * Created by James on 7/27/2018.
+ */
+public interface IAEIPlugin {
+    
+    public void register();
+}

+ 27 - 0
src/main/java/me/shedaniel/api/IDisplayCategory.java

@@ -0,0 +1,27 @@
+package me.shedaniel.api;
+
+import me.shedaniel.gui.widget.AEISlot;
+import me.shedaniel.gui.widget.Control;
+
+import java.util.List;
+
+/**
+ * Created by James on 8/7/2018.
+ */
+public interface IDisplayCategory<T extends IRecipe> {
+    public String getId();
+    
+    public String getDisplayName();
+    
+    public void addRecipe(T recipe);
+    
+    public void resetRecipes();
+    
+    public List<AEISlot> setupDisplay(int number);
+    
+    public boolean canDisplay(T recipe);
+    
+    public void drawExtras();
+    
+    public void addWidget(List<Control> controls, int number);
+}

+ 7 - 0
src/main/java/me/shedaniel/api/IDisplayHelper.java

@@ -0,0 +1,7 @@
+package me.shedaniel.api;
+
+/**
+ * Created by James on 8/7/2018.
+ */
+public interface IDisplayHelper {
+}

+ 10 - 0
src/main/java/me/shedaniel/api/IDrawable.java

@@ -0,0 +1,10 @@
+package me.shedaniel.api;
+
+/**
+ * Created by James on 8/7/2018.
+ */
+public interface IDrawable {
+    public void draw();
+
+    public boolean isHighlighted();
+}

+ 15 - 0
src/main/java/me/shedaniel/api/IRecipe.java

@@ -0,0 +1,15 @@
+package me.shedaniel.api;
+
+import java.util.List;
+
+/**
+ * Created by James on 7/27/2018.
+ */
+public interface IRecipe<T> {
+
+    public String getId();
+
+    public List<T> getOutput();
+
+    public List<List<T>> getInput();
+}

+ 20 - 0
src/main/java/me/shedaniel/api/IRecipeManager.java

@@ -0,0 +1,20 @@
+package me.shedaniel.api;
+
+import net.minecraft.item.ItemStack;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Created by James on 8/5/2018.
+ */
+public interface IRecipeManager {
+    
+    public void addRecipe(String id, IRecipe recipe);
+    
+    public void addRecipe(String id, List<? extends IRecipe> recipes);
+    
+    public void addDisplayAdapter(IDisplayCategory adapter);
+    
+    public Map<IDisplayCategory, List<IRecipe>> getRecipesFor(ItemStack stack);
+}

+ 8 - 0
src/main/java/me/shedaniel/api/TriBooleanProducer.java

@@ -0,0 +1,8 @@
+package me.shedaniel.api;
+
+/**
+ * Created by James on 8/4/2018.
+ */
+public interface TriBooleanProducer {
+    public boolean accept(int first, int second, int third);
+}

+ 235 - 0
src/main/java/me/shedaniel/gui/AEIRenderHelper.java

@@ -0,0 +1,235 @@
+package me.shedaniel.gui;
+
+import me.shedaniel.gui.widget.AEISlot;
+import me.shedaniel.gui.widget.Control;
+import me.shedaniel.gui.widget.IFocusable;
+import me.shedaniel.impl.AEIRecipeManager;
+import me.shedaniel.library.KeyBindManager;
+import me.shedaniel.listenerdefinitions.IMixinGuiContainer;
+import net.minecraft.client.MainWindow;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.FontRenderer;
+import net.minecraft.client.gui.inventory.GuiContainer;
+import net.minecraft.client.renderer.GlStateManager;
+import net.minecraft.client.renderer.ItemRenderer;
+import net.minecraft.item.ItemStack;
+
+import java.awt.*;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+
+/**
+ * Created by James on 7/28/2018.
+ */
+public class AEIRenderHelper {
+    static Point mouseLoc;
+    static public GuiItemList aeiGui;
+    static GuiContainer overlayedGui;
+    static List<TooltipData> tooltipsToRender = new ArrayList<>();
+    
+    public static void setMouseLoc(int x, int y) {
+        mouseLoc = new Point(x, y);
+    }
+    
+    static public IFocusable focusedControl;
+    
+    public static Point getMouseLoc() {
+        return mouseLoc;
+    }
+    
+    public static MainWindow getResolution() {
+        
+        return Minecraft.getInstance().mainWindow;
+    }
+    
+    public static void drawAEI(GuiContainer overlayedGui) {
+        AEIRenderHelper.overlayedGui = overlayedGui;
+        if (aeiGui == null) {
+            aeiGui = new GuiItemList(overlayedGui);
+        }
+        aeiGui.draw();
+        renderTooltips();
+    }
+    
+    public static void resize() {
+        if (aeiGui != null) {
+            aeiGui.resize();
+        }
+        if (overlayedGui instanceof RecipeGui) {
+            overlayedGui.onResize(Minecraft.getInstance(), 0, 0);
+        }
+    }
+    
+    public static ItemRenderer getItemRender() {
+        return Minecraft.getInstance().getItemRenderer();
+    }
+    
+    public static FontRenderer getFontRenderer() {
+        return Minecraft.getInstance().fontRenderer;
+    }
+    
+    public static GuiContainer getOverlayedGui() {
+        if (overlayedGui instanceof GuiContainer)
+            return overlayedGui;
+        return null;
+    }
+    
+    public static void addToolTip(List<String> text, int x, int y) {
+        tooltipsToRender.add(new TooltipData(text, x, y));
+    }
+    
+    
+    private static void renderTooltips() {
+        GlStateManager.pushMatrix();
+        GlStateManager.enableLighting();
+        for(TooltipData tooltipData : tooltipsToRender) {
+            getOverlayedGui().drawHoveringText(tooltipData.text, tooltipData.x, tooltipData.y);
+        }
+        GlStateManager.disableLighting();
+        tooltipsToRender.clear();
+        GlStateManager.popMatrix();
+        
+    }
+    
+    public static boolean mouseClick(int x, int y, int button) {
+        if (aeiGui.visible) {
+            for(Control control : aeiGui.controls) {
+                if (control.isHighlighted() && control.isEnabled() && control.onClick != null) {
+                    if (focusedControl != null)
+                        focusedControl.setFocused(false);
+                    if (control instanceof IFocusable) {
+                        focusedControl = (IFocusable) control;
+                        ((IFocusable) control).setFocused(true);
+                    }
+                    return control.onClick.apply(button);
+                }
+            }
+            if (focusedControl != null) {
+                focusedControl.setFocused(false);
+                focusedControl = null;
+            }
+        }
+        if (overlayedGui instanceof RecipeGui) {
+            List<Control> controls = ((RecipeGui) overlayedGui).controls;
+            Optional<Control> ctrl = controls.stream().filter(Control::isHighlighted).filter(Control::isEnabled).findFirst();
+            if (ctrl.isPresent()) {
+                try {
+                    return ctrl.get().onClick.apply(button);
+                } catch (Exception e) {
+                }
+            }
+        }
+        return false;
+    }
+    
+    public static boolean keyDown(int typedChar, int keyCode, int unknown) {
+        boolean handled = false;
+        if (focusedControl != null && focusedControl instanceof Control) {
+            Control control = (Control) focusedControl;
+            if (control.onKeyDown != null) {
+                handled = control.onKeyDown.accept(typedChar, keyCode, unknown);
+            }
+            if (control.charPressed != null)
+                if (typedChar == 256) {
+                    ((IFocusable) control).setFocused(false);
+                    focusedControl = null;
+                }
+            handled = true;
+        }
+        if (!handled) {
+            return KeyBindManager.processGuiKeybinds(typedChar);
+        }
+        return handled;
+    }
+    
+    public static boolean charInput(long num, int keyCode, int unknown) {
+        if (focusedControl != null && focusedControl instanceof Control) {
+            Control control = (Control) focusedControl;
+            if (control.charPressed != null) {
+                int numChars = Character.charCount(keyCode);
+                if (num == numChars)
+                    control.charPressed.accept((char) keyCode, unknown);
+                else {
+                    char[] chars = Character.toChars(keyCode);
+                    for(int x = 0; x < chars.length; x++) {
+                        control.charPressed.accept(chars[x], unknown);
+                    }
+                }
+                return true;
+            }
+        }
+        return false;
+    }
+    
+    public static boolean mouseScrolled(double direction) {
+        if (!aeiGui.visible)
+            return false;
+        if (direction > 0 && aeiGui.buttonLeft.isEnabled())
+            aeiGui.buttonLeft.onClick.apply(0);
+        else if (direction < 0 && aeiGui.buttonRight.isEnabled())
+            aeiGui.buttonRight.onClick.apply(0);
+        return true;
+    }
+    
+    private static class TooltipData {
+        
+        private final List<String> text;
+        private final int x;
+        private final int y;
+        
+        public TooltipData(List<String> text, int x, int y) {
+            this.text = text;
+            this.x = x;
+            this.y = y;
+        }
+    }
+    
+    public static void updateSearch() {
+        aeiGui.updateView();
+    }
+    
+    public static void tick() {
+        if (aeiGui != null && Minecraft.getInstance().currentScreen == overlayedGui)
+            aeiGui.tick();
+    }
+    
+    public static void recipeKeybind() {
+        if (!(Minecraft.getInstance().currentScreen instanceof GuiContainer))
+            return;
+        Control control = aeiGui.getLastHovered();
+        if (control != null && control.isHighlighted() && control instanceof AEISlot) {
+            AEISlot slot = (AEISlot) control;
+            AEIRecipeManager.instance().displayRecipesFor(slot.getStack());
+            return;
+        }
+        if (((IMixinGuiContainer) overlayedGui).getHoveredSlot() != null) {
+            ItemStack stack = ((IMixinGuiContainer) overlayedGui).getHoveredSlot().getStack();
+            AEIRecipeManager.instance().displayRecipesFor(stack);
+        }
+        
+    }
+    
+    public static void useKeybind() {
+        if (!(Minecraft.getInstance().currentScreen instanceof GuiContainer))
+            return;
+        Control control = aeiGui.getLastHovered();
+        if (control != null && control.isHighlighted() && control instanceof AEISlot) {
+            AEISlot slot = (AEISlot) control;
+            AEIRecipeManager.instance().displayUsesFor(slot.getStack());
+            return;
+        }
+        if (((IMixinGuiContainer) overlayedGui).getHoveredSlot() != null) {
+            ItemStack stack = ((IMixinGuiContainer) overlayedGui).getHoveredSlot().getStack();
+            AEIRecipeManager.instance().displayUsesFor(stack);
+        }
+        
+    }
+    
+    public static void hideKeybind() {
+        if (Minecraft.getInstance().currentScreen == overlayedGui && aeiGui != null) {
+            aeiGui.visible = !aeiGui.visible;
+        }
+    }
+}

+ 33 - 0
src/main/java/me/shedaniel/gui/Drawable.java

@@ -0,0 +1,33 @@
+package me.shedaniel.gui;
+
+import me.shedaniel.api.IDrawable;
+import me.shedaniel.gui.widget.Control;
+
+import java.awt.*;
+
+/**
+ * Created by James on 7/28/2018.
+ */
+public abstract class Drawable implements IDrawable {
+    protected Rectangle rect;
+    
+    public Drawable(int x, int y, int width, int height) {
+        rect = new Rectangle(x, y, width, height);
+    }
+    
+    public Drawable(Rectangle rect) {
+        this.rect = rect;
+    }
+    
+    public abstract void draw();
+    
+    public boolean isHighlighted() {
+        Point mousePoint = AEIRenderHelper.getMouseLoc();
+        if (rect.contains(mousePoint.x, mousePoint.y)) {
+            if (this instanceof Control)
+                AEIRenderHelper.aeiGui.setLastHovered((Control) this);
+            return true;
+        }
+        return false;
+    }
+}

+ 264 - 0
src/main/java/me/shedaniel/gui/GuiItemList.java

@@ -0,0 +1,264 @@
+package me.shedaniel.gui;
+
+import me.shedaniel.ClientListener;
+import me.shedaniel.gui.widget.AEISlot;
+import me.shedaniel.gui.widget.Button;
+import me.shedaniel.gui.widget.Control;
+import me.shedaniel.gui.widget.TextBox;
+import me.shedaniel.listenerdefinitions.IMixinGuiContainer;
+import net.minecraft.client.MainWindow;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.inventory.GuiContainer;
+import net.minecraft.client.renderer.GlStateManager;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.ResourceLocation;
+import net.minecraft.util.math.MathHelper;
+import net.minecraft.util.registry.IRegistry;
+import net.minecraft.util.text.TextComponentTranslation;
+
+import java.awt.*;
+import java.util.ArrayList;
+
+public class GuiItemList extends Drawable {
+    
+    public static final int FOOTERSIZE = 44;
+    private GuiContainer overlayedGui;
+    private static int page = 0;
+    private ArrayList<AEISlot> displaySlots;
+    protected ArrayList<Control> controls;
+    private boolean needsResize = false;
+    Button buttonLeft;
+    Button buttonRight;
+    Button buttonCheating;
+    TextBox searchBox;
+    private ArrayList<ItemStack> view;
+    private Control lastHovered;
+    protected boolean visible = true;
+    private int oldGuiLeft = 0;
+    private boolean cheatMode = false;
+    
+    public GuiItemList(GuiContainer overlayedGui) {
+        super(calculateRect(overlayedGui));
+        displaySlots = new ArrayList<>();
+        controls = new ArrayList<>();
+        this.overlayedGui = overlayedGui;
+        view = new ArrayList<>();
+        resize();
+    }
+    
+    public boolean canCheat() {
+        EntityPlayer player = Minecraft.getInstance().player;
+        if (cheatMode) {
+            if (!player.hasPermissionLevel(1)) {
+                cheatClicked(0);
+                return false;
+            }
+            return true;
+        }
+        return false;
+    }
+    
+    private static Rectangle calculateRect(GuiContainer overlayedGui) {
+        MainWindow res = AEIRenderHelper.getResolution();
+        int startX = (((IMixinGuiContainer) overlayedGui).getGuiLeft() + ((IMixinGuiContainer) overlayedGui).getXSize()) + 10;
+        int width = res.getScaledWidth() - startX;
+        return new Rectangle(startX, 0, width, res.getScaledHeight());
+    }
+    
+    protected void resize() {
+        MainWindow res = AEIRenderHelper.getResolution();
+        
+        if (overlayedGui != Minecraft.getInstance().currentScreen) {
+            if (Minecraft.getInstance().currentScreen instanceof GuiContainer) {
+                overlayedGui = (GuiContainer) Minecraft.getInstance().currentScreen;
+                
+            } else {
+                needsResize = true;
+                return;
+            }
+        }
+        oldGuiLeft = ((IMixinGuiContainer) overlayedGui).getGuiLeft();
+        rect = calculateRect(overlayedGui);
+        page = 0;
+        buttonLeft = new Button(rect.x, rect.y + 3, 16, 20, "<");
+        buttonLeft.onClick = this::btnLeftClicked;
+        buttonRight = new Button(rect.x + rect.width - 18, rect.y + 3, 16, 20, ">");
+        buttonRight.onClick = this::btnRightClicked;
+        controls.clear();
+        controls.add(buttonLeft);
+        controls.add(buttonRight);
+        String savedText = "";
+        if (searchBox != null) {
+            savedText = searchBox.getText();
+        }
+        searchBox = new TextBox(rect.x, rect.height - 31, rect.width - 4, 18);
+        searchBox.setText(savedText);
+        controls.add(searchBox);
+        buttonCheating = new Button(5, 5, 45, 20, getCheatModeText());
+        buttonCheating.onClick = this::cheatClicked;
+        controls.add(buttonCheating);
+        calculateSlots();
+        updateView();
+        fillSlots();
+        controls.addAll(displaySlots);
+    }
+    
+    private void fillSlots() {
+        page = MathHelper.clamp(page, 0, (int) Math.floor(view.size() / displaySlots.size()));
+        int firstSlot = page * displaySlots.size();
+        for(int i = 0; i < displaySlots.size(); i++) {
+            if (firstSlot + i < view.size() && firstSlot + i >= 0) {
+                displaySlots.get(i).setStack(view.get(firstSlot + i));
+            } else {
+                displaySlots.get(i).setStack(ItemStack.EMPTY);
+            }
+        }
+    }
+    
+    private void calculateSlots() {
+        int x = rect.x;
+        int y = rect.y + 20;
+        MainWindow res = AEIRenderHelper.getResolution();
+        displaySlots.clear();
+        int xOffset = 4;
+        int yOffset = 4;
+        while (true) {
+            AEISlot slot = new AEISlot(x + xOffset, y + yOffset);
+            slot.setCheatable(true);
+            xOffset += 18;
+            displaySlots.add(slot);
+            if (x + xOffset + 18 > res.getScaledWidth()) {
+                xOffset = 4;
+                yOffset += 18;
+            }
+            if (y + yOffset + 9 + FOOTERSIZE > rect.height) {
+                break;
+            }
+        }
+    }
+    
+    @Override
+    public void draw() {
+        if (!visible)
+            return;
+        if (needsResize == true)
+            resize();
+        if (oldGuiLeft != ((IMixinGuiContainer) overlayedGui).getGuiLeft())
+            resize();
+        GlStateManager.pushMatrix();
+        updateButtons();
+        controls.forEach(Control::draw);
+        String header = String.format("%s/%s", page + 1, ((int) Math.floor(view.size() / displaySlots.size())) + 1);
+        Minecraft.getInstance().fontRenderer.drawStringWithShadow(header, rect.x + (rect.width / 2) - (Minecraft.getInstance().fontRenderer.getStringWidth(header) / 2), rect.y + 10, -1);
+        GlStateManager.popMatrix();
+    }
+    
+    private void updateButtons() {
+        if (page == 0)
+            buttonLeft.setEnabled(false);
+        else
+            buttonLeft.setEnabled(true);
+        if (displaySlots.size() + displaySlots.size() * page >= view.size())
+            buttonRight.setEnabled(false);
+        else
+            buttonRight.setEnabled(true);
+    }
+    
+    
+    public boolean btnRightClicked(int button) {
+        if (button == 0) {
+            page++;
+            fillSlots();
+            return true;
+        }
+        return false;
+    }
+    
+    public boolean btnLeftClicked(int button) {
+        if (button == 0) {
+            page--;
+            fillSlots();
+            return true;
+        }
+        return false;
+    }
+    
+    public boolean cheatClicked(int button) {
+        if (button == 0) {
+            cheatMode = !cheatMode;
+            
+            buttonCheating.setString(getCheatModeText());
+            return true;
+        }
+        return false;
+    }
+    
+    private String getCheatModeText() {
+        if (cheatMode) {
+            TextComponentTranslation cheat = new TextComponentTranslation("text.aei.cheat", new Object[]{null});
+            return cheat.getFormattedText();
+        }
+        TextComponentTranslation noCheat = new TextComponentTranslation("text.aei.nocheat", new Object[]{null});
+        return noCheat.getFormattedText();
+    }
+    
+    protected void updateView() {
+        String searchText = searchBox.getText();
+        String modText = null;
+        if (searchText.contains("@")) {
+            int nextBreak = searchText.indexOf(' ', searchText.indexOf('@'));
+            if (nextBreak == 0 || nextBreak == -1)
+                nextBreak = searchText.length();
+            modText = searchText.substring(searchText.indexOf('@'), nextBreak);
+            searchText = searchText.replace(modText, "").trim();
+            modText = modText.replace("@", "").toLowerCase();
+        }
+        
+        view.clear();
+        if (searchText.equals("") || searchText == null) {
+            for(ItemStack stack : ClientListener.stackList) {
+                if (modText != null) {
+                    if (getMod(stack).contains(modText)) {
+                        view.add(stack);
+                    }
+                } else {
+                    view.add(stack);
+                }
+            }
+        } else {
+            for(ItemStack stack : ClientListener.stackList) {
+                if (stack.getItem().getName().getString().toLowerCase().contains(searchText))
+                    if (modText != null) {
+                        if (getMod(stack).contains(modText)) {
+                            view.add(stack);
+                        }
+                    } else {
+                        view.add(stack);
+                    }
+            }
+        }
+        page = 0;
+        fillSlots();
+    }
+    
+    public void tick() {
+        controls.forEach(f -> f.tick());
+    }
+    
+    public void setLastHovered(Control ctrl) {
+        lastHovered = ctrl;
+    }
+    
+    public Control getLastHovered() {
+        return lastHovered;
+    }
+    
+    private String getMod(ItemStack stack) {
+        if (stack != null && !stack.isEmpty()) {
+            ResourceLocation location = IRegistry.ITEM.getKey(stack.getItem());
+            return location.getNamespace();
+        }
+        return "";
+    }
+}

+ 13 - 0
src/main/java/me/shedaniel/gui/RecipeContainer.java

@@ -0,0 +1,13 @@
+package me.shedaniel.gui;
+
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.inventory.Container;
+
+public class RecipeContainer extends Container {
+    
+    @Override
+    public boolean canInteractWith(EntityPlayer entityPlayer) {
+        return true;
+    }
+}

+ 218 - 0
src/main/java/me/shedaniel/gui/RecipeGui.java

@@ -0,0 +1,218 @@
+package me.shedaniel.gui;
+
+import me.shedaniel.api.IDisplayCategory;
+import me.shedaniel.api.IRecipe;
+import me.shedaniel.gui.widget.AEISlot;
+import me.shedaniel.gui.widget.Button;
+import me.shedaniel.gui.widget.Control;
+import net.minecraft.client.MainWindow;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.GuiScreen;
+import net.minecraft.client.gui.inventory.GuiContainer;
+import net.minecraft.client.renderer.GlStateManager;
+import net.minecraft.inventory.Container;
+import net.minecraft.util.ResourceLocation;
+import net.minecraft.util.math.MathHelper;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+public class RecipeGui extends GuiContainer {
+    private static final ResourceLocation CHEST_GUI_TEXTURE = new ResourceLocation("almostenoughitems", "textures/gui/recipecontainer.png");
+    private final MainWindow mainWindow;
+    private final Container container;
+    private final GuiScreen prevScreen;
+    private final Map<IDisplayCategory, List<IRecipe>> recipes;
+    private int guiWidth = 176;
+    private int guiHeight = 222;
+    ArrayList<IDisplayCategory> categories = new ArrayList<>();
+    private int categoryPointer = 0;
+    private int recipePointer = 0;
+    private List<AEISlot> slots;
+    private int cycleCounter = 0;
+    private int[] itemPointer;
+    List<Control> controls = new LinkedList<>();
+    
+    public RecipeGui(Container p_i1072_1_, GuiScreen prevScreen, Map<IDisplayCategory, List<IRecipe>> recipes) {
+        super(new RecipeContainer());
+        this.container = p_i1072_1_;
+        this.prevScreen = prevScreen;
+        this.recipes = recipes;
+        this.mc = Minecraft.getInstance();
+        this.itemRender = mc.getItemRenderer();
+        this.fontRenderer = mc.fontRenderer;
+        this.mainWindow = Minecraft.getInstance().mainWindow;
+        
+        setupCategories();
+    }
+    
+    private void setupCategories() {
+        categories.addAll(recipes.keySet());
+        updateRecipe();
+    }
+    
+    
+    @Override
+    public void render(int mouseX, int mouseY, float partialTicks) {
+        super.render(mouseX, mouseY, partialTicks);
+        int y = (int) ((mainWindow.getScaledHeight() / 2 - this.guiHeight / 2));
+        drawCenteredString(this.fontRenderer, categories.get(categoryPointer).getDisplayName(), guiLeft + guiWidth / 2, y + 11, -1);
+        drawCenteredString(this.fontRenderer, String.format("%d/%d", 1 + getCurrentPage(), getTotalPages()), guiLeft + guiWidth / 2, y + 34, -1);
+        controls.forEach(Control::draw);
+    }
+    
+    private int getCurrentPage() {
+        return recipePointer / 2;
+    }
+    
+    @Override
+    public void tick() {
+        super.tick();
+        slots.forEach(AEISlot::tick);
+        controls.forEach(Control::tick);
+    }
+    
+    
+    @Override
+    public void onResize(Minecraft p_onResize_1_, int p_onResize_2_, int p_onResize_3_) {
+        super.onResize(p_onResize_1_, p_onResize_2_, p_onResize_3_);
+        updateRecipe();
+    }
+    
+    private void updateRecipe() {
+        IRecipe recipe = recipes.get(categories.get(categoryPointer)).get(recipePointer);
+        categories.get(categoryPointer).resetRecipes();
+        categories.get(categoryPointer).addRecipe(recipe);
+        slots = categories.get(categoryPointer).setupDisplay(0);
+        if (recipes.get(categories.get(categoryPointer)).size() >= categoryPointer + 2) {
+            IRecipe recipe2 = recipes.get(categories.get(categoryPointer)).get(recipePointer + 1);
+            categories.get(categoryPointer).addRecipe(recipe2);
+            slots.addAll(categories.get(categoryPointer).setupDisplay(1));
+        }
+        
+        guiLeft = (int) ((mainWindow.getScaledWidth() / 2 - this.guiWidth / 2));
+        guiTop = (int) ((mainWindow.getScaledHeight() / 2 - this.guiHeight / 2));
+        
+        for(AEISlot slot : slots) {
+            slot.move(guiLeft, guiTop);
+        }
+        
+        Button btnCategoryLeft = new Button(guiLeft + 10, guiTop + 5, 15, 20, "<");
+        Button btnCategoryRight = new Button(guiLeft + guiWidth - 25, guiTop + 5, 15, 20, ">");
+        btnCategoryRight.onClick = this::btnCategoryRight;
+        btnCategoryLeft.onClick = this::btnCategoryLeft;
+        
+        Button btnRecipeLeft = new Button(guiLeft + 10, guiTop + 28, 15, 20, "<");
+        Button btnRecipeRight = new Button(guiLeft + guiWidth - 25, guiTop + 28, 15, 20, ">");
+        btnRecipeLeft.setEnabled(recipes.get(categories.get(categoryPointer)).size() > 1 && recipePointer > 0);
+        btnRecipeRight.setEnabled(recipes.get(categories.get(categoryPointer)).size() > 1 && getCurrentPage() + 1 < getTotalPages());
+        btnRecipeRight.onClick = this::btnRecipeRight;
+        btnRecipeLeft.onClick = this::btnRecipeLeft;
+        
+        controls.clear();
+        controls.add(btnCategoryLeft);
+        controls.add(btnCategoryRight);
+        if (categories.size() <= 2) {
+            btnCategoryLeft.setEnabled(false);
+            btnCategoryRight.setEnabled(false);
+        }
+        
+        controls.add(btnRecipeLeft);
+        controls.add(btnRecipeRight);
+        
+        itemPointer = new int[9];
+        for(int i = 0; i < itemPointer.length; i++) {
+            itemPointer[i] = 0;
+        }
+        
+        List<Control> newControls = new LinkedList<>();
+        categories.get(categoryPointer).addWidget(newControls, 0);
+        if (recipes.get(categories.get(categoryPointer)).size() >= categoryPointer + 2) {
+            categories.get(categoryPointer).addWidget(newControls, 1);
+        }
+        newControls.forEach(f -> f.move(guiLeft, guiTop));
+        controls.addAll(newControls);
+    }
+    
+    @Override
+    protected void drawGuiContainerBackgroundLayer(float v, int i, int i1) {
+        drawDefaultBackground();
+        GlStateManager.color4f(1.0F, 1.0F, 1.0F, 1.0F);
+        this.mc.getTextureManager().bindTexture(CHEST_GUI_TEXTURE);
+        
+        int lvt_4_1_ = (int) ((mainWindow.getScaledWidth() / 2 - this.guiWidth / 2));
+        int lvt_5_1_ = (int) ((mainWindow.getScaledHeight() / 2 - this.guiHeight / 2));
+        
+        this.drawTexturedModalRect(lvt_4_1_, lvt_5_1_, 0, 0, this.guiWidth, this.guiHeight);
+        slots.forEach(AEISlot::draw);
+    }
+    
+    
+    @Override
+    protected void initGui() {
+        super.initGui();
+    }
+    
+    @Override
+    public boolean keyPressed(int p_keyPressed_1_, int p_keyPressed_2_, int p_keyPressed_3_) {
+        if (p_keyPressed_1_ == 259 && prevScreen != null && AEIRenderHelper.focusedControl == null) {
+            Minecraft.getInstance().displayGuiScreen(prevScreen);
+            return true;
+        }
+        
+        return super.keyPressed(p_keyPressed_1_, p_keyPressed_2_, p_keyPressed_3_);
+    }
+    
+    @Override
+    public void onGuiClosed() {
+        super.onGuiClosed();
+    }
+    
+    private boolean btnCategoryLeft(int button) {
+        recipePointer = 0;
+        categoryPointer--;
+        if (categoryPointer < 0) {
+            categoryPointer = categories.size() - 1;
+        }
+        updateRecipe();
+        return true;
+    }
+    
+    private boolean btnCategoryRight(int button) {
+        recipePointer = 0;
+        categoryPointer++;
+        if (categoryPointer >= categories.size()) {
+            categoryPointer = 0;
+        }
+        updateRecipe();
+        return true;
+    }
+    
+    private boolean btnRecipeLeft(int button) {
+        recipePointer -= 2;
+        if (recipePointer < 0) {
+            recipePointer = (getTotalPages() - 1) * 2;
+        }
+        updateRecipe();
+        return true;
+    }
+    
+    private boolean btnRecipeRight(int button) {
+        recipePointer += 2;
+        if (recipePointer >= recipes.get(categories.get(categoryPointer)).size()) {
+            recipePointer = 0;
+        }
+        updateRecipe();
+        return true;
+    }
+    
+    private int riseDoublesToInt(double i) {
+        return (int) (i + (i % 1 == 0 ? 0 : 1));
+    }
+    
+    private int getTotalPages() {
+        return MathHelper.clamp(riseDoublesToInt(recipes.get(categories.get(categoryPointer)).size() / 2), 1, Integer.MAX_VALUE);
+    }
+}

+ 181 - 0
src/main/java/me/shedaniel/gui/widget/AEISlot.java

@@ -0,0 +1,181 @@
+package me.shedaniel.gui.widget;
+
+import com.google.common.collect.Lists;
+import me.shedaniel.gui.AEIRenderHelper;
+import me.shedaniel.listenerdefinitions.IMixinGuiContainer;
+import me.shedaniel.network.CheatPacket;
+import me.shedaniel.network.DeletePacket;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.inventory.GuiContainer;
+import net.minecraft.client.renderer.RenderHelper;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.ResourceLocation;
+import net.minecraft.util.registry.IRegistry;
+
+import java.awt.*;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Created by James on 7/28/2018.
+ */
+public class AEISlot extends Control {
+    private static final ResourceLocation RECIPE_GUI = new ResourceLocation("almostenoughitems", "textures/gui/recipecontainer.png");
+    private boolean cheatable = false;
+    private List<ItemStack> itemList = new LinkedList<>();
+    private int itemListPointer = 0;
+    private long displayCounter = 0;
+    
+    public boolean isDrawBackground() {
+        return drawBackground;
+    }
+    
+    private String extraTooltip;
+    
+    
+    @Override
+    public void tick() {
+        if (itemList.size() > 1) {
+            displayCounter++;
+            if (displayCounter % 10 == 0)
+                if (itemListPointer + 1 >= itemList.size())
+                    itemListPointer = 0;
+                else itemListPointer++;
+        }
+    }
+    
+    public void setStackList(List<ItemStack> newItemList) {
+        itemList = newItemList;
+        itemListPointer = 0;
+        displayCounter = 0;
+    }
+    
+    public void setExtraTooltip(String toolTip) {
+        extraTooltip = toolTip;
+    }
+    
+    public void setDrawBackground(boolean drawBackground) {
+        this.drawBackground = drawBackground;
+    }
+    
+    private boolean drawBackground = false;
+    private Point backgroundUV = new Point(0, 222);
+    
+    public AEISlot(int x, int y) {
+        super(x, y, 18, 18);
+        this.onClick = this::onClick;
+    }
+    
+    public ItemStack getStack() {
+        if (itemList.isEmpty()) {
+            return ItemStack.EMPTY;
+        }
+        return itemList.get(itemListPointer);
+    }
+    
+    public void setStack(ItemStack stack) {
+        itemList.clear();
+        if (stack != null)
+            itemList.add(stack);
+        itemListPointer = 0;
+    }
+    
+    @Override
+    public void draw() {
+        if (drawBackground) {
+            Minecraft.getInstance().getTextureManager().bindTexture(RECIPE_GUI);
+            drawTexturedModalRect(rect.x - 1, rect.y - 1, backgroundUV.x, backgroundUV.y, rect.width, rect.height);
+        }
+        if (getStack().isEmpty())
+            return;
+        RenderHelper.enableGUIStandardItemLighting();
+        
+        drawStack(rect.x, rect.y);
+        if (isHighlighted())
+            drawTooltip();
+    }
+    
+    private void drawTooltip() {
+        List<String> toolTip = getTooltip();
+        toolTip.add("§9" + getMod());
+        Point mouse = AEIRenderHelper.getMouseLoc();
+        AEIRenderHelper.addToolTip(toolTip, mouse.x, mouse.y);
+    }
+    
+    private boolean onClick(int button) {
+        EntityPlayer player = Minecraft.getInstance().player;
+        if (AEIRenderHelper.aeiGui.canCheat() && !(player.inventory.getItemStack().isEmpty())) {
+            //Delete the itemstack.
+            Minecraft.getInstance().getConnection().sendPacket(new DeletePacket());
+            return true;
+        }
+        if (!player.inventory.getItemStack().isEmpty()) {
+            return false;
+        }
+        
+        if (AEIRenderHelper.aeiGui.canCheat() && this.cheatable) {
+            if (getStack() != null && !getStack().isEmpty()) {
+                ItemStack cheatedStack = getStack().copy();
+                if (button == 0)
+                    cheatedStack.setCount(1);
+                if (button == 1) {
+                    cheatedStack.setCount(cheatedStack.getMaxStackSize());
+                }
+                Minecraft.getInstance().getConnection().sendPacket(new CheatPacket(cheatedStack));
+                return true;
+            }
+        } else {
+            AEIRenderHelper.recipeKeybind();
+        }
+        return false;
+    }
+    
+    
+    private void drawStack(int x, int y) {
+        GuiContainer gui = AEIRenderHelper.getOverlayedGui();
+        AEIRenderHelper.getItemRender().zLevel = 200.0F;
+        AEIRenderHelper.getItemRender().renderItemAndEffectIntoGUI(getStack(), x, y);
+        assert gui != null;
+        if (((IMixinGuiContainer) gui).getDraggedStack().isEmpty())
+            AEIRenderHelper.getItemRender().renderItemOverlayIntoGUI(Minecraft.getInstance().fontRenderer, getStack(), x, y - 0, "");
+        else
+            AEIRenderHelper.getItemRender().renderItemOverlayIntoGUI(Minecraft.getInstance().fontRenderer, getStack(), x, y - 8, "");
+        AEIRenderHelper.getItemRender().zLevel = 0.0F;
+    }
+    
+    public String getMod() {
+        if (!getStack().isEmpty()) {
+            ResourceLocation location = IRegistry.ITEM.getKey(getStack().getItem());
+            assert location != null;
+            return location.getNamespace();
+        }
+        return "";
+    }
+    
+    private List<String> getTooltip() {
+        Minecraft mc = Minecraft.getInstance();
+        GuiContainer gui = AEIRenderHelper.getOverlayedGui();
+        List<String> toolTip = Lists.newArrayList();
+        if (gui != null) {
+            toolTip = gui.getItemToolTip(getStack());
+        } else {
+            toolTip.add(getStack().getDisplayName().getFormattedText());
+        }
+        if (extraTooltip != null) {
+            toolTip.add(extraTooltip);
+        }
+        
+        return toolTip;
+    }
+    
+    public boolean isCheatable() {
+        return cheatable;
+    }
+    
+    public void setCheatable(boolean cheatable) {
+        this.cheatable = cheatable;
+    }
+    
+    
+}

+ 72 - 0
src/main/java/me/shedaniel/gui/widget/Button.java

@@ -0,0 +1,72 @@
+package me.shedaniel.gui.widget;
+
+import me.shedaniel.gui.AEIRenderHelper;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.FontRenderer;
+import net.minecraft.client.gui.inventory.GuiContainer;
+import net.minecraft.client.renderer.GlStateManager;
+import net.minecraft.util.ResourceLocation;
+
+import java.awt.*;
+
+/**
+ * Created by James on 7/29/2018.
+ */
+public class Button extends Control {
+    private String buttonText;
+    protected static final ResourceLocation BUTTON_TEXTURES = new ResourceLocation("textures/gui/widgets.png");
+    
+    
+    public Button(int x, int y, int width, int height, String buttonText) {
+        super(x, y, width, height);
+        this.buttonText = buttonText;
+    }
+    
+    public Button(Rectangle rect, String buttonText) {
+        super(rect);
+        this.buttonText = buttonText;
+    }
+    
+    public void setString(String text) {
+        buttonText = text;
+    }
+    
+    
+    @Override
+    public void draw() {
+        GlStateManager.pushMatrix();
+        GlStateManager.disableLighting();
+        GuiContainer gui = AEIRenderHelper.getOverlayedGui();
+        Minecraft lvt_4_1_ = Minecraft.getInstance();
+        FontRenderer lvt_5_1_ = lvt_4_1_.fontRenderer;
+        lvt_4_1_.getTextureManager().bindTexture(BUTTON_TEXTURES);
+        GlStateManager.color4f(1.0F, 1.0F, 1.0F, 1.0F);
+        int hoverState = (byte) 0;
+        if (this.isEnabled()) {
+            if (!this.isHighlighted())
+                hoverState = (byte) 1;
+            else
+                hoverState = (byte) 2;
+        }
+        
+        GlStateManager.enableBlend();
+        GlStateManager.blendFuncSeparate(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO);
+        GlStateManager.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA);
+        gui.drawTexturedModalRect(rect.x, rect.y, 0, 46 + hoverState * 20, rect.width / 2, rect.height);
+        gui.drawTexturedModalRect(rect.x + rect.width / 2, rect.y, 200 - rect.width / 2, 46 + hoverState * 20, rect.width / 2, rect.height);
+        //this.mouseDragged(lvt_4_1_, p_194828_1_, p_194828_2_);
+        int lvt_7_1_ = 14737632;
+//        if(!this.enabled) {
+//            lvt_7_1_ = 10526880;
+//        } else if(this.hovered) {
+//            lvt_7_1_ = 16777120;
+//        }
+        
+        
+        gui.drawCenteredString(lvt_5_1_, this.buttonText, rect.x + rect.width / 2, rect.y + (rect.height - 8) / 2, lvt_7_1_);
+        GlStateManager.enableLighting();
+        GlStateManager.popMatrix();
+    }
+    
+    
+}

+ 96 - 0
src/main/java/me/shedaniel/gui/widget/Control.java

@@ -0,0 +1,96 @@
+package me.shedaniel.gui.widget;
+
+import me.shedaniel.api.TriBooleanProducer;
+import me.shedaniel.gui.Drawable;
+import net.minecraft.client.renderer.BufferBuilder;
+import net.minecraft.client.renderer.GlStateManager;
+import net.minecraft.client.renderer.Tessellator;
+import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
+
+import java.awt.*;
+import java.util.function.BiConsumer;
+import java.util.function.IntFunction;
+
+/**
+ * Created by James on 7/29/2018.
+ */
+public abstract class Control extends Drawable {
+    private boolean enabled = true;
+    public IntFunction<Boolean> onClick;
+    public TriBooleanProducer onKeyDown;
+    public BiConsumer<Character, Integer> charPressed;
+    
+    public Control(int x, int y, int width, int height) {
+        super(x, y, width, height);
+    }
+    
+    public Control(Rectangle rect) {
+        super(rect);
+    }
+    
+    public boolean isEnabled() {
+        return enabled;
+    }
+    
+    public void setEnabled(boolean enabled) {
+        this.enabled = enabled;
+    }
+    
+    public void move(int x, int y) {
+        rect.x += x;
+        rect.y += y;
+        //rect.move(x+rect.x,rect.y+y);//Why the fuck?
+    }
+    
+    protected static void drawRect(int p_drawRect_0_, int p_drawRect_1_, int p_drawRect_2_, int p_drawRect_3_, int p_drawRect_4_) {
+        int lvt_5_3_;
+        if (p_drawRect_0_ < p_drawRect_2_) {
+            lvt_5_3_ = p_drawRect_0_;
+            p_drawRect_0_ = p_drawRect_2_;
+            p_drawRect_2_ = lvt_5_3_;
+        }
+        
+        if (p_drawRect_1_ < p_drawRect_3_) {
+            lvt_5_3_ = p_drawRect_1_;
+            p_drawRect_1_ = p_drawRect_3_;
+            p_drawRect_3_ = lvt_5_3_;
+        }
+        
+        float lvt_5_3_1 = (float) (p_drawRect_4_ >> 24 & 255) / 255.0F;
+        float lvt_6_1_ = (float) (p_drawRect_4_ >> 16 & 255) / 255.0F;
+        float lvt_7_1_ = (float) (p_drawRect_4_ >> 8 & 255) / 255.0F;
+        float lvt_8_1_ = (float) (p_drawRect_4_ & 255) / 255.0F;
+        Tessellator lvt_9_1_ = Tessellator.getInstance();
+        BufferBuilder lvt_10_1_ = lvt_9_1_.getBuffer();
+        GlStateManager.enableAlphaTest();
+        GlStateManager.enableBlend();
+        GlStateManager.disableTexture2D();
+        GlStateManager.blendFuncSeparate(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO);
+        GlStateManager.color4f(lvt_6_1_, lvt_7_1_, lvt_8_1_, lvt_5_3_1);
+        lvt_10_1_.begin(7, DefaultVertexFormats.POSITION);
+        lvt_10_1_.pos((double) p_drawRect_0_, (double) p_drawRect_3_, 0.0D).endVertex();
+        lvt_10_1_.pos((double) p_drawRect_2_, (double) p_drawRect_3_, 0.0D).endVertex();
+        lvt_10_1_.pos((double) p_drawRect_2_, (double) p_drawRect_1_, 0.0D).endVertex();
+        lvt_10_1_.pos((double) p_drawRect_0_, (double) p_drawRect_1_, 0.0D).endVertex();
+        lvt_9_1_.draw();
+        GlStateManager.enableTexture2D();
+        GlStateManager.disableBlend();
+        GlStateManager.disableAlphaTest();
+    }
+    
+    protected void drawTexturedModalRect(int x, int y, int u, int v, int width, int height) {
+        float lvt_7_1_ = 0.00390625F;
+        float lvt_8_1_ = 0.00390625F;
+        Tessellator lvt_9_1_ = Tessellator.getInstance();
+        BufferBuilder lvt_10_1_ = lvt_9_1_.getBuffer();
+        lvt_10_1_.begin(7, DefaultVertexFormats.POSITION_TEX);
+        lvt_10_1_.pos((double) (x + 0), (double) (y + height), (double) 200).tex((double) ((float) (u + 0) * 0.00390625F), (double) ((float) (v + height) * 0.00390625F)).endVertex();
+        lvt_10_1_.pos((double) (x + width), (double) (y + height), (double) 200).tex((double) ((float) (u + width) * 0.00390625F), (double) ((float) (v + height) * 0.00390625F)).endVertex();
+        lvt_10_1_.pos((double) (x + width), (double) (y + 0), (double) 200).tex((double) ((float) (u + width) * 0.00390625F), (double) ((float) (v + 0) * 0.00390625F)).endVertex();
+        lvt_10_1_.pos((double) (x + 0), (double) (y + 0), (double) 200).tex((double) ((float) (u + 0) * 0.00390625F), (double) ((float) (v + 0) * 0.00390625F)).endVertex();
+        lvt_9_1_.draw();
+    }
+    
+    public void tick() {
+    }
+}

+ 10 - 0
src/main/java/me/shedaniel/gui/widget/IFocusable.java

@@ -0,0 +1,10 @@
+package me.shedaniel.gui.widget;
+
+/**
+ * Created by James on 8/3/2018.
+ */
+public interface IFocusable {
+    public boolean hasFocus();
+    
+    public void setFocused(boolean val);
+}

+ 71 - 0
src/main/java/me/shedaniel/gui/widget/TextBox.java

@@ -0,0 +1,71 @@
+package me.shedaniel.gui.widget;
+
+import me.shedaniel.gui.AEIRenderHelper;
+import net.minecraft.client.gui.GuiTextField;
+
+import java.awt.*;
+
+/**
+ * Created by James on 8/3/2018.
+ */
+public class TextBox extends Control implements IFocusable {
+    
+    private GuiTextField textField;
+    
+    public TextBox(int x, int y, int width, int height) {
+        super(x, y, width, height);
+        textField = new GuiTextField(-1, AEIRenderHelper.getFontRenderer(), x, y, width, height);
+        this.onClick = this::doMouseClick;
+        this.onKeyDown = this::onKeyPressed;
+        this.charPressed = this::charTyped;
+    }
+    
+    @Override
+    public void draw() {
+        textField.drawTextField(0, 0, 0);
+    }
+    
+    @Override
+    public boolean hasFocus() {
+        return textField.isFocused();
+    }
+    
+    @Override
+    public void setFocused(boolean val) {
+        textField.setFocused(val);
+    }
+    
+    protected boolean doMouseClick(int button) {
+        Point mouseLoc = AEIRenderHelper.getMouseLoc();
+        if (!hasFocus())
+            setFocused(true);
+        return textField.mouseClicked(mouseLoc.x, mouseLoc.y, 0);
+    }
+    
+    protected boolean onKeyPressed(int first, int second, int third) {
+        boolean handled = textField.keyPressed(first, second, third);
+        if (handled) {
+            AEIRenderHelper.updateSearch();
+        }
+        
+        return handled;
+    }
+    
+    public String getText() {
+        return textField.getText();
+    }
+    
+    public void setText(String value) {
+        textField.setText(value);
+    }
+    
+    protected void charTyped(char p_charTyped_1_, int p_charTyped_2_) {
+        textField.charTyped(p_charTyped_1_, p_charTyped_2_);
+        AEIRenderHelper.updateSearch();
+    }
+    
+    @Override
+    public void tick() {
+        textField.tick();
+    }
+}

+ 38 - 0
src/main/java/me/shedaniel/gui/widget/WidgetArrow.java

@@ -0,0 +1,38 @@
+package me.shedaniel.gui.widget;
+
+import net.minecraft.client.Minecraft;
+import net.minecraft.util.ResourceLocation;
+
+public class WidgetArrow extends Control {
+    private static final ResourceLocation RECIPE_GUI = new ResourceLocation("almostenoughitems", "textures/gui/recipecontainer.png");
+    private int progress = 0;
+    private int updateTick = 0;
+    private boolean animated;
+    
+    public WidgetArrow(int x, int y, boolean animated) {
+        super(x, y, 22, 18);
+        this.animated = animated;
+    }
+    
+    @Override
+    public void draw() {
+        Minecraft.getInstance().getTextureManager().bindTexture(RECIPE_GUI);
+        this.drawTexturedModalRect(rect.x, rect.y, 18, 222, 22, 18);
+        if (animated) {
+            int width = (int) ((progress / 10f) * 22);
+            this.drawTexturedModalRect(rect.x - 1, rect.y - 1, 40, 222, width, 18);
+        }
+    }
+    
+    @Override
+    public void tick() {
+        updateTick++;
+        if (updateTick >= 20) {
+            updateTick = 0;
+            
+            progress++;
+            if (progress > 10)
+                progress = 0;
+        }
+    }
+}

+ 157 - 0
src/main/java/me/shedaniel/impl/AEIRecipeManager.java

@@ -0,0 +1,157 @@
+package me.shedaniel.impl;
+
+import me.shedaniel.api.IAEIPlugin;
+import me.shedaniel.api.IDisplayCategory;
+import me.shedaniel.api.IRecipe;
+import me.shedaniel.api.IRecipeManager;
+import me.shedaniel.gui.RecipeGui;
+import net.minecraft.client.Minecraft;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+import net.minecraft.item.crafting.RecipeManager;
+import net.minecraft.item.crafting.ShapelessRecipe;
+import net.minecraft.util.ResourceLocation;
+import org.dimdev.riftloader.RiftLoader;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Created by James on 8/7/2018.
+ */
+public class AEIRecipeManager implements IRecipeManager {
+    private Map<String, List<IRecipe>> recipeList;
+    private List<IDisplayCategory> displayAdapters;
+    public static RecipeManager recipeManager;
+    
+    private static AEIRecipeManager myInstance;
+    
+    private AEIRecipeManager() {
+        recipeList = new HashMap<>();
+        displayAdapters = new LinkedList<>();
+    }
+    
+    public static AEIRecipeManager instance() {
+        if (myInstance == null) {
+            System.out.println("Newing me up.");
+            myInstance = new AEIRecipeManager();
+        }
+        return myInstance;
+    }
+    
+    @Override
+    public void addRecipe(String id, IRecipe recipe) {
+        if (recipeList.containsKey(id)) {
+            recipeList.get(id).add(recipe);
+        } else {
+            List<IRecipe> recipes = new LinkedList<>();
+            recipeList.put(id, recipes);
+            recipes.add(recipe);
+        }
+        
+        
+    }
+    
+    @Override
+    public void addRecipe(String id, List<? extends IRecipe> recipes) {
+        if (recipeList.containsKey(id)) {
+            recipeList.get(id).addAll(recipes);
+        } else {
+            List<IRecipe> newRecipeList = new LinkedList<>();
+            recipeList.put(id, newRecipeList);
+            newRecipeList.addAll(recipes);
+        }
+    }
+    
+    @Override
+    public void addDisplayAdapter(IDisplayCategory adapter) {
+        displayAdapters.add(adapter);
+    }
+    
+    @Override
+    public Map<IDisplayCategory, List<IRecipe>> getRecipesFor(ItemStack stack) {
+        Map<IDisplayCategory, List<IRecipe>> categories = new HashMap<>();
+        displayAdapters.forEach(f -> categories.put(f, new LinkedList<>()));
+        for(List<IRecipe> value : recipeList.values()) {
+            for(IRecipe iRecipe : value) {
+                for(Object o : iRecipe.getOutput()) {
+                    if (o instanceof ItemStack) {
+                        if (ItemStack.areItemsEqual(stack, (ItemStack) o)) {
+                            for(IDisplayCategory iDisplayCategory : categories.keySet()) {
+                                if (iDisplayCategory.getId() == iRecipe.getId()) {
+                                    categories.get(iDisplayCategory).add(iRecipe);
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        categories.keySet().removeIf(f -> categories.get(f).isEmpty());
+        return categories;
+    }
+    
+    public Map<IDisplayCategory, List<IRecipe>> getUsesFor(ItemStack stack) {
+        Map<IDisplayCategory, List<IRecipe>> categories = new HashMap<>();
+        displayAdapters.forEach(f -> categories.put(f, new LinkedList<>()));
+        for(List<IRecipe> value : recipeList.values()) {
+            for(IRecipe iRecipe : value) {
+                boolean found = false;
+                for(Object o : iRecipe.getInput()) {
+                    List<ItemStack> input = (List<ItemStack>) o;
+                    
+                    for(ItemStack itemStack : input) {
+                        if (ItemStack.areItemsEqual(itemStack, stack)) {
+                            for(IDisplayCategory iDisplayCategory : categories.keySet()) {
+                                if (iDisplayCategory.getId() == iRecipe.getId()) {
+                                    categories.get(iDisplayCategory).add(iRecipe);
+                                    found = true;
+                                }
+                            }
+                            if (found)
+                                break;
+                        }
+                    }
+                    if (found)
+                        break;
+                }
+            }
+        }
+        categories.keySet().removeIf(f -> categories.get(f).isEmpty());
+        return categories;
+    }
+    
+    
+    public List<IDisplayCategory> getAdatapersForOutput(ItemStack stack) {
+        return null;
+    }
+    
+    public List<IDisplayCategory> getAdaptersForOutput(Item item) {
+        return null;
+    }
+    
+    public void RecipesLoaded(RecipeManager manager) {
+        recipeList.clear();
+        displayAdapters.clear();
+        AEIRecipeManager.instance().recipeManager = manager;
+        RiftLoader.instance.getListeners(IAEIPlugin.class).forEach(IAEIPlugin::register);
+    }
+    
+    public void displayRecipesFor(ItemStack stack) {
+        Map<IDisplayCategory, List<IRecipe>> recipes = AEIRecipeManager.instance().getRecipesFor(stack);
+        if (recipes.isEmpty())
+            return;
+        RecipeGui gui = new RecipeGui(null, Minecraft.getInstance().currentScreen, recipes);
+        Minecraft.getInstance().displayGuiScreen(gui);
+    }
+    
+    public void displayUsesFor(ItemStack stack) {
+        Map<IDisplayCategory, List<IRecipe>> recipes = AEIRecipeManager.instance().getUsesFor(stack);
+        if (recipes.isEmpty())
+            return;
+        RecipeGui gui = new RecipeGui(null, Minecraft.getInstance().currentScreen, recipes);
+        Minecraft.getInstance().displayGuiScreen(gui);
+    }
+}

+ 58 - 0
src/main/java/me/shedaniel/library/KeyBindManager.java

@@ -0,0 +1,58 @@
+package me.shedaniel.library;
+
+import me.shedaniel.listenerdefinitions.PreLoadOptions;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.settings.KeyBinding;
+import org.apache.commons.lang3.ArrayUtils;
+import org.dimdev.rift.listener.client.KeybindHandler;
+
+import java.util.*;
+
+/**
+ * Created by James on 8/7/2018.
+ */
+public class KeyBindManager implements PreLoadOptions, KeybindHandler {
+    
+    private static boolean optionsLoaded = false;
+    private static List<KeyBinding> bindingsToAdd = new ArrayList<>();
+    private static Map<KeyBinding, Sink> bindingFunctions = new HashMap<>();
+    
+    public static KeyBinding createKeybinding(String bindingName, int key, String categoryName, Sink function) {
+        KeyBinding newBinding;
+        newBinding = new KeyBinding(bindingName, key, categoryName);
+        if (optionsLoaded) {
+            ArrayUtils.add(Minecraft.getInstance().gameSettings.keyBindings, newBinding);
+        } else {
+            bindingsToAdd.add(newBinding);
+        }
+        bindingFunctions.put(newBinding, function);
+        addCategoryIfMissing(categoryName);
+        return newBinding;
+    }
+    
+    private static void addCategoryIfMissing(String categoryName) {
+        /*if (!KeyBinding.CATEGORY_ORDER.containsKey(categoryName)){
+            KeyBinding.CATEGORY_ORDER.put(categoryName,KeyBinding.CATEGORY_ORDER.size()+1);
+        }*/
+    }
+    
+    @Override
+    public List<KeyBinding> loadOptions() {
+        optionsLoaded = true;
+        return bindingsToAdd;
+    }
+    
+    @Override
+    public void processKeybinds() {
+        bindingFunctions.keySet().stream().filter(KeyBinding::isPressed).forEach(f -> bindingFunctions.get(f).Sink());
+    }
+    
+    public static boolean processGuiKeybinds(int typedChar) {
+        Optional<KeyBinding> binding = bindingFunctions.keySet().stream().filter(f -> f.getDefault().getKeyCode() == typedChar).findFirst();
+        if (binding.isPresent()) {
+            bindingFunctions.get(binding.get()).Sink();
+            return true;
+        }
+        return false;
+    }
+}

+ 6 - 0
src/main/java/me/shedaniel/library/Sink.java

@@ -0,0 +1,6 @@
+package me.shedaniel.library;
+
+@FunctionalInterface
+public interface Sink {
+    public void Sink();
+}

+ 9 - 0
src/main/java/me/shedaniel/listenerdefinitions/CharInput.java

@@ -0,0 +1,9 @@
+package me.shedaniel.listenerdefinitions;
+
+/**
+ * Created by James on 8/4/2018.
+ */
+public interface CharInput {
+    
+    public boolean charInput(long p_onCharEvent_1_, int p_onCharEvent_3_, int p_onCharEvent_4_);
+}

+ 8 - 0
src/main/java/me/shedaniel/listenerdefinitions/DoneLoading.java

@@ -0,0 +1,8 @@
+package me.shedaniel.listenerdefinitions;
+
+/**
+ * Created by James on 7/27/2018.
+ */
+public interface DoneLoading {
+    void onDoneLoading();
+}

+ 10 - 0
src/main/java/me/shedaniel/listenerdefinitions/DrawContainer.java

@@ -0,0 +1,10 @@
+package me.shedaniel.listenerdefinitions;
+
+import net.minecraft.client.gui.inventory.GuiContainer;
+
+/**
+ * Created by James on 7/27/2018.
+ */
+public interface DrawContainer {
+    public void draw(int mouseX, int mouseY, float dunno, GuiContainer gui);
+}

+ 8 - 0
src/main/java/me/shedaniel/listenerdefinitions/GuiCickListener.java

@@ -0,0 +1,8 @@
+package me.shedaniel.listenerdefinitions;
+
+/**
+ * Created by James on 7/29/2018.
+ */
+public interface GuiCickListener {
+    public boolean onClick(int x, int y, int button);
+}

+ 9 - 0
src/main/java/me/shedaniel/listenerdefinitions/GuiKeyDown.java

@@ -0,0 +1,9 @@
+package me.shedaniel.listenerdefinitions;
+
+/**
+ * Created by James on 8/3/2018.
+ */
+public interface GuiKeyDown {
+    
+    public boolean keyDown(int p_keyPressed_1_, int p_keyPressed_2_, int p_keyPressed_3_);
+}

+ 14 - 0
src/main/java/me/shedaniel/listenerdefinitions/IMixinGuiContainer.java

@@ -0,0 +1,14 @@
+package me.shedaniel.listenerdefinitions;
+
+import net.minecraft.inventory.Slot;
+import net.minecraft.item.ItemStack;
+
+public interface IMixinGuiContainer {
+    public ItemStack getDraggedStack();
+    
+    public int getGuiLeft();
+    
+    public int getXSize();
+    
+    public Slot getHoveredSlot();
+}

+ 8 - 0
src/main/java/me/shedaniel/listenerdefinitions/MinecraftResize.java

@@ -0,0 +1,8 @@
+package me.shedaniel.listenerdefinitions;
+
+/**
+ * Created by James on 7/28/2018.
+ */
+public interface MinecraftResize {
+    public void resize();
+}

+ 5 - 0
src/main/java/me/shedaniel/listenerdefinitions/MouseScrollListener.java

@@ -0,0 +1,5 @@
+package me.shedaniel.listenerdefinitions;
+
+public interface MouseScrollListener {
+    public boolean mouseScrolled(double direction);
+}

+ 9 - 0
src/main/java/me/shedaniel/listenerdefinitions/PreLoadOptions.java

@@ -0,0 +1,9 @@
+package me.shedaniel.listenerdefinitions;
+
+import net.minecraft.client.settings.KeyBinding;
+
+import java.util.List;
+
+public interface PreLoadOptions {
+    public List<KeyBinding> loadOptions();
+}

+ 7 - 0
src/main/java/me/shedaniel/listenerdefinitions/RecipeLoadListener.java

@@ -0,0 +1,7 @@
+package me.shedaniel.listenerdefinitions;
+
+import net.minecraft.item.crafting.RecipeManager;
+
+public interface RecipeLoadListener {
+    public void recipesLoaded(RecipeManager recipeManager);
+}

+ 60 - 0
src/main/java/me/shedaniel/listeners/DrawContainerListener.java

@@ -0,0 +1,60 @@
+package me.shedaniel.listeners;
+
+import me.shedaniel.gui.AEIRenderHelper;
+import me.shedaniel.listenerdefinitions.*;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.GuiScreen;
+import net.minecraft.client.gui.inventory.GuiContainer;
+import net.minecraft.client.gui.inventory.GuiContainerCreative;
+import net.minecraft.item.ItemGroup;
+import org.dimdev.rift.listener.client.ClientTickable;
+
+/**
+ * Created by James on 7/27/2018.
+ */
+public class DrawContainerListener implements DrawContainer, GuiCickListener, GuiKeyDown, CharInput, ClientTickable, MouseScrollListener {
+    @Override
+    public void draw(int x, int y, float dunno, GuiContainer gui) {
+        if (!(gui instanceof GuiContainerCreative) || ((GuiContainerCreative) gui).getSelectedTabIndex() == ItemGroup.INVENTORY.getIndex()) {
+            AEIRenderHelper.setMouseLoc(x, y);
+            AEIRenderHelper.drawAEI(gui);
+        }
+    }
+    
+    @Override
+    public boolean onClick(int x, int y, int button) {
+        GuiScreen gui = Minecraft.getInstance().currentScreen;
+        if (!(gui instanceof GuiContainerCreative) || ((GuiContainerCreative) gui).getSelectedTabIndex() == ItemGroup.INVENTORY.getIndex())
+            return AEIRenderHelper.mouseClick(x, y, button);
+        return false;
+    }
+    
+    @Override
+    public boolean keyDown(int p_keyPressed_1_, int p_keyPressed_2_, int p_keyPressed_3_) {
+        GuiScreen gui = Minecraft.getInstance().currentScreen;
+        if (!(gui instanceof GuiContainerCreative) || ((GuiContainerCreative) gui).getSelectedTabIndex() == ItemGroup.INVENTORY.getIndex())
+            return AEIRenderHelper.keyDown(p_keyPressed_1_, p_keyPressed_2_, p_keyPressed_3_);
+        return false;
+    }
+    
+    @Override
+    public boolean charInput(long p_onCharEvent_1_, int p_onCharEvent_3_, int p_onCharEvent_4_) {
+        GuiScreen gui = Minecraft.getInstance().currentScreen;
+        if (!(gui instanceof GuiContainerCreative) || ((GuiContainerCreative) gui).getSelectedTabIndex() == ItemGroup.INVENTORY.getIndex())
+            return AEIRenderHelper.charInput(p_onCharEvent_1_, p_onCharEvent_3_, p_onCharEvent_4_);
+        return false;
+    }
+    
+    @Override
+    public void clientTick(final Minecraft minecraft) {
+        AEIRenderHelper.tick();
+    }
+    
+    @Override
+    public boolean mouseScrolled(double direction) {
+        GuiScreen gui = Minecraft.getInstance().currentScreen;
+        if (!(gui instanceof GuiContainerCreative) || ((GuiContainerCreative) gui).getSelectedTabIndex() == ItemGroup.INVENTORY.getIndex())
+            return AEIRenderHelper.mouseScrolled(direction);
+        return false;
+    }
+}

+ 22 - 0
src/main/java/me/shedaniel/listeners/InitListener.java

@@ -0,0 +1,22 @@
+package me.shedaniel.listeners;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.dimdev.riftloader.listener.InitializationListener;
+import org.spongepowered.asm.launch.MixinBootstrap;
+import org.spongepowered.asm.mixin.Mixins;
+
+/**
+ * Created by James on 7/27/2018.
+ */
+public class InitListener implements InitializationListener {
+    private static final Logger LOGGER = LogManager.getLogger();
+    
+    @Override
+    public void onInitialization() {
+        LOGGER.info("Adding AEI Mixins");
+        MixinBootstrap.init();
+        Mixins.addConfiguration("mixins.roughlyenoughitems.json");
+        
+    }
+}

+ 14 - 0
src/main/java/me/shedaniel/listeners/ResizeListener.java

@@ -0,0 +1,14 @@
+package me.shedaniel.listeners;
+
+import me.shedaniel.gui.AEIRenderHelper;
+import me.shedaniel.listenerdefinitions.MinecraftResize;
+
+/**
+ * Created by James on 7/28/2018.
+ */
+public class ResizeListener implements MinecraftResize {
+    @Override
+    public void resize() {
+        AEIRenderHelper.resize();
+    }
+}

+ 22 - 0
src/main/java/me/shedaniel/mixins/MixinDoneLoading.java

@@ -0,0 +1,22 @@
+package me.shedaniel.mixins;
+
+import me.shedaniel.listenerdefinitions.DoneLoading;
+import net.minecraft.init.Bootstrap;
+import org.dimdev.riftloader.RiftLoader;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+/**
+ * Created by James on 7/27/2018.
+ */
+@Mixin(Bootstrap.class)
+public class MixinDoneLoading {
+    @Inject(method = "register", at = @At("RETURN"))
+    private static void onBootstrapRegister(CallbackInfo ci) {
+        for(DoneLoading listener : RiftLoader.instance.getListeners(DoneLoading.class)) {
+            listener.onDoneLoading();
+        }
+    }
+}

+ 98 - 0
src/main/java/me/shedaniel/mixins/MixinGuiContainer.java

@@ -0,0 +1,98 @@
+package me.shedaniel.mixins;
+
+import me.shedaniel.listenerdefinitions.*;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.IGuiEventListenerDeferred;
+import net.minecraft.client.gui.inventory.GuiContainer;
+import net.minecraft.inventory.Slot;
+import net.minecraft.item.ItemStack;
+import org.dimdev.riftloader.RiftLoader;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
+
+/**
+ * Created by James on 7/27/2018.
+ */
+@Mixin(GuiContainer.class)
+public abstract class MixinGuiContainer implements IGuiEventListenerDeferred, IMixinGuiContainer {
+    @Shadow
+    protected Slot hoveredSlot;
+    @Shadow
+    private ItemStack draggedStack;
+    @Shadow
+    protected int guiLeft;
+    @Shadow
+    protected int guiTop;
+    @Shadow
+    protected int xSize;
+    @Shadow
+    protected int ySize;
+    
+    @Inject(method = "render", at = @At("RETURN"))
+    private void onRender(int p_drawScreen_1_, int p_drawScreen_2_, float p_drawScreen_3_, CallbackInfo ci) {
+        for(DrawContainer listener : RiftLoader.instance.getListeners(DrawContainer.class)) {
+            listener.draw(p_drawScreen_1_, p_drawScreen_2_, p_drawScreen_3_, (GuiContainer) Minecraft.getInstance().currentScreen);
+        }
+    }
+    
+    @Inject(method = "mouseClicked", at = @At("HEAD"), cancellable = true)
+    private void onMouseClicked(double p_mouseClicked_1_, double p_mouseClicked_3_, int p_mouseClicked_5_, CallbackInfoReturnable<Boolean> ci) {
+        boolean handled = false;
+        for(GuiCickListener listener : RiftLoader.instance.getListeners(GuiCickListener.class)) {
+            if (listener.onClick((int) p_mouseClicked_1_, (int) p_mouseClicked_3_, p_mouseClicked_5_)) {
+                ci.setReturnValue(true);
+                handled = true;
+            }
+        }
+        if (handled)
+            ci.cancel();
+        
+    }
+    
+    @Inject(method = "keyPressed", at = @At("HEAD"), cancellable = true)
+    private void onKeyPressed(int p_keyPressed_1_, int p_keyPressed_2_, int p_keyPressed_3_, CallbackInfoReturnable<Boolean> ci) {
+        boolean handled = false;
+        for(GuiKeyDown listener : RiftLoader.instance.getListeners(GuiKeyDown.class)) {
+            if (listener.keyDown(p_keyPressed_1_, p_keyPressed_2_, p_keyPressed_3_))
+                handled = true;
+        }
+        if (handled) {
+            ci.setReturnValue(handled);
+            ci.cancel();
+        }
+    }
+    
+    public boolean mouseScrolled(double p_mouseScrolled_1_) {
+        boolean handled = false;
+        for(MouseScrollListener listener : RiftLoader.instance.getListeners(MouseScrollListener.class)) {
+            if (listener.mouseScrolled(p_mouseScrolled_1_)) {
+                handled = true;
+            }
+        }
+        return handled;
+    }
+    
+    @Override
+    public ItemStack getDraggedStack() {
+        return draggedStack;
+    }
+    
+    @Override
+    public int getGuiLeft() {
+        return guiLeft;
+    }
+    
+    @Override
+    public int getXSize() {
+        return xSize;
+    }
+    
+    @Override
+    public Slot getHoveredSlot() {
+        return hoveredSlot;
+    }
+}

+ 93 - 0
src/main/java/me/shedaniel/mixins/MixinGuiContainerCreative.java

@@ -0,0 +1,93 @@
+package me.shedaniel.mixins;
+
+import me.shedaniel.listenerdefinitions.GuiKeyDown;
+import net.minecraft.client.gui.GuiTextField;
+import net.minecraft.client.gui.inventory.GuiContainerCreative;
+import net.minecraft.client.renderer.InventoryEffectRenderer;
+import net.minecraft.inventory.Container;
+import net.minecraft.inventory.Slot;
+import net.minecraft.item.ItemGroup;
+import net.minecraft.util.math.MathHelper;
+import org.dimdev.riftloader.RiftLoader;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Overwrite;
+import org.spongepowered.asm.mixin.Shadow;
+
+import javax.annotation.Nullable;
+import java.util.Objects;
+
+@Mixin(GuiContainerCreative.class)
+public abstract class MixinGuiContainerCreative extends InventoryEffectRenderer {
+    
+    @Shadow
+    private static int selectedTabIndex = ItemGroup.BUILDING_BLOCKS.getIndex();
+    @Shadow
+    private GuiTextField searchField;
+    
+    @Shadow
+    protected abstract boolean hasTmpInventory(@Nullable Slot p_208018_1_);
+    
+    @Shadow
+    protected abstract void setCurrentCreativeTab(ItemGroup tab);
+    
+    @Shadow
+    protected abstract void updateCreativeSearch();
+    
+    @Shadow
+    private boolean field_195377_F;
+    
+    @Shadow
+    protected abstract boolean needsScrollBars();
+    
+    @Shadow
+    private float currentScroll;
+    
+    public MixinGuiContainerCreative(Container inventorySlotsIn) {
+        super(inventorySlotsIn);
+    }
+    
+    @Overwrite
+    public boolean keyPressed(int p_keyPressed_1_, int p_keyPressed_2_, int p_keyPressed_3_) {
+        this.field_195377_F = false;
+        if (selectedTabIndex != ItemGroup.SEARCH.getIndex()) {
+            if (selectedTabIndex != ItemGroup.INVENTORY.getIndex()) {
+                if (this.mc.gameSettings.keyBindChat.matchesKey(p_keyPressed_1_, p_keyPressed_2_)) {
+                    this.field_195377_F = true;
+                    this.setCurrentCreativeTab(ItemGroup.SEARCH);
+                    return true;
+                }
+            } else for(GuiKeyDown listener : RiftLoader.instance.getListeners(GuiKeyDown.class))
+                if (listener.keyDown(p_keyPressed_1_, p_keyPressed_2_, p_keyPressed_3_))
+                    return true;
+        } else {
+            boolean flag = !this.hasTmpInventory(this.hoveredSlot) || this.hoveredSlot != null && this.hoveredSlot.getHasStack();
+            
+            if (flag && this.func_195363_d(p_keyPressed_1_, p_keyPressed_2_)) {
+                this.field_195377_F = true;
+                return true;
+            } else {
+                String s = this.searchField.getText();
+                
+                if (this.searchField.keyPressed(p_keyPressed_1_, p_keyPressed_2_, p_keyPressed_3_)) {
+                    if (!Objects.equals(s, this.searchField.getText()))
+                        this.updateCreativeSearch();
+                    return true;
+                }
+            }
+        }
+        return super.keyPressed(p_keyPressed_1_, p_keyPressed_2_, p_keyPressed_3_);
+    }
+    
+    public boolean mouseScrolled(double p_mouseScrolled_1_) {
+        if (!this.needsScrollBars()) {
+            return super.mouseScrolled(p_mouseScrolled_1_);
+        } else {
+            int i = (((GuiContainerCreative.ContainerCreative) this.inventorySlots).itemList.size() + 9 - 1) / 9 - 5;
+            this.currentScroll = (float) ((double) this.currentScroll - p_mouseScrolled_1_ / (double) i);
+            this.currentScroll = MathHelper.clamp(this.currentScroll, 0.0F, 1.0F);
+            ((GuiContainerCreative.ContainerCreative) this.inventorySlots).scrollTo(this.currentScroll);
+            return true;
+        }
+    }
+    
+}

+ 27 - 0
src/main/java/me/shedaniel/mixins/MixinKeyboardListener.java

@@ -0,0 +1,27 @@
+package me.shedaniel.mixins;
+
+import me.shedaniel.listenerdefinitions.CharInput;
+import net.minecraft.client.KeyboardListener;
+import org.dimdev.riftloader.RiftLoader;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+/**
+ * Created by James on 8/4/2018.
+ */
+@Mixin(KeyboardListener.class)
+public class MixinKeyboardListener {
+    @Inject(method = "onCharEvent", at = @At("RETURN"), cancellable = true)
+    private void onCharEvent(long p_onCharEvent_1_, int p_onCharEvent_3_, int p_onCharEvent_4_, CallbackInfo ci) {
+        boolean handled = false;
+        for(CharInput listener : RiftLoader.instance.getListeners(CharInput.class)) {
+            if (listener.charInput(p_onCharEvent_1_, p_onCharEvent_3_, p_onCharEvent_4_)) {
+                handled = true;
+            }
+        }
+        if (handled)
+            ci.cancel();
+    }
+}

+ 23 - 0
src/main/java/me/shedaniel/mixins/MixinMinecraftResize.java

@@ -0,0 +1,23 @@
+package me.shedaniel.mixins;
+
+import me.shedaniel.listenerdefinitions.MinecraftResize;
+import net.minecraft.client.MainWindow;
+import org.dimdev.riftloader.RiftLoader;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+/**
+ * Created by James on 7/28/2018.
+ */
+@Mixin(MainWindow.class)
+public class MixinMinecraftResize {
+    @Inject(method = "updateSize", at = @At("RETURN"))
+    private void onResize(CallbackInfo ci) {
+        for(MinecraftResize listener : RiftLoader.instance.getListeners(MinecraftResize.class)) {
+            listener.resize();
+        }
+    }
+    
+}

+ 26 - 0
src/main/java/me/shedaniel/mixins/MixinRecipeManager.java

@@ -0,0 +1,26 @@
+package me.shedaniel.mixins;
+
+import me.shedaniel.listenerdefinitions.RecipeLoadListener;
+import net.minecraft.client.network.NetHandlerPlayClient;
+import net.minecraft.item.crafting.RecipeManager;
+import net.minecraft.network.play.server.SPacketUpdateRecipes;
+import org.dimdev.riftloader.RiftLoader;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+@Mixin(NetHandlerPlayClient.class)
+public class MixinRecipeManager {
+    
+    @Shadow
+    RecipeManager recipeManager;
+    
+    @Inject(method = "handleUpdateRecipes", at = @At("RETURN"))
+    private void onUpdateRecipies(SPacketUpdateRecipes packetIn, CallbackInfo ci) {
+        for(RecipeLoadListener listener : RiftLoader.instance.getListeners(RecipeLoadListener.class)) {
+            listener.recipesLoaded(recipeManager);
+        }
+    }
+}

+ 44 - 0
src/main/java/me/shedaniel/mixins/SettingsMixin.java

@@ -0,0 +1,44 @@
+package me.shedaniel.mixins;
+
+import me.shedaniel.listenerdefinitions.PreLoadOptions;
+import net.minecraft.client.GameSettings;
+import net.minecraft.client.settings.KeyBinding;
+import org.dimdev.riftloader.RiftLoader;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by James on 8/7/2018.
+ */
+
+@Mixin(GameSettings.class)
+public class SettingsMixin {
+    @Shadow
+    public KeyBinding[] keyBindings;
+    
+    public SettingsMixin() {
+        System.out.println("loaded");
+    }
+    
+    @Inject(method = "loadOptions", at = @At("HEAD"))
+    public void beforeLoadOptions(CallbackInfo ci) {
+        
+        RiftLoader.instance.getListeners(PreLoadOptions.class).stream().forEach(f -> processNewBindings(f.loadOptions()));
+    }
+    
+    private void processNewBindings(List<KeyBinding> newBindings) {
+        List<KeyBinding> toAdd = new ArrayList<>();
+        toAdd.addAll(newBindings);
+        for(KeyBinding keyBinding : keyBindings) {
+            toAdd.add(keyBinding);
+        }
+        keyBindings = (KeyBinding[]) toAdd.toArray(new KeyBinding[0]);
+    }
+    
+}

+ 45 - 0
src/main/java/me/shedaniel/network/CheatPacket.java

@@ -0,0 +1,45 @@
+package me.shedaniel.network;
+
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.network.NetHandlerPlayServer;
+import net.minecraft.network.Packet;
+import net.minecraft.network.PacketBuffer;
+import net.minecraft.network.play.INetHandlerPlayServer;
+
+import java.io.IOException;
+
+/**
+ * Created by James on 7/29/2018.
+ */
+public class CheatPacket implements Packet<INetHandlerPlayServer> {
+    
+    private ItemStack stack;
+    
+    public CheatPacket() {
+    }
+    
+    public CheatPacket(ItemStack stack) {
+        this.stack = stack;
+    }
+    
+    @Override
+    public void readPacketData(PacketBuffer packetBuffer) throws IOException {
+        stack = ItemStack.read(packetBuffer.readCompoundTag());
+    }
+    
+    @Override
+    public void writePacketData(PacketBuffer packetBuffer) throws IOException {
+        NBTTagCompound tag = new NBTTagCompound();
+        stack.write(tag);
+        packetBuffer.writeCompoundTag(tag);
+    }
+    
+    @Override
+    public void processPacket(INetHandlerPlayServer iNetHandlerPlayServer) {
+        NetHandlerPlayServer server = (NetHandlerPlayServer) iNetHandlerPlayServer;
+        EntityPlayerMP player = server.player;
+        player.inventory.addItemStackToInventory(stack);
+    }
+}

+ 32 - 0
src/main/java/me/shedaniel/network/DeletePacket.java

@@ -0,0 +1,32 @@
+package me.shedaniel.network;
+
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.item.ItemStack;
+import net.minecraft.network.NetHandlerPlayServer;
+import net.minecraft.network.Packet;
+import net.minecraft.network.PacketBuffer;
+import net.minecraft.network.play.INetHandlerPlayServer;
+
+import java.io.IOException;
+
+public class DeletePacket implements Packet<INetHandlerPlayServer> {
+    @Override
+    public void readPacketData(PacketBuffer packetBuffer) throws IOException {
+    
+    }
+    
+    @Override
+    public void writePacketData(PacketBuffer packetBuffer) throws IOException {
+    
+    }
+    
+    @Override
+    public void processPacket(INetHandlerPlayServer iNetHandlerPlayServer) {
+        NetHandlerPlayServer server = (NetHandlerPlayServer) iNetHandlerPlayServer;
+        EntityPlayerMP player = server.player;
+        
+        if (!player.inventory.getItemStack().isEmpty()) {
+            player.inventory.setItemStack(ItemStack.EMPTY);
+        }
+    }
+}

+ 43 - 0
src/main/java/me/shedaniel/plugin/VanillaPlugin.java

@@ -0,0 +1,43 @@
+package me.shedaniel.plugin;
+
+import me.shedaniel.api.IAEIPlugin;
+import me.shedaniel.impl.AEIRecipeManager;
+import me.shedaniel.plugin.crafting.VanillaCraftingCategory;
+import me.shedaniel.plugin.crafting.VanillaCraftingRecipe;
+import me.shedaniel.plugin.crafting.VanillaShapedCraftingRecipe;
+import me.shedaniel.plugin.crafting.VanillaShapelessCraftingRecipe;
+import me.shedaniel.plugin.furnace.VanillaFurnaceCategory;
+import me.shedaniel.plugin.furnace.VanillaFurnaceRecipe;
+import net.minecraft.item.crafting.FurnaceRecipe;
+import net.minecraft.item.crafting.IRecipe;
+import net.minecraft.item.crafting.ShapedRecipe;
+import net.minecraft.item.crafting.ShapelessRecipe;
+
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+
+public class VanillaPlugin implements IAEIPlugin {
+    @Override
+    public void register() {
+        List<VanillaCraftingRecipe> recipes = new LinkedList<>();
+        List<VanillaFurnaceRecipe> furnaceRecipes = new LinkedList<>();
+        AEIRecipeManager.instance().addDisplayAdapter(new VanillaCraftingCategory());
+        AEIRecipeManager.instance().addDisplayAdapter(new VanillaFurnaceCategory());
+        
+        for(IRecipe recipe : AEIRecipeManager.instance().recipeManager.getRecipes()) {
+            if (recipe instanceof ShapelessRecipe) {
+                recipes.add(new VanillaShapelessCraftingRecipe((ShapelessRecipe) recipe));
+            }
+            if (recipe instanceof ShapedRecipe) {
+                recipes.add(new VanillaShapedCraftingRecipe((ShapedRecipe) recipe));
+            }
+            if (recipe instanceof FurnaceRecipe) {
+                furnaceRecipes.add(new VanillaFurnaceRecipe((FurnaceRecipe) recipe));
+            }
+        }
+        
+        AEIRecipeManager.instance().addRecipe("vanilla", recipes);
+        AEIRecipeManager.instance().addRecipe("furnace", furnaceRecipes);
+    }
+}

+ 106 - 0
src/main/java/me/shedaniel/plugin/crafting/VanillaCraftingCategory.java

@@ -0,0 +1,106 @@
+package me.shedaniel.plugin.crafting;
+
+import me.shedaniel.api.IDisplayCategory;
+import me.shedaniel.gui.widget.AEISlot;
+import me.shedaniel.gui.widget.Control;
+import me.shedaniel.gui.widget.WidgetArrow;
+import net.minecraft.client.MainWindow;
+import net.minecraft.client.Minecraft;
+import net.minecraft.item.ItemStack;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+public class VanillaCraftingCategory implements IDisplayCategory<VanillaCraftingRecipe> {
+    MainWindow mainWindow = Minecraft.getInstance().mainWindow;
+    private List<VanillaCraftingRecipe> recipes;
+    
+    @Override
+    public String getId() {
+        return "vanilla";
+    }
+    
+    @Override
+    public String getDisplayName() {
+        return "Crafting";
+    }
+    
+    @Override
+    public void addRecipe(VanillaCraftingRecipe recipe) {
+        if (this.recipes == null)
+            this.recipes = new ArrayList<>();
+        this.recipes.add(recipe);
+    }
+    
+    @Override
+    public void resetRecipes() {
+        this.recipes = new ArrayList<>();
+    }
+    
+    @Override
+    public List<AEISlot> setupDisplay(int number) {
+        List<AEISlot> slots = new LinkedList<>();
+        int count = 0;
+        List<List<ItemStack>> input = recipes.get(number).getInput();
+        for(int y = 0; y < 3; y++) {
+            for(int x = 0; x < 3; x++) {
+                AEISlot slot = new AEISlot(20 + x * 18, 75 + y * 18 + number * 75);
+                slot.setDrawBackground(true);
+                slots.add(slot);
+                count++;
+            }
+        }
+        for(int i = 0; i < input.size(); i++) {
+            if (recipes.get(number) instanceof VanillaShapedCraftingRecipe) {
+                if (!input.get(i).isEmpty())
+                    slots.get(getSlotWithSize(number, i)).setStackList(input.get(i));
+            } else if (!input.get(i).isEmpty())
+                slots.get(i).setStackList(input.get(i));
+        }
+        AEISlot slot = new AEISlot(130, 75 + 18 + number * 75);
+        
+        slot.setDrawBackground(true);
+        slot.setStack(recipes.get(number).getOutput().get(0).copy());
+        slots.add(slot);
+        return slots;
+    }
+    
+    @Override
+    public boolean canDisplay(VanillaCraftingRecipe recipe) {
+        return false;
+    }
+    
+    @Override
+    public void drawExtras() {
+    
+    }
+    
+    @Override
+    public void addWidget(List<Control> controls, int number) {
+        WidgetArrow wa = new WidgetArrow(90, 70 + 22 + number * 75, false);
+        controls.add(wa);
+    }
+    
+    private int getSlotWithSize(int number, int num) {
+        if (recipes.get(number).getWidth() == 1) {
+            if (num == 1)
+                return 3;
+            if (num == 2)
+                return 6;
+        }
+        
+        if (recipes.get(number).getWidth() == 2) {
+            if (num == 2)
+                return 3;
+            if (num == 3)
+                return 4;
+            if (num == 4)
+                return 6;
+            if (num == 5)
+                return 7;
+            
+        }
+        return num;
+    }
+}

+ 16 - 0
src/main/java/me/shedaniel/plugin/crafting/VanillaCraftingRecipe.java

@@ -0,0 +1,16 @@
+package me.shedaniel.plugin.crafting;
+
+import me.shedaniel.api.IRecipe;
+import net.minecraft.item.ItemStack;
+
+public abstract class VanillaCraftingRecipe implements IRecipe<ItemStack> {
+    
+    public int getWidth() {
+        return 2;
+    }
+    
+    public int getHeight() {
+        return 2;
+    }
+    
+}

+ 55 - 0
src/main/java/me/shedaniel/plugin/crafting/VanillaShapedCraftingRecipe.java

@@ -0,0 +1,55 @@
+package me.shedaniel.plugin.crafting;
+
+import net.minecraft.item.ItemStack;
+import net.minecraft.item.crafting.Ingredient;
+import net.minecraft.item.crafting.ShapedRecipe;
+
+import java.util.LinkedList;
+import java.util.List;
+
+public class VanillaShapedCraftingRecipe extends VanillaCraftingRecipe {
+    
+    private final ShapedRecipe recipe;
+    
+    public VanillaShapedCraftingRecipe(ShapedRecipe recipe) {
+        
+        this.recipe = recipe;
+    }
+    
+    @Override
+    public int getWidth() {
+        return recipe.getWidth();
+    }
+    
+    @Override
+    public int getHeight() {
+        return recipe.getHeight();
+    }
+    
+    @Override
+    public String getId() {
+        return "vanilla";
+    }
+    
+    @Override
+    public List<ItemStack> getOutput() {
+        List<ItemStack> output = new LinkedList<>();
+        output.add(recipe.getRecipeOutput());
+        return output;
+    }
+    
+    @Override
+    public List<List<ItemStack>> getInput() {
+        List<List<ItemStack>> input = new LinkedList<>();
+        int count = 0;
+        for(Ingredient ingredient : recipe.getIngredients()) {
+            List<ItemStack> ingList = new LinkedList<>();
+            for(ItemStack matchingStack : ingredient.getMatchingStacks()) {
+                ingList.add(matchingStack);
+            }
+            input.add(ingList);
+            count++;
+        }
+        return input;
+    }
+}

+ 57 - 0
src/main/java/me/shedaniel/plugin/crafting/VanillaShapelessCraftingRecipe.java

@@ -0,0 +1,57 @@
+package me.shedaniel.plugin.crafting;
+
+import net.minecraft.item.ItemStack;
+import net.minecraft.item.crafting.Ingredient;
+import net.minecraft.item.crafting.ShapelessRecipe;
+
+import java.util.LinkedList;
+import java.util.List;
+
+public class VanillaShapelessCraftingRecipe extends VanillaCraftingRecipe {
+    
+    private final ShapelessRecipe recipe;
+    
+    public VanillaShapelessCraftingRecipe(ShapelessRecipe recipe) {
+        
+        this.recipe = recipe;
+    }
+    
+    @Override
+    public String getId() {
+        return "vanilla";
+    }
+    
+    @Override
+    public List<ItemStack> getOutput() {
+        List<ItemStack> output = new LinkedList<>();
+        output.add(recipe.getRecipeOutput());
+        return output;
+    }
+    
+    @Override
+    public List<List<ItemStack>> getInput() {
+        List<List<ItemStack>> input = new LinkedList<>();
+        for(Ingredient ingredient : recipe.getIngredients()) {
+            List<ItemStack> ingList = new LinkedList<>();
+            for(ItemStack matchingStack : ingredient.getMatchingStacks()) {
+                ingList.add(matchingStack);
+            }
+            input.add(ingList);
+        }
+        return input;
+    }
+    
+    @Override
+    public int getWidth() {
+        if (recipe.getIngredients().size() > 4)
+            return 3;
+        return 2;
+    }
+    
+    @Override
+    public int getHeight() {
+        if (recipe.getIngredients().size() > 4)
+            return 3;
+        return 2;
+    }
+}

+ 83 - 0
src/main/java/me/shedaniel/plugin/furnace/VanillaFurnaceCategory.java

@@ -0,0 +1,83 @@
+package me.shedaniel.plugin.furnace;
+
+import me.shedaniel.api.IDisplayCategory;
+import me.shedaniel.gui.widget.AEISlot;
+import me.shedaniel.gui.widget.Control;
+import me.shedaniel.gui.widget.WidgetArrow;
+import me.shedaniel.plugin.crafting.VanillaCraftingRecipe;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+import net.minecraft.tileentity.TileEntityFurnace;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class VanillaFurnaceCategory implements IDisplayCategory<VanillaFurnaceRecipe> {
+    private List<VanillaFurnaceRecipe> recipes;
+    
+    @Override
+    public String getId() {
+        return "furnace";
+    }
+    
+    @Override
+    public String getDisplayName() {
+        return "Smelting";
+    }
+    
+    @Override
+    public void addRecipe(VanillaFurnaceRecipe recipe) {
+        if (this.recipes == null)
+            this.recipes = new ArrayList<>();
+        this.recipes.add(recipe);
+    }
+    
+    @Override
+    public void resetRecipes() {
+        this.recipes = new ArrayList<>();
+    }
+    
+    @Override
+    public List<AEISlot> setupDisplay(int number) {
+        List<AEISlot> slots = new LinkedList<>();
+        AEISlot inputSlot = new AEISlot(50, 70 + number * 75);
+        inputSlot.setStackList(recipes.get(number).getInput().get(0));
+        inputSlot.setDrawBackground(true);
+        
+        AEISlot outputSlot = new AEISlot(110, 70 + number * 75);
+        outputSlot.setStackList(recipes.get(number).getOutput());
+        outputSlot.setDrawBackground(true);
+        
+        AEISlot fuelSlot = new AEISlot(80, 100 + number * 75);
+        fuelSlot.setStackList(getFuel());
+        fuelSlot.setDrawBackground(true);
+        fuelSlot.setExtraTooltip("Fuel");
+        
+        slots.add(inputSlot);
+        slots.add(outputSlot);
+        slots.add(fuelSlot);
+        return slots;
+    }
+    
+    @Override
+    public boolean canDisplay(VanillaFurnaceRecipe recipe) {
+        return false;
+    }
+    
+    @Override
+    public void drawExtras() {
+    
+    }
+    
+    @Override
+    public void addWidget(List<Control> controls, int number) {
+        WidgetArrow wa = new WidgetArrow(75, 70 + number * 75, true);
+        controls.add(wa);
+    }
+    
+    private List<ItemStack> getFuel() {
+        return TileEntityFurnace.getBurnTimes().keySet().stream().map(Item::getDefaultInstance).collect(Collectors.toList());
+    }
+}

+ 42 - 0
src/main/java/me/shedaniel/plugin/furnace/VanillaFurnaceRecipe.java

@@ -0,0 +1,42 @@
+package me.shedaniel.plugin.furnace;
+
+import me.shedaniel.api.IRecipe;
+import net.minecraft.item.ItemStack;
+import net.minecraft.item.crafting.FurnaceRecipe;
+import net.minecraft.item.crafting.Ingredient;
+
+import java.util.LinkedList;
+import java.util.List;
+
+public class VanillaFurnaceRecipe implements IRecipe<ItemStack> {
+    private final FurnaceRecipe recipe;
+    
+    @Override
+    public String getId() {
+        return "furnace";
+    }
+    
+    public VanillaFurnaceRecipe(FurnaceRecipe recipe) {
+        this.recipe = recipe;
+    }
+    
+    @Override
+    public List<ItemStack> getOutput() {
+        List<ItemStack> output = new LinkedList<>();
+        output.add(recipe.getRecipeOutput().copy());
+        return output;
+    }
+    
+    @Override
+    public List<List<ItemStack>> getInput() {
+        List<List<ItemStack>> input = new LinkedList<>();
+        for(Ingredient ingredient : recipe.getIngredients()) {
+            List<ItemStack> ingredients = new LinkedList<>();
+            for(ItemStack matchingStack : ingredient.getMatchingStacks()) {
+                ingredients.add(matchingStack);
+            }
+            input.add(ingredients);
+        }
+        return input;
+    }
+}

+ 0 - 0
src/main/resources/assets/almostenoughitems/.modassetroot


+ 8 - 0
src/main/resources/assets/almostenoughitems/lang/en_us.json

@@ -0,0 +1,8 @@
+{
+  "key.aei.category":"Almost Enough Items",
+  "key.aei.recipe":"Show Recipe",
+  "key.aei.hide":"Hide/Show AEI",
+  "key.aei.use":"Show Uses",
+  "text.aei.cheat":"Cheat",
+  "text.aei.nocheat":"§4§mCheat"
+}

+ 8 - 0
src/main/resources/assets/almostenoughitems/lang/fr_fr.json

@@ -0,0 +1,8 @@
+{
+  "key.aei.category":"Almost Enough Items",
+  "key.aei.recipe":"Afficher la recette",
+  "key.aei.hide":"Masquer/afficher AEI",
+  "key.aei.use":"Afficher les utilisations",
+  "text.aei.cheat":"Triche",
+  "text.aei.nocheat":"§4§mTriche"
+}

二进制
src/main/resources/assets/almostenoughitems/textures/gui/recipecontainer.png


+ 18 - 0
src/main/resources/mixins.roughlyenoughitems.json

@@ -0,0 +1,18 @@
+{
+  "required": true,
+  "minVersion": "0.7.7",
+  "compatibilityLevel": "JAVA_8",
+  "target": "@env(DEFAULT)",
+  "package": "me.shedaniel.mixins",
+  "refmap": "mixins.roughlyenoughitems.refmap.json",
+  "mixins": [
+    "MixinDoneLoading"
+  ],
+  "client": [
+    "MixinGuiContainer",
+    "MixinMinecraftResize",
+    "MixinKeyboardListener",
+    "MixinRecipeManager",
+    "MixinGuiContainerCreative"
+  ]
+}

+ 6 - 0
src/main/resources/pack.mcmeta

@@ -0,0 +1,6 @@
+{
+  "pack": {
+    "pack_format": 4,
+    "description": "Rift Mod List"
+  }
+}

+ 26 - 0
src/main/resources/riftmod.json

@@ -0,0 +1,26 @@
+{
+  "id": "roughlyenoughitems",
+  "name": "RoughlyEnoughItems",
+  "version": "1.0b-SNAPSHOT",
+  "authors": [
+    "ZenDarva",
+    "Danielshe"
+  ],
+  "listeners": [
+    "me.shedaniel.Core",
+    "me.shedaniel.listeners.InitListener",
+    "me.shedaniel.listeners.DrawContainerListener",
+    "me.shedaniel.listeners.ResizeListener",
+    "me.shedaniel.library.KeyBindManager",
+    {
+      "class": "me.shedaniel.plugin.VanillaPlugin",
+      "side": "client"
+    },
+    {
+      "class": "me.shedaniel.ClientListener",
+      "side": "client"
+    }
+  ],
+  "url": "https://shedaniel.me",
+  "description": "An AEI fork to look like JEI"
+}