Эх сурвалжийг харах

Using fiber as a config lib

Unknown 5 жил өмнө
parent
commit
031bf29d9f
26 өөрчлөгдсөн 585 нэмэгдсэн , 396 устгасан
  1. 8 1
      build.gradle
  2. 1 1
      gradle.properties
  3. 2 2
      src/main/java/me/shedaniel/rei/RoughlyEnoughItemsCore.java
  4. 3 3
      src/main/java/me/shedaniel/rei/api/ConfigManager.java
  5. 89 0
      src/main/java/me/shedaniel/rei/api/ConfigObject.java
  6. 1 1
      src/main/java/me/shedaniel/rei/api/DisplayHelper.java
  7. 7 6
      src/main/java/me/shedaniel/rei/client/ClientHelperImpl.java
  8. 14 48
      src/main/java/me/shedaniel/rei/client/ConfigManagerImpl.java
  9. 0 98
      src/main/java/me/shedaniel/rei/client/ConfigObject.java
  10. 307 0
      src/main/java/me/shedaniel/rei/client/ConfigObjectImpl.java
  11. 1 1
      src/main/java/me/shedaniel/rei/client/ScreenHelper.java
  12. 16 16
      src/main/java/me/shedaniel/rei/gui/ContainerScreenOverlay.java
  13. 3 2
      src/main/java/me/shedaniel/rei/gui/PreRecipeViewingScreen.java
  14. 2 2
      src/main/java/me/shedaniel/rei/gui/RecipeViewingScreen.java
  15. 1 1
      src/main/java/me/shedaniel/rei/gui/VillagerRecipeViewingScreen.java
  16. 9 0
      src/main/java/me/shedaniel/rei/gui/config/ItemCheatingMode.java
  17. 1 1
      src/main/java/me/shedaniel/rei/gui/config/ItemListOrderingConfig.java
  18. 1 1
      src/main/java/me/shedaniel/rei/gui/config/RecipeScreenType.java
  19. 7 7
      src/main/java/me/shedaniel/rei/gui/widget/ItemListOverlay.java
  20. 2 2
      src/main/java/me/shedaniel/rei/gui/widget/RecipeBaseWidget.java
  21. 5 4
      src/main/java/me/shedaniel/rei/gui/widget/RecipeChoosePageWidget.java
  22. 8 5
      src/main/java/me/shedaniel/rei/gui/widget/SlotWidget.java
  23. 1 1
      src/main/java/me/shedaniel/rei/plugin/DefaultAutoCraftingPlugin.java
  24. 9 8
      src/main/java/me/shedaniel/rei/plugin/DefaultPlugin.java
  25. 35 142
      src/main/java/me/shedaniel/rei/utils/ClothScreenRegistry.java
  26. 52 43
      src/main/resources/assets/roughlyenoughitems/lang/en_us.json

+ 8 - 1
build.gradle

@@ -48,6 +48,10 @@ dependencies {
     modImplementation("me.shedaniel.cloth:config-2:${cloth_config_version}") {
         transitive = false
     }
+    modApi ("me.shedaniel.cloth:fiber2cloth:1.0.1") {
+        transitive = false
+    }
+    modApi "me.zeroeightsix:fiber:0.6.0-7"
     modApi "blue.endless:jankson:${project.jankson_version}"
     if (includeDep) {
         include("me.shedaniel.cloth:cloth-events:${cloth_events_version}") {
@@ -56,7 +60,10 @@ dependencies {
         include("me.shedaniel.cloth:config-2:${cloth_config_version}") {
             transitive = false
         }
-        include "blue.endless:jankson:${project.jankson_version}"
+        include ("me.shedaniel.cloth:fiber2cloth:1.0.1") {
+            transitive = false
+        }
+        include "me.zeroeightsix:fiber:0.6.0-7"
     }
     modImplementation "io.github.prospector.modmenu:ModMenu:${modmenu_version}"
     compile "org.lwjgl:lwjgl-jemalloc:3.2.1"

+ 1 - 1
gradle.properties

@@ -4,6 +4,6 @@ yarn_version=1.14.3+build.1
 fabricloader_version=0.4.8+build.155
 jankson_version=1.1.0
 cloth_events_version=0.4.0
-cloth_config_version=0.5.0
+cloth_config_version=0.5.2
 modmenu_version=1.5.4-85
 fabric_api=0.3.0+build.198

+ 2 - 2
src/main/java/me/shedaniel/rei/RoughlyEnoughItemsCore.java

@@ -210,14 +210,14 @@ public class RoughlyEnoughItemsCore implements ClientModInitializer {
                 return;
             }
             lastSync.set(System.currentTimeMillis());
-            if (RoughlyEnoughItemsCore.getConfigManager().getConfig().registerRecipesInAnotherThread) {
+            if (RoughlyEnoughItemsCore.getConfigManager().getConfig().doesRegisterRecipesInAnotherThread()) {
                 CompletableFuture.runAsync(() -> ((RecipeHelperImpl) RoughlyEnoughItemsCore.getRecipeHelper()).recipesLoaded(recipeManager), SYNC_RECIPES);
             } else {
                 ((RecipeHelperImpl) RoughlyEnoughItemsCore.getRecipeHelper()).recipesLoaded(recipeManager);
             }
         });
         ClothClientHooks.SCREEN_ADD_BUTTON.register((minecraftClient, screen, abstractButtonWidget) -> {
-            if (RoughlyEnoughItemsCore.getConfigManager().getConfig().disableRecipeBook && screen instanceof AbstractContainerScreen && abstractButtonWidget instanceof TexturedButtonWidget)
+            if (RoughlyEnoughItemsCore.getConfigManager().getConfig().doesDisableRecipeBook() && screen instanceof AbstractContainerScreen && abstractButtonWidget instanceof TexturedButtonWidget)
                 if (((RecipeBookButtonWidgetHooks) abstractButtonWidget).rei_getTexture().equals(recipeButtonTex))
                     return ActionResult.FAIL;
             return ActionResult.PASS;

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

@@ -5,7 +5,7 @@
 
 package me.shedaniel.rei.api;
 
-import me.shedaniel.rei.client.ConfigObject;
+import me.zeroeightsix.fiber.exception.FiberException;
 import net.minecraft.client.gui.screen.Screen;
 
 import java.io.IOException;
@@ -17,14 +17,14 @@ public interface ConfigManager {
      *
      * @throws IOException when error
      */
-    void saveConfig() throws IOException;
+    void saveConfig() throws IOException, FiberException;
     
     /**
      * Loads the config from the json file, creates the file if not found.
      *
      * @throws IOException when error
      */
-    void loadConfig() throws IOException;
+    void loadConfig() throws IOException, FiberException;
     
     /**
      * Gets the config instance

+ 89 - 0
src/main/java/me/shedaniel/rei/api/ConfigObject.java

@@ -0,0 +1,89 @@
+package me.shedaniel.rei.api;
+
+import me.shedaniel.rei.gui.config.ItemCheatingMode;
+import me.shedaniel.rei.gui.config.ItemListOrdering;
+import me.shedaniel.rei.gui.config.RecipeScreenType;
+import me.zeroeightsix.fiber.tree.ConfigNode;
+
+public interface ConfigObject {
+    
+    ConfigNode getConfigNode();
+    
+    void setCheating(boolean cheating);
+    
+    boolean isCheating();
+    
+    ItemListOrdering getItemListOrdering();
+    
+    boolean isItemListAscending();
+    
+    boolean isUsingDarkTheme();
+    
+    boolean shouldAppendModNames();
+    
+    RecipeScreenType getRecipeScreenType();
+    
+    void setRecipeScreenType(RecipeScreenType recipeScreenType);
+    
+    boolean isLoadingDefaultPlugin();
+    
+    boolean isSideSearchField();
+    
+    boolean isLeftHandSidePanel();
+    
+    boolean isCraftableFilterEnabled();
+    
+    String getGamemodeCommand();
+    
+    String getGiveCommand();
+    
+    String getWeatherCommand();
+    
+    int getMaxRecipePerPage();
+    
+    boolean doesShowUtilsButtons();
+    
+    boolean doesDisableRecipeBook();
+    
+    boolean areClickableRecipeArrowsEnabled();
+    
+    ItemCheatingMode getItemCheatingMode();
+    
+    boolean isUsingLightGrayRecipeBorder();
+    
+    boolean doesVillagerScreenHavePermanentScrollBar();
+    
+    boolean doesRegisterRecipesInAnotherThread();
+    
+    RelativePoint getChoosePageDialogPoint();
+    
+    void setChoosePageDialogPoint(RelativePoint choosePageDialogPoint);
+    
+    public static class RelativePoint {
+        
+        private double relativeX, relativeY;
+        
+        public RelativePoint(double relativeX, double relativeY) {
+            this.relativeX = relativeX;
+            this.relativeY = relativeY;
+        }
+        
+        public double getRelativeX() {
+            return relativeX;
+        }
+        
+        public double getRelativeY() {
+            return relativeY;
+        }
+        
+        public double getX(double width) {
+            return width * relativeX;
+        }
+        
+        public double getY(double height) {
+            return height * relativeY;
+        }
+        
+    }
+    
+}

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

@@ -112,7 +112,7 @@ public interface DisplayHelper {
          * @return the item list bounds
          */
         default Rectangle getItemListArea(Rectangle rectangle) {
-            return new Rectangle(rectangle.x + 2, rectangle.y + 24, rectangle.width - 4, rectangle.height - (RoughlyEnoughItemsCore.getConfigManager().getConfig().sideSearchField ? 27 + 22 : 27));
+            return new Rectangle(rectangle.x + 2, rectangle.y + 24, rectangle.width - 4, rectangle.height - (RoughlyEnoughItemsCore.getConfigManager().getConfig().isSideSearchField() ? 27 + 22 : 27));
         }
         
         /**

+ 7 - 6
src/main/java/me/shedaniel/rei/client/ClientHelperImpl.java

@@ -18,6 +18,7 @@ import me.shedaniel.rei.gui.PreRecipeViewingScreen;
 import me.shedaniel.rei.gui.RecipeViewingScreen;
 import me.shedaniel.rei.gui.VillagerRecipeViewingScreen;
 import me.shedaniel.rei.gui.config.RecipeScreenType;
+import me.zeroeightsix.fiber.exception.FiberException;
 import net.fabricmc.api.ClientModInitializer;
 import net.fabricmc.fabric.api.client.keybinding.FabricKeyBinding;
 import net.fabricmc.fabric.api.network.ClientSidePacketRegistry;
@@ -124,15 +125,15 @@ public class ClientHelperImpl implements ClientHelper, ClientModInitializer {
     
     @Override
     public boolean isCheating() {
-        return RoughlyEnoughItemsCore.getConfigManager().getConfig().cheating;
+        return RoughlyEnoughItemsCore.getConfigManager().getConfig().isCheating();
     }
     
     @Override
     public void setCheating(boolean cheating) {
-        RoughlyEnoughItemsCore.getConfigManager().getConfig().cheating = cheating;
+        RoughlyEnoughItemsCore.getConfigManager().getConfig().setCheating(cheating);
         try {
             RoughlyEnoughItemsCore.getConfigManager().saveConfig();
-        } catch (IOException e) {
+        } catch (IOException | FiberException e) {
             e.printStackTrace();
         }
     }
@@ -158,7 +159,7 @@ public class ClientHelperImpl implements ClientHelper, ClientModInitializer {
         } else {
             Identifier identifier = Registry.ITEM.getId(cheatedStack.getItem());
             String tagMessage = cheatedStack.copy().getTag() != null && !cheatedStack.copy().getTag().isEmpty() ? cheatedStack.copy().getTag().asString() : "";
-            String og = cheatedStack.getCount() == 1 ? RoughlyEnoughItemsCore.getConfigManager().getConfig().giveCommand.replaceAll(" \\{count}", "") : RoughlyEnoughItemsCore.getConfigManager().getConfig().giveCommand;
+            String og = cheatedStack.getCount() == 1 ? RoughlyEnoughItemsCore.getConfigManager().getConfig().getGiveCommand().replaceAll(" \\{count}", "") : RoughlyEnoughItemsCore.getConfigManager().getConfig().getGiveCommand();
             String madeUpCommand = og.replaceAll("\\{player_name}", MinecraftClient.getInstance().player.getEntityName()).replaceAll("\\{item_name}", identifier.getPath()).replaceAll("\\{item_identifier}", identifier.toString()).replaceAll("\\{nbt}", tagMessage).replaceAll("\\{count}", String.valueOf(cheatedStack.getCount()));
             if (madeUpCommand.length() > 256) {
                 madeUpCommand = og.replaceAll("\\{player_name}", MinecraftClient.getInstance().player.getEntityName()).replaceAll("\\{item_name}", identifier.getPath()).replaceAll("\\{item_identifier}", identifier.toString()).replaceAll("\\{nbt}", "").replaceAll("\\{count}", String.valueOf(cheatedStack.getCount()));
@@ -234,9 +235,9 @@ public class ClientHelperImpl implements ClientHelper, ClientModInitializer {
     
     @Override
     public void openRecipeViewingScreen(Map<RecipeCategory<?>, List<RecipeDisplay>> map) {
-        if (RoughlyEnoughItemsCore.getConfigManager().getConfig().screenType == RecipeScreenType.VILLAGER)
+        if (RoughlyEnoughItemsCore.getConfigManager().getConfig().getRecipeScreenType() == RecipeScreenType.VILLAGER)
             MinecraftClient.getInstance().openScreen(new VillagerRecipeViewingScreen(map));
-        else if (RoughlyEnoughItemsCore.getConfigManager().getConfig().screenType == RecipeScreenType.UNSET)
+        else if (RoughlyEnoughItemsCore.getConfigManager().getConfig().getRecipeScreenType() == RecipeScreenType.UNSET)
             MinecraftClient.getInstance().openScreen(new PreRecipeViewingScreen(map));
         else
             MinecraftClient.getInstance().openScreen(new RecipeViewingScreen(map));

+ 14 - 48
src/main/java/me/shedaniel/rei/client/ConfigManagerImpl.java

@@ -5,12 +5,11 @@
 
 package me.shedaniel.rei.client;
 
-import blue.endless.jankson.Jankson;
-import blue.endless.jankson.JsonObject;
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
 import me.shedaniel.rei.RoughlyEnoughItemsCore;
 import me.shedaniel.rei.api.ConfigManager;
+import me.shedaniel.rei.api.ConfigObject;
+import me.zeroeightsix.fiber.JanksonSettings;
+import me.zeroeightsix.fiber.exception.FiberException;
 import net.fabricmc.loader.api.FabricLoader;
 import net.minecraft.client.MinecraftClient;
 import net.minecraft.client.gui.screen.Screen;
@@ -18,98 +17,65 @@ import net.minecraft.client.resource.language.I18n;
 import net.minecraft.text.LiteralText;
 
 import java.io.File;
-import java.io.FileOutputStream;
 import java.io.IOException;
 import java.nio.file.Files;
-import java.nio.file.StandardCopyOption;
 import java.util.List;
 
 public class ConfigManagerImpl implements ConfigManager {
     
-    private static final Gson GSON = new GsonBuilder().create();
-    private static final Jankson JANKSON = Jankson.builder().build();
-    private final File configFile, veryOldConfigFile, oldConfigFile;
+    private final File configFile;
     private ConfigObject config;
     private boolean craftableOnly;
     
     public ConfigManagerImpl() {
-        this.veryOldConfigFile = new File(FabricLoader.getInstance().getConfigDirectory(), "rei.json");
-        this.oldConfigFile = new File(FabricLoader.getInstance().getConfigDirectory(), "roughlyenoughitems/config.json");
         this.configFile = new File(FabricLoader.getInstance().getConfigDirectory(), "roughlyenoughitems/config.json5");
         this.craftableOnly = false;
         try {
             loadConfig();
             RoughlyEnoughItemsCore.LOGGER.info("[REI] Config is loaded.");
-        } catch (IOException e) {
+        } catch (IOException | FiberException e) {
             e.printStackTrace();
         }
     }
     
     @Override
-    public void saveConfig() throws IOException {
+    public void saveConfig() throws IOException, FiberException {
         configFile.getParentFile().mkdirs();
         if (!configFile.exists() && !configFile.createNewFile()) {
             RoughlyEnoughItemsCore.LOGGER.error("[REI] Failed to save config! Overwriting with default config.");
-            config = new ConfigObject();
+            config = new ConfigObjectImpl();
             return;
         }
         try {
-            String result = JANKSON.toJson(config).toJson(true, true, 0);
-            if (!configFile.exists())
-                configFile.createNewFile();
-            FileOutputStream out = new FileOutputStream(configFile, false);
-            
-            out.write(result.getBytes());
-            out.flush();
-            out.close();
+            new JanksonSettings().serialize(config.getConfigNode(), Files.newOutputStream(configFile.toPath()), false);
         } catch (Exception e) {
             e.printStackTrace();
             RoughlyEnoughItemsCore.LOGGER.error("[REI] Failed to save config! Overwriting with default config.");
-            config = new ConfigObject();
+            config = new ConfigObjectImpl();
             return;
         }
     }
     
     @Override
-    public void loadConfig() throws IOException {
+    public void loadConfig() throws IOException, FiberException {
         configFile.getParentFile().mkdirs();
-        if (!configFile.exists() && veryOldConfigFile.exists()) {
-            RoughlyEnoughItemsCore.LOGGER.info("[REI] Detected old config file, trying to move it.");
-            try {
-                Files.move(veryOldConfigFile.toPath(), configFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
-            } catch (Exception e) {
-                e.printStackTrace();
-                RoughlyEnoughItemsCore.LOGGER.error("[REI] Failed to move config file.");
-            }
-        }
-        if (!configFile.exists() && oldConfigFile.exists()) {
-            RoughlyEnoughItemsCore.LOGGER.info("[REI] Detected old config file, trying to move it.");
-            try {
-                Files.move(oldConfigFile.toPath(), configFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
-            } catch (Exception e) {
-                e.printStackTrace();
-                RoughlyEnoughItemsCore.LOGGER.error("[REI] Failed to move config file.");
-            }
-        }
         if (!configFile.exists() || !configFile.canRead()) {
             RoughlyEnoughItemsCore.LOGGER.warn("[REI] Config not found! Creating one.");
-            config = new ConfigObject();
+            config = new ConfigObjectImpl();
             saveConfig();
             return;
         }
         boolean failed = false;
         try {
-            JsonObject configJson = JANKSON.load(configFile);
-            String regularized = configJson.toJson(false, false, 0);
-            
-            config = GSON.fromJson(regularized, ConfigObject.class);
+            config = new ConfigObjectImpl();
+            new JanksonSettings().deserialize(config.getConfigNode(), Files.newInputStream(configFile.toPath()));
         } catch (Exception e) {
             e.printStackTrace();
             failed = true;
         }
         if (failed || config == null) {
             RoughlyEnoughItemsCore.LOGGER.error("[REI] Failed to load config! Overwriting with default config.");
-            config = new ConfigObject();
+            config = new ConfigObjectImpl();
         }
         saveConfig();
     }

+ 0 - 98
src/main/java/me/shedaniel/rei/client/ConfigObject.java

@@ -1,98 +0,0 @@
-/*
- * Roughly Enough Items by Danielshe.
- * Licensed under the MIT License.
- */
-
-package me.shedaniel.rei.client;
-
-import blue.endless.jankson.Comment;
-import me.shedaniel.rei.gui.config.ItemCheatingMode;
-import me.shedaniel.rei.gui.config.ItemListOrdering;
-import me.shedaniel.rei.gui.config.RecipeScreenType;
-
-public class ConfigObject {
-    
-    public boolean cheating = false;
-    
-    @Comment("The ordering of the items on the item panel.")
-    public ItemListOrdering itemListOrdering = ItemListOrdering.registry;
-    
-    @Comment("The ordering of the items on the item panel.")
-    public boolean isAscending = true;
-    
-    @Comment("To toggle the craftable button next to the search field.")
-    public boolean enableCraftableOnlyButton = false;
-    
-    @Comment("True: search field will be on the side (left / right), false: in the middle")
-    public boolean sideSearchField = false;
-    
-    @Comment("The command used in servers to cheat items")
-    public String giveCommand = "/minecraft:give {player_name} {item_identifier}{nbt} {count}";
-    
-    @Comment("The command used to change gamemode")
-    public String gamemodeCommand = "/gamemode {gamemode}";
-    
-    @Comment("The command used to change weather")
-    public String weatherCommand = "/weather {weather}";
-    
-    @Comment("True: item panel on the left, false: on the right")
-    public boolean mirrorItemPanel = false;
-    
-    @Comment("To disable REI's default plugin, don't change this unless you understand what you are doing")
-    public boolean loadDefaultPlugin = true;
-    
-    @Comment("Maximum recipes viewed at one time.")
-    public int maxRecipePerPage = 3;
-    
-    @Comment("Toggle utils buttons")
-    public boolean showUtilsButtons = false;
-    
-    @Comment("Disable Recipe Book")
-    public boolean disableRecipeBook = false;
-    
-    public boolean clickableRecipeArrows = true;
-    
-    public ItemCheatingMode itemCheatingMode = ItemCheatingMode.REI_LIKE;
-    
-    public boolean lightGrayRecipeBorder = false;
-    
-    public boolean villagerScreenPermanentScrollBar = false;
-    
-    public boolean darkTheme = false;
-    
-    public boolean registerRecipesInAnotherThread = true;
-    
-    public RecipeScreenType screenType = RecipeScreenType.UNSET;
-    
-    @Comment(
-            "The location of choose page dialog, will automatically be set to your last location so there is no need to change this.")
-    public RelativePoint choosePageDialogPoint = new RelativePoint(.5, .5);
-    
-    public static class RelativePoint {
-        
-        private double relativeX, relativeY;
-        
-        public RelativePoint(double relativeX, double relativeY) {
-            this.relativeX = relativeX;
-            this.relativeY = relativeY;
-        }
-        
-        public double getRelativeX() {
-            return relativeX;
-        }
-        
-        public double getRelativeY() {
-            return relativeY;
-        }
-        
-        public double getX(double width) {
-            return width * relativeX;
-        }
-        
-        public double getY(double height) {
-            return height * relativeY;
-        }
-        
-    }
-    
-}

+ 307 - 0
src/main/java/me/shedaniel/rei/client/ConfigObjectImpl.java

@@ -0,0 +1,307 @@
+/*
+ * Roughly Enough Items by Danielshe.
+ * Licensed under the MIT License.
+ */
+
+package me.shedaniel.rei.client;
+
+import me.shedaniel.rei.api.ConfigObject;
+import me.shedaniel.rei.gui.config.ItemCheatingMode;
+import me.shedaniel.rei.gui.config.ItemListOrdering;
+import me.shedaniel.rei.gui.config.ItemListOrderingConfig;
+import me.shedaniel.rei.gui.config.RecipeScreenType;
+import me.zeroeightsix.fiber.exception.FiberException;
+import me.zeroeightsix.fiber.tree.ConfigNode;
+import me.zeroeightsix.fiber.tree.ConfigValue;
+import me.zeroeightsix.fiber.tree.Node;
+
+public class ConfigObjectImpl implements ConfigObject {
+    
+    public ConfigNode configNode = new ConfigNode();
+    
+    private Node general = configNode.fork("!general");
+    private Node appearance = configNode.fork("appearance");
+    private Node modules = configNode.fork("modules");
+    private Node technical = configNode.fork("technical");
+    
+    private ConfigValue<Boolean> cheating = ConfigValue.builder(Boolean.class)
+            .withParent(general)
+            .withDefaultValue(false)
+            .withComment("Declares whether cheating mode is on.")
+            .withName("cheating")
+            .build();
+    
+    private ConfigValue<ItemListOrderingConfig> itemListOrdering = ConfigValue.builder(ItemListOrderingConfig.class)
+            .withParent(appearance)
+            .withDefaultValue(ItemListOrderingConfig.REGISTRY_ASCENDING)
+            .withComment("The ordering of the items on the item panel.")
+            .withName("itemListOrdering")
+            .build();
+    
+    private ConfigValue<Boolean> darkTheme = ConfigValue.builder(Boolean.class)
+            .withParent(appearance)
+            .withDefaultValue(false)
+            .withComment("Declares the appearance of REI windows.")
+            .withName("darkTheme")
+            .build();
+    
+    private ConfigValue<RecipeScreenType> recipeScreenType = ConfigValue.builder(RecipeScreenType.class)
+            .withParent(appearance)
+            .withDefaultValue(RecipeScreenType.UNSET)
+            .withComment("The ordering of the items on the item panel.")
+            .withName("recipeScreenType")
+            .build();
+    
+    private ConfigValue<Boolean> loadDefaultPlugin = ConfigValue.builder(Boolean.class)
+            .withParent(technical)
+            .withDefaultValue(true)
+            .withComment("To disable REI's default plugin.\nDon't change this unless you understand what you are doing")
+            .withName("loadDefaultPlugin")
+            .build();
+    
+    private ConfigValue<Boolean> sideSearchField = ConfigValue.builder(Boolean.class)
+            .withParent(appearance)
+            .withDefaultValue(false)
+            .withComment("Declares the position of the search field.")
+            .withName("sideSearchField")
+            .build();
+    
+    private ConfigValue<Boolean> mirrorItemPanel = ConfigValue.builder(Boolean.class)
+            .withParent(appearance)
+            .withDefaultValue(false)
+            .withComment("Declares the position of the item list panel.")
+            .withName("mirrorItemPanel")
+            .build();
+    
+    private ConfigValue<Boolean> enableCraftableOnlyButton = ConfigValue.builder(Boolean.class)
+            .withParent(modules)
+            .withDefaultValue(true)
+            .withComment("Declares whether the craftable filter button is enabled.")
+            .withName("enableCraftableOnlyButton")
+            .build();
+    
+    private ConfigValue<String> gamemodeCommand = ConfigValue.builder(String.class)
+            .withParent(technical)
+            .withDefaultValue("/gamemode {gamemode}")
+            .withComment("Declares the command used to change gamemode.")
+            .withName("gamemodeCommand")
+            .build();
+    
+    private ConfigValue<String> giveCommand = ConfigValue.builder(String.class)
+            .withParent(technical)
+            .withDefaultValue("/give {player_name} {item_identifier}{nbt} {count}")
+            .withComment("Declares the command used in servers to cheat items.")
+            .withName("giveCommand")
+            .build();
+    
+    private ConfigValue<String> weatherCommand = ConfigValue.builder(String.class)
+            .withParent(technical)
+            .withDefaultValue("/weather {weather}")
+            .withComment("Declares the command used to change weather.")
+            .withName("weatherCommand")
+            .build();
+    
+    private ConfigValue<Integer> maxRecipePerPage = ConfigValue.builder(Integer.class)
+            .withParent(appearance)
+            .withDefaultValue(3)
+            .withComment("Declares the maximum amount of recipes displayed in a page if possible.")
+            .withName("maxRecipePerPage")
+            .constraints()
+            .minNumerical(2)
+            .maxNumerical(99)
+            .finish()
+            .build();
+    
+    private ConfigValue<Boolean> showUtilsButtons = ConfigValue.builder(Boolean.class)
+            .withParent(modules)
+            .withDefaultValue(false)
+            .withComment("Declares whether the utils buttons are shown.")
+            .withName("showUtilsButtons")
+            .build();
+    
+    private ConfigValue<Boolean> disableRecipeBook = ConfigValue.builder(Boolean.class)
+            .withParent(modules)
+            .withDefaultValue(false)
+            .withComment("Declares whether REI should remove the recipe book.")
+            .withName("disableRecipeBook")
+            .build();
+    
+    private ConfigValue<Boolean> clickableRecipeArrows = ConfigValue.builder(Boolean.class)
+            .withParent(appearance)
+            .withDefaultValue(true)
+            .withName("clickableRecipeArrows")
+            .build();
+    
+    private ConfigValue<ItemCheatingMode> itemCheatingMode = ConfigValue.builder(ItemCheatingMode.class)
+            .withParent(appearance)
+            .withDefaultValue(ItemCheatingMode.REI_LIKE)
+            .withName("itemCheatingMode")
+            .build();
+    
+    private ConfigValue<Boolean> lightGrayRecipeBorder = ConfigValue.builder(Boolean.class)
+            .withParent(appearance)
+            .withDefaultValue(false)
+            .withComment("Declares the appearance of recipe's border.")
+            .withName("lightGrayRecipeBorder")
+            .build();
+    
+    private ConfigValue<Boolean> appendModNames = ConfigValue.builder(Boolean.class)
+            .withParent(appearance)
+            .withDefaultValue(false)
+            .withComment("Declares whether REI should append mod names to item stacks.")
+            .withName("appendModNames")
+            .build();
+    
+    private ConfigValue<Boolean> villagerScreenPermanentScrollBar = ConfigValue.builder(Boolean.class)
+            .withParent(appearance)
+            .withDefaultValue(false)
+            .withComment("Declares how the scrollbar in villager screen act.")
+            .withName("villagerScreenPermanentScrollBar")
+            .build();
+    
+    private ConfigValue<Boolean> registerRecipesInAnotherThread = ConfigValue.builder(Boolean.class)
+            .withParent(technical)
+            .withDefaultValue(true)
+            .withName("registerRecipesInAnotherThread")
+            .build();
+    
+    private ConfigValue<RelativePoint> choosePageDialogPoint = ConfigValue.builder(RelativePoint.class)
+            .withParent(technical)
+            .withDefaultValue(new RelativePoint(.5, .5))
+            .withName("choosePageDialogPoint")
+            .build();
+    
+    public ConfigObjectImpl() throws FiberException {
+    
+    }
+    
+    @Override
+    public ConfigNode getConfigNode() {
+        return configNode;
+    }
+    
+    @Override
+    public boolean isCheating() {
+        return cheating.getValue();
+    }
+    
+    @Override
+    public void setCheating(boolean cheating) {
+        this.cheating.setValue(cheating);
+    }
+    
+    @Override
+    public ItemListOrdering getItemListOrdering() {
+        return itemListOrdering.getValue().getOrdering();
+    }
+    
+    @Override
+    public boolean isItemListAscending() {
+        return itemListOrdering.getValue().isAscending();
+    }
+    
+    @Override
+    public boolean isUsingDarkTheme() {
+        return darkTheme.getValue().booleanValue();
+    }
+    
+    @Override
+    public boolean shouldAppendModNames() {
+        return appendModNames.getValue().booleanValue();
+    }
+    
+    @Override
+    public RecipeScreenType getRecipeScreenType() {
+        return recipeScreenType.getValue();
+    }
+    
+    @Override
+    public void setRecipeScreenType(RecipeScreenType recipeScreenType) {
+        this.recipeScreenType.setValue(recipeScreenType);
+    }
+    
+    @Override
+    public boolean isLoadingDefaultPlugin() {
+        return loadDefaultPlugin.getValue().booleanValue();
+    }
+    
+    @Override
+    public boolean isSideSearchField() {
+        return sideSearchField.getValue().booleanValue();
+    }
+    
+    @Override
+    public boolean isLeftHandSidePanel() {
+        return mirrorItemPanel.getValue().booleanValue();
+    }
+    
+    @Override
+    public boolean isCraftableFilterEnabled() {
+        return enableCraftableOnlyButton.getValue().booleanValue();
+    }
+    
+    @Override
+    public String getGamemodeCommand() {
+        return gamemodeCommand.getValue();
+    }
+    
+    @Override
+    public String getGiveCommand() {
+        return giveCommand.getValue();
+    }
+    
+    @Override
+    public String getWeatherCommand() {
+        return weatherCommand.getValue();
+    }
+    
+    @Override
+    public int getMaxRecipePerPage() {
+        return maxRecipePerPage.getValue().intValue();
+    }
+    
+    @Override
+    public boolean doesShowUtilsButtons() {
+        return showUtilsButtons.getValue().booleanValue();
+    }
+    
+    @Override
+    public boolean doesDisableRecipeBook() {
+        return disableRecipeBook.getValue().booleanValue();
+    }
+    
+    @Override
+    public boolean areClickableRecipeArrowsEnabled() {
+        return clickableRecipeArrows.getValue().booleanValue();
+    }
+    
+    @Override
+    public ItemCheatingMode getItemCheatingMode() {
+        return itemCheatingMode.getValue();
+    }
+    
+    @Override
+    public boolean isUsingLightGrayRecipeBorder() {
+        return lightGrayRecipeBorder.getValue().booleanValue();
+    }
+    
+    @Override
+    public boolean doesVillagerScreenHavePermanentScrollBar() {
+        return villagerScreenPermanentScrollBar.getValue().booleanValue();
+    }
+    
+    @Override
+    public boolean doesRegisterRecipesInAnotherThread() {
+        return registerRecipesInAnotherThread.getValue().booleanValue();
+    }
+    
+    @Override
+    public RelativePoint getChoosePageDialogPoint() {
+        return choosePageDialogPoint.getValue();
+    }
+    
+    @Override
+    public void setChoosePageDialogPoint(RelativePoint choosePageDialogPoint) {
+        this.choosePageDialogPoint.setValue(choosePageDialogPoint);
+    }
+}

+ 1 - 1
src/main/java/me/shedaniel/rei/client/ScreenHelper.java

@@ -82,7 +82,7 @@ public class ScreenHelper implements ClientModInitializer {
     }
     
     public static boolean isDarkModeEnabled() {
-        return RoughlyEnoughItemsCore.getConfigManager().getConfig().darkTheme;
+        return RoughlyEnoughItemsCore.getConfigManager().getConfig().isUsingDarkTheme();
     }
     
     @Override

+ 16 - 16
src/main/java/me/shedaniel/rei/gui/ContainerScreenOverlay.java

@@ -43,7 +43,7 @@ import java.util.List;
 import java.util.*;
 import java.util.stream.Collectors;
 
-public class ContainerScreenOverlay extends AbstractParentElement implements Drawable {
+public class ContainerScreenOverlay extends Widget {
     
     private static final Identifier CHEST_GUI_TEXTURE = new Identifier("roughlyenoughitems", "textures/gui/recipecontainer.png");
     private static final List<QueuedTooltip> QUEUED_TOOLTIPS = Lists.newArrayList();
@@ -71,7 +71,7 @@ public class ContainerScreenOverlay extends AbstractParentElement implements Dra
         this.children().clear();
         this.window = MinecraftClient.getInstance().window;
         DisplayHelper.DisplayBoundsHandler boundsHandler = RoughlyEnoughItemsCore.getDisplayHelper().getResponsibleBoundsHandler(MinecraftClient.getInstance().currentScreen.getClass());
-        this.rectangle = RoughlyEnoughItemsCore.getConfigManager().getConfig().mirrorItemPanel ? boundsHandler.getLeftBounds(MinecraftClient.getInstance().currentScreen) : boundsHandler.getRightBounds(MinecraftClient.getInstance().currentScreen);
+        this.rectangle = RoughlyEnoughItemsCore.getConfigManager().getConfig().isLeftHandSidePanel() ? boundsHandler.getLeftBounds(MinecraftClient.getInstance().currentScreen) : boundsHandler.getRightBounds(MinecraftClient.getInstance().currentScreen);
         widgets.add(itemListOverlay = new ItemListOverlay(page));
         itemListOverlay.updateList(boundsHandler, boundsHandler.getItemListArea(rectangle), page, searchTerm, false);
         
@@ -117,7 +117,7 @@ public class ContainerScreenOverlay extends AbstractParentElement implements Dra
         if (setPage)
             page = MathHelper.clamp(page, 0, getTotalPage());
         
-        widgets.add(new ButtonWidget(RoughlyEnoughItemsCore.getConfigManager().getConfig().mirrorItemPanel ? window.getScaledWidth() - 30 : 10, 10, 20, 20, "") {
+        widgets.add(new ButtonWidget(RoughlyEnoughItemsCore.getConfigManager().getConfig().isLeftHandSidePanel() ? window.getScaledWidth() - 30 : 10, 10, 20, 20, "") {
             @Override
             public void onPressed() {
                 if (Screen.hasShiftDown()) {
@@ -162,11 +162,11 @@ public class ContainerScreenOverlay extends AbstractParentElement implements Dra
                 return false;
             }
         });
-        if (RoughlyEnoughItemsCore.getConfigManager().getConfig().showUtilsButtons) {
-            widgets.add(new ButtonWidget(RoughlyEnoughItemsCore.getConfigManager().getConfig().mirrorItemPanel ? window.getScaledWidth() - 55 : 35, 10, 20, 20, "") {
+        if (RoughlyEnoughItemsCore.getConfigManager().getConfig().doesShowUtilsButtons()) {
+            widgets.add(new ButtonWidget(RoughlyEnoughItemsCore.getConfigManager().getConfig().isLeftHandSidePanel() ? window.getScaledWidth() - 55 : 35, 10, 20, 20, "") {
                 @Override
                 public void onPressed() {
-                    MinecraftClient.getInstance().player.sendChatMessage(RoughlyEnoughItemsCore.getConfigManager().getConfig().gamemodeCommand.replaceAll("\\{gamemode}", getNextGameMode(Screen.hasShiftDown()).getName()));
+                    MinecraftClient.getInstance().player.sendChatMessage(RoughlyEnoughItemsCore.getConfigManager().getConfig().getGamemodeCommand().replaceAll("\\{gamemode}", getNextGameMode(Screen.hasShiftDown()).getName()));
                 }
                 
                 @Override
@@ -185,12 +185,12 @@ public class ContainerScreenOverlay extends AbstractParentElement implements Dra
                     return false;
                 }
             });
-            int xxx = RoughlyEnoughItemsCore.getConfigManager().getConfig().mirrorItemPanel ? window.getScaledWidth() - 30 : 10;
+            int xxx = RoughlyEnoughItemsCore.getConfigManager().getConfig().isLeftHandSidePanel() ? window.getScaledWidth() - 30 : 10;
             for (Weather weather : Weather.values()) {
                 widgets.add(new ButtonWidget(xxx, 35, 20, 20, "") {
                     @Override
                     public void onPressed() {
-                        MinecraftClient.getInstance().player.sendChatMessage(RoughlyEnoughItemsCore.getConfigManager().getConfig().weatherCommand.replaceAll("\\{weather}", weather.name().toLowerCase(Locale.ROOT)));
+                        MinecraftClient.getInstance().player.sendChatMessage(RoughlyEnoughItemsCore.getConfigManager().getConfig().getWeatherCommand().replaceAll("\\{weather}", weather.name().toLowerCase(Locale.ROOT)));
                     }
                     
                     @Override
@@ -212,7 +212,7 @@ public class ContainerScreenOverlay extends AbstractParentElement implements Dra
                         return false;
                     }
                 });
-                xxx += RoughlyEnoughItemsCore.getConfigManager().getConfig().mirrorItemPanel ? -25 : 25;
+                xxx += RoughlyEnoughItemsCore.getConfigManager().getConfig().isLeftHandSidePanel() ? -25 : 25;
             }
         }
         widgets.add(new ClickableLabelWidget(rectangle.x + (rectangle.width / 2), rectangle.y + 10, "", getTotalPage() > 0) {
@@ -250,7 +250,7 @@ public class ContainerScreenOverlay extends AbstractParentElement implements Dra
             searchTerm = s;
             itemListOverlay.updateList(boundsHandler, boundsHandler.getItemListArea(rectangle), page, searchTerm, true);
         });
-        if (RoughlyEnoughItemsCore.getConfigManager().getConfig().enableCraftableOnlyButton)
+        if (RoughlyEnoughItemsCore.getConfigManager().getConfig().isCraftableFilterEnabled())
             this.widgets.add(toggleButtonWidget = new CraftableToggleButtonWidget(getCraftableToggleArea()) {
                 @Override
                 public void onPressed() {
@@ -319,8 +319,8 @@ public class ContainerScreenOverlay extends AbstractParentElement implements Dra
     }
     
     private Rectangle getTextFieldArea() {
-        int widthRemoved = RoughlyEnoughItemsCore.getConfigManager().getConfig().enableCraftableOnlyButton ? 22 : 2;
-        if (RoughlyEnoughItemsCore.getConfigManager().getConfig().sideSearchField)
+        int widthRemoved = RoughlyEnoughItemsCore.getConfigManager().getConfig().isCraftableFilterEnabled() ? 22 : 2;
+        if (RoughlyEnoughItemsCore.getConfigManager().getConfig().isSideSearchField())
             return new Rectangle(rectangle.x + 2, window.getScaledHeight() - 22, rectangle.width - 6 - widthRemoved, 18);
         if (MinecraftClient.getInstance().currentScreen instanceof RecipeViewingScreen) {
             RecipeViewingScreen widget = (RecipeViewingScreen) MinecraftClient.getInstance().currentScreen;
@@ -351,7 +351,7 @@ public class ContainerScreenOverlay extends AbstractParentElement implements Dra
     @Override
     public void render(int mouseX, int mouseY, float delta) {
         List<ItemStack> currentStacks = ClientHelper.getInstance().getInventoryItemsTypes();
-        if (RoughlyEnoughItemsCore.getDisplayHelper().getBaseBoundsHandler() != null && RoughlyEnoughItemsCore.getDisplayHelper().getBaseBoundsHandler().shouldRecalculateArea(!RoughlyEnoughItemsCore.getConfigManager().getConfig().mirrorItemPanel, rectangle))
+        if (RoughlyEnoughItemsCore.getDisplayHelper().getBaseBoundsHandler() != null && RoughlyEnoughItemsCore.getDisplayHelper().getBaseBoundsHandler().shouldRecalculateArea(!RoughlyEnoughItemsCore.getConfigManager().getConfig().isLeftHandSidePanel(), rectangle))
             shouldReInit = true;
         if (shouldReInit)
             init(true);
@@ -373,7 +373,7 @@ public class ContainerScreenOverlay extends AbstractParentElement implements Dra
         GlStateManager.color4f(1.0F, 1.0F, 1.0F, 1.0F);
         GuiLighting.disable();
         this.renderWidgets(mouseX, mouseY, delta);
-        if (MinecraftClient.getInstance().currentScreen instanceof AbstractContainerScreen && RoughlyEnoughItemsCore.getConfigManager().getConfig().clickableRecipeArrows) {
+        if (MinecraftClient.getInstance().currentScreen instanceof AbstractContainerScreen && RoughlyEnoughItemsCore.getConfigManager().getConfig().areClickableRecipeArrowsEnabled()) {
             ContainerScreenHooks hooks = (ContainerScreenHooks) MinecraftClient.getInstance().currentScreen;
             for (RecipeHelperImpl.ScreenClickArea area : RecipeHelper.getInstance().getScreenClickAreas())
                 if (area.getScreenClass().equals(MinecraftClient.getInstance().currentScreen.getClass()))
@@ -532,7 +532,7 @@ public class ContainerScreenOverlay extends AbstractParentElement implements Dra
     public boolean mouseClicked(double double_1, double double_2, int int_1) {
         if (!ScreenHelper.isOverlayVisible())
             return false;
-        if (MinecraftClient.getInstance().currentScreen instanceof AbstractContainerScreen && RoughlyEnoughItemsCore.getConfigManager().getConfig().clickableRecipeArrows) {
+        if (MinecraftClient.getInstance().currentScreen instanceof AbstractContainerScreen && RoughlyEnoughItemsCore.getConfigManager().getConfig().areClickableRecipeArrowsEnabled()) {
             ContainerScreenHooks hooks = (ContainerScreenHooks) MinecraftClient.getInstance().currentScreen;
             for (RecipeHelperImpl.ScreenClickArea area : RecipeHelper.getInstance().getScreenClickAreas())
                 if (area.getScreenClass().equals(MinecraftClient.getInstance().currentScreen.getClass()))
@@ -556,7 +556,7 @@ public class ContainerScreenOverlay extends AbstractParentElement implements Dra
         if (!rectangle.contains(mouseX, mouseY))
             return false;
         for (DisplayHelper.DisplayBoundsHandler handler : RoughlyEnoughItemsCore.getDisplayHelper().getSortedBoundsHandlers(MinecraftClient.getInstance().currentScreen.getClass())) {
-            ActionResult in = handler.isInZone(!RoughlyEnoughItemsCore.getConfigManager().getConfig().mirrorItemPanel, mouseX, mouseY);
+            ActionResult in = handler.isInZone(!RoughlyEnoughItemsCore.getConfigManager().getConfig().isLeftHandSidePanel(), mouseX, mouseY);
             if (in != ActionResult.PASS)
                 return in == ActionResult.SUCCESS;
         }

+ 3 - 2
src/main/java/me/shedaniel/rei/gui/PreRecipeViewingScreen.java

@@ -15,6 +15,7 @@ import me.shedaniel.rei.gui.config.RecipeScreenType;
 import me.shedaniel.rei.gui.widget.ButtonWidget;
 import me.shedaniel.rei.gui.widget.Widget;
 import me.shedaniel.rei.gui.widget.WidgetWithBounds;
+import me.zeroeightsix.fiber.exception.FiberException;
 import net.minecraft.client.MinecraftClient;
 import net.minecraft.client.gui.Element;
 import net.minecraft.client.gui.screen.Screen;
@@ -53,10 +54,10 @@ public class PreRecipeViewingScreen extends Screen {
         this.widgets.add(new ButtonWidget(width / 2 - 100, height - 40, 200, 20, I18n.translate("text.rei.select")) {
             @Override
             public void onPressed() {
-                RoughlyEnoughItemsCore.getConfigManager().getConfig().screenType = original ? RecipeScreenType.ORIGINAL : RecipeScreenType.VILLAGER;
+                RoughlyEnoughItemsCore.getConfigManager().getConfig().setRecipeScreenType(original ? RecipeScreenType.ORIGINAL : RecipeScreenType.VILLAGER);
                 try {
                     RoughlyEnoughItemsCore.getConfigManager().saveConfig();
-                } catch (IOException e) {
+                } catch (IOException | FiberException e) {
                     e.printStackTrace();
                 }
                 ClientHelper.getInstance().openRecipeViewingScreen(map);

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

@@ -350,12 +350,12 @@ public class RecipeViewingScreen extends Screen {
         if (selectedCategory.getFixedRecipesPerPage() > 0)
             return selectedCategory.getFixedRecipesPerPage() - 1;
         int height = selectedCategory.getDisplayHeight();
-        return MathHelper.clamp(MathHelper.floor(((double) largestHeight - 40d) / ((double) height + 7d)) - 1, 0, Math.min(RoughlyEnoughItemsCore.getConfigManager().getConfig().maxRecipePerPage - 1, selectedCategory.getMaximumRecipePerPage() - 1));
+        return MathHelper.clamp(MathHelper.floor(((double) largestHeight - 40d) / ((double) height + 7d)) - 1, 0, Math.min(RoughlyEnoughItemsCore.getConfigManager().getConfig().getMaxRecipePerPage() - 1, selectedCategory.getMaximumRecipePerPage() - 1));
     }
     
     private int getRecipesPerPageByHeight() {
         int height = selectedCategory.getDisplayHeight();
-        return MathHelper.clamp(MathHelper.floor(((double) guiHeight - 40d) / ((double) height + 7d)), 0, Math.min(RoughlyEnoughItemsCore.getConfigManager().getConfig().maxRecipePerPage - 1, selectedCategory.getMaximumRecipePerPage() - 1));
+        return MathHelper.clamp(MathHelper.floor(((double) guiHeight - 40d) / ((double) height + 7d)), 0, Math.min(RoughlyEnoughItemsCore.getConfigManager().getConfig().getMaxRecipePerPage() - 1, selectedCategory.getMaximumRecipePerPage() - 1));
     }
     
     @Override

+ 1 - 1
src/main/java/me/shedaniel/rei/gui/VillagerRecipeViewingScreen.java

@@ -293,7 +293,7 @@ public class VillagerRecipeViewingScreen extends Screen {
     
     @Override
     public void render(int mouseX, int mouseY, float delta) {
-        if (RoughlyEnoughItemsCore.getConfigManager().getConfig().villagerScreenPermanentScrollBar) {
+        if (RoughlyEnoughItemsCore.getConfigManager().getConfig().doesVillagerScreenHavePermanentScrollBar()) {
             scrollBarAlphaFutureTime = System.currentTimeMillis();
             scrollBarAlphaFuture = 0;
             scrollBarAlpha = 1;

+ 9 - 0
src/main/java/me/shedaniel/rei/gui/config/ItemCheatingMode.java

@@ -5,7 +5,16 @@
 
 package me.shedaniel.rei.gui.config;
 
+import net.minecraft.client.resource.language.I18n;
+
+import java.util.Locale;
+
 public enum ItemCheatingMode {
     REI_LIKE,
     JEI_LIKE;
+    
+    @Override
+    public String toString() {
+        return I18n.translate("config.roughlyenoughitems.itemCheatingMode." + name().toLowerCase(Locale.ROOT));
+    }
 }

+ 1 - 1
src/main/java/me/shedaniel/rei/gui/config/ItemListOrderingConfig.java

@@ -38,6 +38,6 @@ public enum ItemListOrderingConfig {
     
     @Override
     public String toString() {
-        return I18n.translate("text.rei.config.list_ordering_button", I18n.translate(getOrdering().getNameTranslationKey()), I18n.translate(isAscending ? "ordering.rei.ascending" : "ordering.rei.descending"));
+        return I18n.translate("config.roughlyenoughitems.list_ordering_button", I18n.translate(getOrdering().getNameTranslationKey()), I18n.translate(isAscending ? "ordering.rei.ascending" : "ordering.rei.descending"));
     }
 }

+ 1 - 1
src/main/java/me/shedaniel/rei/gui/config/RecipeScreenType.java

@@ -16,6 +16,6 @@ public enum RecipeScreenType {
     
     @Override
     public String toString() {
-        return I18n.translate("text.rei.config.recipe_screen_type." + name().toLowerCase(Locale.ROOT));
+        return I18n.translate("config.roughlyenoughitems.recipeScreenType." + name().toLowerCase(Locale.ROOT));
     }
 }

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

@@ -44,9 +44,9 @@ public class ItemListOverlay extends Widget {
     
     static {
         ASCENDING_COMPARATOR = (itemStack, t1) -> {
-            if (RoughlyEnoughItemsCore.getConfigManager().getConfig().itemListOrdering.equals(ItemListOrdering.name))
+            if (RoughlyEnoughItemsCore.getConfigManager().getConfig().getItemListOrdering().equals(ItemListOrdering.name))
                 return tryGetItemStackName(itemStack).compareToIgnoreCase(tryGetItemStackName(t1));
-            if (RoughlyEnoughItemsCore.getConfigManager().getConfig().itemListOrdering.equals(ItemListOrdering.item_groups)) {
+            if (RoughlyEnoughItemsCore.getConfigManager().getConfig().getItemListOrdering().equals(ItemListOrdering.item_groups)) {
                 List<ItemGroup> itemGroups = Arrays.asList(ItemGroup.GROUPS);
                 return itemGroups.indexOf(itemStack.getItem().getGroup()) - itemGroups.indexOf(t1.getItem().getGroup());
             }
@@ -191,9 +191,9 @@ public class ItemListOverlay extends Widget {
                             if (ClientHelper.getInstance().isCheating()) {
                                 if (getCurrentItemStack() != null && !getCurrentItemStack().isEmpty()) {
                                     ItemStack cheatedStack = getCurrentItemStack().copy();
-                                    if (RoughlyEnoughItemsCore.getConfigManager().getConfig().itemCheatingMode == ItemCheatingMode.REI_LIKE)
+                                    if (RoughlyEnoughItemsCore.getConfigManager().getConfig().getItemCheatingMode() == ItemCheatingMode.REI_LIKE)
                                         cheatedStack.setCount(button != 1 ? 1 : cheatedStack.getMaxCount());
-                                    else if (RoughlyEnoughItemsCore.getConfigManager().getConfig().itemCheatingMode == ItemCheatingMode.JEI_LIKE)
+                                    else if (RoughlyEnoughItemsCore.getConfigManager().getConfig().getItemCheatingMode() == ItemCheatingMode.JEI_LIKE)
                                         cheatedStack.setCount(button != 0 ? 1 : cheatedStack.getMaxCount());
                                     else
                                         cheatedStack.setCount(1);
@@ -231,7 +231,7 @@ public class ItemListOverlay extends Widget {
     
     public boolean canBeFit(int left, int top, Rectangle listArea) {
         for (DisplayHelper.DisplayBoundsHandler sortedBoundsHandler : RoughlyEnoughItemsCore.getDisplayHelper().getSortedBoundsHandlers(minecraft.currentScreen.getClass())) {
-            ActionResult fit = sortedBoundsHandler.canItemSlotWidgetFit(!RoughlyEnoughItemsCore.getConfigManager().getConfig().mirrorItemPanel, left, top, minecraft.currentScreen, listArea);
+            ActionResult fit = sortedBoundsHandler.canItemSlotWidgetFit(!RoughlyEnoughItemsCore.getConfigManager().getConfig().isLeftHandSidePanel(), left, top, minecraft.currentScreen, listArea);
             if (fit != ActionResult.PASS)
                 return fit == ActionResult.SUCCESS;
         }
@@ -257,9 +257,9 @@ public class ItemListOverlay extends Widget {
     private List<ItemStack> processSearchTerm(String searchTerm, List<ItemStack> ol, List<ItemStack> inventoryItems) {
         lastSearchArgument.clear();
         List<ItemStack> os = new LinkedList<>(ol);
-        if (RoughlyEnoughItemsCore.getConfigManager().getConfig().itemListOrdering != ItemListOrdering.registry)
+        if (RoughlyEnoughItemsCore.getConfigManager().getConfig().getItemListOrdering() != ItemListOrdering.registry)
             os = ol.stream().sorted(ASCENDING_COMPARATOR).collect(Collectors.toList());
-        if (!RoughlyEnoughItemsCore.getConfigManager().getConfig().isAscending)
+        if (!RoughlyEnoughItemsCore.getConfigManager().getConfig().isItemListAscending())
             Collections.reverse(os);
         String[] splitSearchTerm = StringUtils.splitByWholeSeparatorPreserveAllTokens(searchTerm, "|");
         Arrays.stream(splitSearchTerm).forEachOrdered(s -> {

+ 2 - 2
src/main/java/me/shedaniel/rei/gui/widget/RecipeBaseWidget.java

@@ -80,7 +80,7 @@ public class RecipeBaseWidget extends WidgetWithBounds {
     }
     
     protected boolean isRendering() {
-        return RoughlyEnoughItemsCore.getConfigManager().getConfig().screenType != RecipeScreenType.VILLAGER;
+        return RoughlyEnoughItemsCore.getConfigManager().getConfig().getRecipeScreenType() != RecipeScreenType.VILLAGER;
     }
     
     protected int getInnerColor() {
@@ -88,7 +88,7 @@ public class RecipeBaseWidget extends WidgetWithBounds {
     }
     
     protected int getTextureOffset() {
-        return RoughlyEnoughItemsCore.getConfigManager().getConfig().lightGrayRecipeBorder ? 0 : 66;
+        return RoughlyEnoughItemsCore.getConfigManager().getConfig().isUsingLightGrayRecipeBorder() ? 0 : 66;
     }
     
     

+ 5 - 4
src/main/java/me/shedaniel/rei/gui/widget/RecipeChoosePageWidget.java

@@ -9,9 +9,10 @@ import com.google.common.collect.Lists;
 import com.mojang.blaze3d.platform.GlStateManager;
 import me.shedaniel.rei.RoughlyEnoughItemsCore;
 import me.shedaniel.rei.api.ConfigManager;
-import me.shedaniel.rei.client.ConfigObject;
+import me.shedaniel.rei.client.ConfigObjectImpl;
 import me.shedaniel.rei.client.ScreenHelper;
 import me.shedaniel.rei.gui.RecipeViewingScreen;
+import me.zeroeightsix.fiber.exception.FiberException;
 import net.minecraft.client.MinecraftClient;
 import net.minecraft.client.render.GuiLighting;
 import net.minecraft.client.resource.language.I18n;
@@ -45,7 +46,7 @@ public class RecipeChoosePageWidget extends DraggableWidget {
     
     private static Point getPointFromConfig() {
         Window window = MinecraftClient.getInstance().window;
-        ConfigObject.RelativePoint point = RoughlyEnoughItemsCore.getConfigManager().getConfig().choosePageDialogPoint;
+        ConfigObjectImpl.RelativePoint point = RoughlyEnoughItemsCore.getConfigManager().getConfig().getChoosePageDialogPoint();
         return new Point((int) point.getX(window.getScaledWidth()), (int) point.getY(window.getScaledHeight()));
     }
     
@@ -185,10 +186,10 @@ public class RecipeChoosePageWidget extends DraggableWidget {
     public void onMouseReleaseMidPoint(Point midPoint) {
         ConfigManager configManager = RoughlyEnoughItemsCore.getConfigManager();
         Window window = minecraft.window;
-        configManager.getConfig().choosePageDialogPoint = new ConfigObject.RelativePoint(midPoint.getX() / window.getScaledWidth(), midPoint.getY() / window.getScaledHeight());
+        configManager.getConfig().setChoosePageDialogPoint(new ConfigObjectImpl.RelativePoint(midPoint.getX() / window.getScaledWidth(), midPoint.getY() / window.getScaledHeight()));
         try {
             configManager.saveConfig();
-        } catch (IOException e) {
+        } catch (IOException | FiberException e) {
             e.printStackTrace();
         }
     }

+ 8 - 5
src/main/java/me/shedaniel/rei/gui/widget/SlotWidget.java

@@ -8,6 +8,7 @@ package me.shedaniel.rei.gui.widget;
 import com.google.common.collect.Lists;
 import com.mojang.blaze3d.platform.GlStateManager;
 import me.shedaniel.cloth.api.ClientUtils;
+import me.shedaniel.rei.RoughlyEnoughItemsCore;
 import me.shedaniel.rei.api.ClientHelper;
 import me.shedaniel.rei.api.Renderer;
 import me.shedaniel.rei.client.ScreenHelper;
@@ -151,13 +152,15 @@ public class SlotWidget extends WidgetWithBounds {
     }
     
     protected List<String> getTooltip(ItemStack itemStack) {
-        final String modString = ClientHelper.getInstance().getFormattedModFromItem(itemStack.getItem());
         List<String> toolTip = Lists.newArrayList(ItemListOverlay.tryGetItemStackToolTip(itemStack, true));
-        String s1 = ClientHelper.getInstance().getModFromItem(itemStack.getItem()).toLowerCase(Locale.ROOT);
         toolTip.addAll(getExtraToolTips(itemStack));
-        if (!modString.isEmpty()) {
-            toolTip.removeIf(s -> s.toLowerCase(Locale.ROOT).contains(s1));
-            toolTip.add(modString);
+        if (RoughlyEnoughItemsCore.getConfigManager().getConfig().shouldAppendModNames()) {
+            final String modString = ClientHelper.getInstance().getFormattedModFromItem(itemStack.getItem());
+            String s1 = ClientHelper.getInstance().getModFromItem(itemStack.getItem()).toLowerCase(Locale.ROOT);
+            if (!modString.isEmpty()) {
+                toolTip.removeIf(s -> s.toLowerCase(Locale.ROOT).contains(s1));
+                toolTip.add(modString);
+            }
         }
         return toolTip;
     }

+ 1 - 1
src/main/java/me/shedaniel/rei/plugin/DefaultAutoCraftingPlugin.java

@@ -29,7 +29,7 @@ public class DefaultAutoCraftingPlugin implements REIPluginV0 {
     
     @Override
     public void registerOthers(RecipeHelper recipeHelper) {
-        if (!RoughlyEnoughItemsCore.getConfigManager().getConfig().loadDefaultPlugin) {
+        if (!RoughlyEnoughItemsCore.getConfigManager().getConfig().isLoadingDefaultPlugin()) {
             return;
         }
         recipeHelper.registerAutoCraftingHandler(new AutoCraftingTableBookHandler());

+ 9 - 8
src/main/java/me/shedaniel/rei/plugin/DefaultPlugin.java

@@ -8,7 +8,9 @@ package me.shedaniel.rei.plugin;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import me.shedaniel.rei.RoughlyEnoughItemsCore;
-import me.shedaniel.rei.api.*;
+import me.shedaniel.rei.api.DisplayHelper;
+import me.shedaniel.rei.api.ItemRegistry;
+import me.shedaniel.rei.api.RecipeHelper;
 import me.shedaniel.rei.api.plugins.REIPluginV0;
 import me.shedaniel.rei.client.ScreenHelper;
 import me.shedaniel.rei.gui.RecipeViewingScreen;
@@ -42,7 +44,6 @@ import net.minecraft.client.gui.screen.ingame.*;
 import net.minecraft.client.gui.screen.recipebook.RecipeBookProvider;
 import net.minecraft.enchantment.Enchantment;
 import net.minecraft.enchantment.EnchantmentHelper;
-import net.minecraft.item.EnchantedBookItem;
 import net.minecraft.item.ItemConvertible;
 import net.minecraft.item.ItemStack;
 import net.minecraft.item.Items;
@@ -92,7 +93,7 @@ public class DefaultPlugin implements REIPluginV0 {
     
     @Override
     public void registerItems(ItemRegistry itemRegistry) {
-        if (!RoughlyEnoughItemsCore.getConfigManager().getConfig().loadDefaultPlugin) {
+        if (!RoughlyEnoughItemsCore.getConfigManager().getConfig().isLoadingDefaultPlugin()) {
             return;
         }
         Registry.ITEM.stream().forEach(item -> {
@@ -115,7 +116,7 @@ public class DefaultPlugin implements REIPluginV0 {
     
     @Override
     public void registerPluginCategories(RecipeHelper recipeHelper) {
-        if (!RoughlyEnoughItemsCore.getConfigManager().getConfig().loadDefaultPlugin) {
+        if (!RoughlyEnoughItemsCore.getConfigManager().getConfig().isLoadingDefaultPlugin()) {
             return;
         }
         recipeHelper.registerCategory(new DefaultCraftingCategory());
@@ -131,7 +132,7 @@ public class DefaultPlugin implements REIPluginV0 {
     
     @Override
     public void registerRecipeDisplays(RecipeHelper recipeHelper) {
-        if (!RoughlyEnoughItemsCore.getConfigManager().getConfig().loadDefaultPlugin) {
+        if (!RoughlyEnoughItemsCore.getConfigManager().getConfig().isLoadingDefaultPlugin()) {
             return;
         }
         recipeHelper.registerRecipes(CRAFTING, ShapelessRecipe.class, DefaultShapelessDisplay::new);
@@ -182,7 +183,7 @@ public class DefaultPlugin implements REIPluginV0 {
     
     @Override
     public void registerBounds(DisplayHelper displayHelper) {
-        if (!RoughlyEnoughItemsCore.getConfigManager().getConfig().loadDefaultPlugin) {
+        if (!RoughlyEnoughItemsCore.getConfigManager().getConfig().isLoadingDefaultPlugin()) {
             return;
         }
         displayHelper.getBaseBoundsHandler().registerExclusionZones(AbstractInventoryScreen.class, new DefaultPotionEffectExclusionZones());
@@ -272,7 +273,7 @@ public class DefaultPlugin implements REIPluginV0 {
             
             @Override
             public Rectangle getItemListArea(Rectangle rectangle) {
-                return new Rectangle(rectangle.x + 1, rectangle.y + 24, rectangle.width - 2, rectangle.height - (RoughlyEnoughItemsCore.getConfigManager().getConfig().sideSearchField ? 27 + 22 : 27));
+                return new Rectangle(rectangle.x + 1, rectangle.y + 24, rectangle.width - 2, rectangle.height - (RoughlyEnoughItemsCore.getConfigManager().getConfig().isSideSearchField() ? 27 + 22 : 27));
             }
             
             @Override
@@ -284,7 +285,7 @@ public class DefaultPlugin implements REIPluginV0 {
     
     @Override
     public void registerOthers(RecipeHelper recipeHelper) {
-        if (!RoughlyEnoughItemsCore.getConfigManager().getConfig().loadDefaultPlugin) {
+        if (!RoughlyEnoughItemsCore.getConfigManager().getConfig().isLoadingDefaultPlugin()) {
             return;
         }
         recipeHelper.registerWorkingStations(CRAFTING, new ItemStack(Items.CRAFTING_TABLE));

+ 35 - 142
src/main/java/me/shedaniel/rei/utils/ClothScreenRegistry.java

@@ -5,166 +5,59 @@
 
 package me.shedaniel.rei.utils;
 
-import me.shedaniel.cloth.hooks.ScreenHooks;
-import me.shedaniel.clothconfig2.api.ConfigBuilder;
-import me.shedaniel.clothconfig2.api.ConfigCategory;
 import me.shedaniel.clothconfig2.api.ConfigEntryBuilder;
+import me.shedaniel.fiber2cloth.api.Fiber2Cloth;
 import me.shedaniel.rei.RoughlyEnoughItemsCore;
 import me.shedaniel.rei.api.ConfigManager;
-import me.shedaniel.rei.client.ScreenHelper;
 import me.shedaniel.rei.gui.config.ItemCheatingMode;
 import me.shedaniel.rei.gui.config.ItemListOrderingConfig;
 import me.shedaniel.rei.gui.config.RecipeScreenType;
-import me.shedaniel.rei.gui.credits.CreditsScreen;
-import net.minecraft.client.MinecraftClient;
+import me.zeroeightsix.fiber.exception.FiberException;
+import me.zeroeightsix.fiber.tree.ConfigValue;
 import net.minecraft.client.gui.screen.Screen;
-import net.minecraft.client.gui.widget.ButtonWidget;
-import net.minecraft.client.resource.language.I18n;
 
 import java.io.IOException;
-import java.util.Locale;
-import java.util.Optional;
+import java.util.List;
+
+import static me.shedaniel.fiber2cloth.impl.Fiber2ClothImpl.error;
+import static me.shedaniel.fiber2cloth.impl.Fiber2ClothImpl.splitLine;
 
 public class ClothScreenRegistry {
     
-    public static final String RESET = "text.cloth-config.reset_value";
-    
-    @SuppressWarnings("deprecation")
     public static Screen getConfigScreen(Screen parent) {
         final ConfigManager configManager = RoughlyEnoughItemsCore.getConfigManager();
-        ConfigBuilder builder = ConfigBuilder.create().setParentScreen(parent).setTitle("text.rei.config.title").setSavingRunnable(() -> {
+        ConfigEntryBuilder configEntryBuilder = ConfigEntryBuilder.create();
+        return Fiber2Cloth.create(parent, "roughlyenoughitems", configManager.getConfig().getConfigNode(), "config.roughlyenoughitems.title").setSaveRunnable(() -> {
             try {
                 configManager.saveConfig();
-            } catch (IOException e) {
+            } catch (IOException | FiberException e) {
                 e.printStackTrace();
             }
-        });
-        ConfigEntryBuilder eb = ConfigEntryBuilder.create();
-        ConfigCategory general = builder.getOrCreateCategory("text.rei.config.general");
-        general.addEntry(eb.startBooleanToggle("text.rei.config.cheating", configManager.getConfig().cheating)
-                .setDefaultValue(false)
-                .setSaveConsumer(bool -> configManager.getConfig().cheating = bool)
-                .setTooltipSupplier(bool -> {
-                    String s = null;
-                    if (!bool)
-                        s = I18n.translate("text.rei.cheating_disabled");
-                    else if (!RoughlyEnoughItemsCore.hasOperatorPermission())
-                        s = I18n.translate("text.rei.cheating_enabled_no_perms");
-                    else if (RoughlyEnoughItemsCore.hasPermissionToUsePackets())
-                        s = I18n.translate("text.rei.cheating_enabled");
-                    else
-                        s = I18n.translate("text.rei.cheating_limited_enabled");
-                    return Optional.ofNullable(new String[]{s});
-                })
-                .build());
-        ConfigCategory appearance = builder.getOrCreateCategory("text.rei.config.appearance");
-        appearance.addEntry(eb.startBooleanToggle("text.rei.config.appearance_theme", ScreenHelper.isDarkModeEnabled())
-                .setDefaultValue(false)
-                .setSaveConsumer(bool -> configManager.getConfig().darkTheme = bool)
-                .setYesNoTextSupplier(bool -> I18n.translate("text.rei.config.appearance_theme." + bool))
-                .setTooltip(getConfigTooltip("appearance_theme"))
-                .build());
-        appearance.addEntry(eb.startEnumSelector("text.rei.config.recipe_screen_type", RecipeScreenType.class, configManager.getConfig().screenType)
-                .setDefaultValue(RecipeScreenType.UNSET)
-                .setSaveConsumer(bool -> configManager.getConfig().screenType = (RecipeScreenType) bool)
-                .setTooltip(getConfigTooltip("recipe_screen_type"))
-                .build());
-        appearance.addEntry(eb.startBooleanToggle("text.rei.config.side_search_box", configManager.getConfig().sideSearchField)
-                .setDefaultValue(false)
-                .setYesNoTextSupplier(bool -> I18n.translate("text.rei.config.side_search_box.text." + bool))
-                .setSaveConsumer(bool -> configManager.getConfig().sideSearchField = bool)
-                .setTooltip(getConfigTooltip("side_search_box"))
-                .build());
-        appearance.addEntry(eb.startEnumSelector("text.rei.config.list_ordering", ItemListOrderingConfig.class, ItemListOrderingConfig.from(configManager.getConfig().itemListOrdering, configManager.getConfig().isAscending))
-                .setDefaultValue(ItemListOrderingConfig.REGISTRY_ASCENDING)
-                .setSaveConsumer(config -> {
-                    configManager.getConfig().itemListOrdering = ((ItemListOrderingConfig) config).getOrdering();
-                    configManager.getConfig().isAscending = ((ItemListOrderingConfig) config).isAscending();
-                })
-                .setTooltip(getConfigTooltip("list_ordering", ItemListOrderingConfig.REGISTRY_ASCENDING.toString()))
-                .build());
-        appearance.addEntry(eb.startBooleanToggle("text.rei.config.item_list_position", configManager.getConfig().mirrorItemPanel)
-                .setDefaultValue(false)
-                .setYesNoTextSupplier(bool -> I18n.translate(bool ? "text.rei.config.item_list_position.left" : "text.rei.config.item_list_position.right"))
-                .setSaveConsumer(bool -> configManager.getConfig().mirrorItemPanel = bool)
-                .setTooltip(getConfigTooltip("item_list_position"))
-                .build());
-        appearance.addEntry(eb.startIntSlider("text.rei.config.max_recipes_per_page", configManager.getConfig().maxRecipePerPage, 2, 99)
-                .setDefaultValue(3)
-                .setSaveConsumer(i -> configManager.getConfig().maxRecipePerPage = i)
-                .setTooltip(getConfigTooltip("max_recipes_per_page"))
-                .build());
-        appearance.addEntry(eb.startBooleanToggle("text.rei.config.light_gray_recipe_border", configManager.getConfig().lightGrayRecipeBorder)
-                .setDefaultValue(false)
-                .setYesNoTextSupplier(bool -> I18n.translate("text.rei.config.light_gray_recipe_border.text." + bool))
-                .setSaveConsumer(bool -> configManager.getConfig().lightGrayRecipeBorder = bool)
-                .setTooltip(getConfigTooltip("light_gray_recipe_border"))
-                .build());
-        appearance.addEntry(eb.startBooleanToggle("text.rei.config.villager_screen_permanent_scroll_bar", configManager.getConfig().villagerScreenPermanentScrollBar)
-                .setYesNoTextSupplier(bool -> I18n.translate("text.rei.config.villager_screen_permanent_scroll_bar.text." + bool))
-                .setDefaultValue(false)
-                .setSaveConsumer(bool -> configManager.getConfig().villagerScreenPermanentScrollBar = bool)
-                .setTooltip(getConfigTooltip("villager_screen_permanent_scroll_bar"))
-                .build());
-        
-        ConfigCategory action = builder.getOrCreateCategory("text.rei.config.action");
-        action.addEntry(eb.startEnumSelector("text.rei.config.item_cheating_mode", ItemCheatingMode.class, configManager.getConfig().itemCheatingMode)
-                .setDefaultValue(ItemCheatingMode.REI_LIKE)
-                .setSaveConsumer(i -> configManager.getConfig().itemCheatingMode = (i instanceof ItemCheatingMode) ? (ItemCheatingMode) i : ItemCheatingMode.REI_LIKE)
-                .setEnumNameProvider(e -> I18n.translate("text.rei.config.item_cheating_mode." + ((ItemCheatingMode) e).name().toLowerCase(Locale.ROOT)))
-                .setTooltip(getConfigTooltip("item_cheating_mode"))
-                .build());
-        action.addEntry(eb.startStrField("text.rei.give_command", configManager.getConfig().giveCommand)
-                .setDefaultValue("/give {player_name} {item_identifier}{nbt} {count}")
-                .setSaveConsumer(s -> configManager.getConfig().giveCommand = s)
-                .setTooltip(getConfigTooltip("give_command"))
-                .build());
-        action.addEntry(eb.startStrField("text.rei.gamemode_command", configManager.getConfig().gamemodeCommand)
-                .setDefaultValue("/gamemode {gamemode}")
-                .setSaveConsumer(s -> configManager.getConfig().gamemodeCommand = s)
-                .setTooltip(getConfigTooltip("gamemode_command"))
-                .build());
-        action.addEntry(eb.startStrField("text.rei.weather_command", configManager.getConfig().weatherCommand)
-                .setDefaultValue("/weather {weather}")
-                .setSaveConsumer(s -> configManager.getConfig().weatherCommand = s)
-                .setTooltip(getConfigTooltip("weather_command"))
-                .build());
-        action.addEntry(eb.startBooleanToggle("text.rei.config.clickable_recipe_arrows", configManager.getConfig().clickableRecipeArrows)
-                .setDefaultValue(true)
-                .setYesNoTextSupplier(bool -> I18n.translate("text.rei.config.text." + bool))
-                .setSaveConsumer(bool -> configManager.getConfig().clickableRecipeArrows = bool)
-                .setTooltip(getConfigTooltip("clickable_recipe_arrows"))
-                .build());
-        ConfigCategory modules = builder.getOrCreateCategory("text.rei.config.modules");
-        modules.addEntry(eb.startBooleanToggle("text.rei.config.craftable_only", configManager.getConfig().enableCraftableOnlyButton)
-                .setDefaultValue(false)
-                .setSaveConsumer(bool -> configManager.getConfig().enableCraftableOnlyButton = bool)
-                .setYesNoTextSupplier(bool -> I18n.translate("text.rei.config.text." + bool))
-                .setTooltip(getConfigTooltip("craftable_only"))
-                .build());
-        modules.addEntry(eb.startBooleanToggle("text.rei.config.util_buttons", configManager.getConfig().showUtilsButtons)
-                .setDefaultValue(false)
-                .setSaveConsumer(bool -> configManager.getConfig().showUtilsButtons = bool)
-                .setYesNoTextSupplier(bool -> I18n.translate("text.rei.config.text." + bool))
-                .setTooltip(getConfigTooltip("util_buttons"))
-                .build());
-        modules.addEntry(eb.startBooleanToggle("text.rei.config.vanilla_recipe_book", configManager.getConfig().disableRecipeBook)
-                .setDefaultValue(false)
-                .setSaveConsumer(bool -> configManager.getConfig().disableRecipeBook = bool)
-                .setYesNoTextSupplier(bool -> I18n.translate("text.rei.config.text." + !bool))
-                .setTooltip(getConfigTooltip("vanilla_recipe_book"))
-                .build());
-        return builder.setAfterInitConsumer(screen -> {
-            ButtonWidget w = new ButtonWidget(6, 6, 60, 20, I18n.translate("text.rei.credits"), widget -> MinecraftClient.getInstance().openScreen(new CreditsScreen(MinecraftClient.getInstance().currentScreen)));
-            ((ScreenHooks) screen).cloth_getButtonWidgets().add(0, w);
-            ((ScreenHooks) screen).cloth_getChildren().add(0, w);
-        }).build();
-    }
-    
-    private static Optional<String[]> getConfigTooltip(String s, Object... o) {
-        if (I18n.hasTranslation("tooltip.rei.config." + s))
-            return Optional.ofNullable(I18n.translate("tooltip.rei.config." + s, o).split("\n"));
-        return Optional.empty();
+        }).registerConfigEntryFunction(ItemListOrderingConfig.class, o -> {
+            ConfigValue<ItemListOrderingConfig> configValue = (ConfigValue<ItemListOrderingConfig>) o;
+            return configEntryBuilder.startEnumSelector("config.roughlyenoughitems." + configValue.getName(), ItemListOrderingConfig.class, configValue.getValue())
+                    .setDefaultValue(configValue.getDefaultValue())
+                    .setTooltip(splitLine(configValue.getComment()))
+                    .setSaveConsumer(var -> configValue.setValue((ItemListOrderingConfig) var))
+                    .setErrorSupplier(var -> error((List) configValue.getConstraints(), var, ItemListOrderingConfig.class))
+                    .build();
+        }).registerConfigEntryFunction(RecipeScreenType.class, o -> {
+            ConfigValue<RecipeScreenType> configValue = (ConfigValue<RecipeScreenType>) o;
+            return configEntryBuilder.startEnumSelector("config.roughlyenoughitems." + configValue.getName(), RecipeScreenType.class, configValue.getValue())
+                    .setDefaultValue(configValue.getDefaultValue())
+                    .setTooltip(splitLine(configValue.getComment()))
+                    .setSaveConsumer(var -> configValue.setValue((RecipeScreenType) var))
+                    .setErrorSupplier(var -> error((List) configValue.getConstraints(), var, RecipeScreenType.class))
+                    .build();
+        }).registerConfigEntryFunction(ItemCheatingMode.class, o -> {
+            ConfigValue<ItemCheatingMode> configValue = (ConfigValue<ItemCheatingMode>) o;
+            return configEntryBuilder.startEnumSelector("config.roughlyenoughitems." + configValue.getName(), ItemCheatingMode.class, configValue.getValue())
+                    .setDefaultValue(configValue.getDefaultValue())
+                    .setTooltip(splitLine(configValue.getComment()))
+                    .setSaveConsumer(var -> configValue.setValue((ItemCheatingMode) var))
+                    .setErrorSupplier(var -> error((List) configValue.getConstraints(), var, ItemCheatingMode.class))
+                    .build();
+        }).build().getScreen();
     }
     
 }

+ 52 - 43
src/main/resources/assets/roughlyenoughitems/lang/en_us.json

@@ -33,14 +33,8 @@
   "text.rei.composting.page": "Page %d",
   "text.rei.config": "Config",
   "text.rei.config_tooltip": "Open Config Screen\n§7Shift-Click to toggle cheat mode",
-  "text.rei.config.side_search_box": "Search Box Position: ",
-  "text.rei.config.item_list_position": "Item List Position: ",
-  "text.rei.config.item_list_position.left": "Left",
-  "text.rei.config.item_list_position.right": "Right",
   "text.rei.cheat_items": "Gave [{item_name}§f] x{item_count} to {player_name}.",
   "text.rei.failed_cheat_items": "§cFailed to give items.",
-  "text.rei.config.list_ordering": "Item List Ordering:",
-  "text.rei.config.list_ordering_button": "%s [%s]",
   "ordering.rei.ascending": "Ascending",
   "ordering.rei.descending": "Descending",
   "ordering.rei.registry": "Registry",
@@ -51,16 +45,10 @@
   "error.rei.not.on.server": "REI is not on the server.",
   "error.rei.not.enough.materials": "Not Enough Materials.",
   "rei.rei.no.slot.in.inv": "Can't find any space for item in the inventory",
-  "text.rei.config.craftable_only": "Craftable Filter: ",
-  "text.rei.config.clickable_recipe_arrows": "Clickable Recipe Arrows: ",
-  "text.rei.config.text.true": "Enabled",
-  "text.rei.config.text.false": "Disabled",
   "text.rei.showing_craftable": "Showing Craftable",
   "text.rei.showing_all": "Showing All",
   "text.rei.delete_items": "§cDelete Item",
   "text.rei.check_updates": "Check Updates: ",
-  "text.rei.config.load_default_plugin": "Load Default Plugin: ",
-  "text.rei.config.load_default_plugin.restart_tooltip": "You probably never want to disable this.\nRestart Minecraft to apply this setting.",
   "text.rei.credits": "Credits",
   "text.rei.left_arrow": "<",
   "text.rei.right_arrow": ">",
@@ -71,13 +59,7 @@
   "text.rei.give_command.suggestion": "Enter command.",
   "text.rei.view_all_categories": "View All Categories",
   "text.rei.go_back_first_page": "Back to Page 1",
-  "text.rei.config.appearance": "Appearance",
-  "text.rei.config.modules": "Modules",
-  "text.rei.config.advanced": "Advanced",
-  "text.rei.config.vanilla_recipe_book": "Vanilla Recipe Book:",
   "text.rei.choose_page": "Choose Page",
-  "text.rei.config.max_recipes_per_page": "Maximum Recipes Each Page:",
-  "text.rei.config.util_buttons": "Utils Buttons:",
   "text.rei.gamemode_button.tooltip": "Switch Game Mode\n§7Switch to %s mode.\n\n§7Shift-Click to switch in a reverse cycle.",
   "text.rei.weather_button.tooltip": "Switch Weather\n§7Switch to %s.",
   "text.rei.enabled": "Yes",
@@ -93,43 +75,70 @@
   "text.rei.next_category": "Next Category",
   "text.rei.previous_page": "Previous Page",
   "text.rei.next_page": "Next Page",
-  "text.rei.config.prefer_visible_recipes": "Prefer Visible Recipes: ",
-  "text.rei.config.title": "Roughly Enough Items Config",
-  "text.rei.config.enable_legacy_speedcraft_support": "Enable Legacy Plugin Support: ",
-  "text.rei.config.april_fools": "April Fools",
-  "text.rei.config.april_fools.2019": "Force 2019 REI April Fools' joke: ",
-  "text.rei.config.appearance_theme": "Appearance Theme:",
-  "text.rei.config.appearance_theme.true": "Dark Theme",
-  "text.rei.config.appearance_theme.false": "Light Theme",
-  "text.rei.config.villager_screen_permanent_scroll_bar": "Recipe Screen Scroll Bar:",
-  "text.rei.config.item_cheating_mode": "Item Cheating Amount:",
-  "text.rei.config.item_cheating_mode.rei_like": "Normal",
-  "text.rei.config.item_cheating_mode.jei_like": "Reversed",
-  "text.rei.config.light_gray_recipe_border": "Recipe Display Border:",
-  "text.rei.config_api_failed": "You arrived here either if Cloth Config v2 API failed or you don't have it installed!\nUpdate / Install the API and report to the bug tracker.",
   "text.rei.back": "Back",
-  "text.rei.config.recipe_screen_type": "Screen Type:",
-  "text.rei.config.recipe_screen_type.unset": "Not Set",
-  "text.rei.config.recipe_screen_type.original": "Original",
-  "text.rei.config.recipe_screen_type.villager": "Villager",
   "text.rei.select": "Select",
   "text.rei.working_station": "Working Station",
   "text.rei.recipe_id": "\n%sRecipe Id: %s",
-  "text.rei.config.register_in_other_thread": "Register Recipes in other thread:",
   "text.rei.recipe_screen_type.selection": "Recipe Screen Type Selection",
   "text.rei.recipe_screen_type.selection.sub": "You can always edit this setting again via the config screen.",
   "text.rei.view_recipes_for": "View Recipes for %s",
-  "text.rei.config.side_search_box.text.false": "Center",
-  "text.rei.config.side_search_box.text.true": "Left / Right",
-  "text.rei.config.villager_screen_permanent_scroll_bar.text.true": "Permanent",
-  "text.rei.config.villager_screen_permanent_scroll_bar.text.false": "Auto Fade",
-  "text.rei.config.light_gray_recipe_border.text.true": "Light Gray",
-  "text.rei.config.light_gray_recipe_border.text.false": "High Contrast",
   "_comment": "Config Tooltips",
   "tooltip.rei.config.side_search_box": "Declares the location of the search field",
   "tooltip.rei.config.list_ordering": "Declares the ordering of the side item list",
   "tooltip.rei.config.item_list_position": "Declares the position of the side item list",
   "tooltip.rei.config.max_recipes_per_page": "Declares the maximum possible displayed recipes",
   "tooltip.rei.config.light_gray_recipe_border": "Declares the appearance of the recipe border",
+  "config.roughlyenoughitems.title": "Roughly Enough Items Config",
+  "config.roughlyenoughitems.!general": "General",
+  "config.roughlyenoughitems.appearance": "Appearance",
+  "config.roughlyenoughitems.modules": "Modules",
+  "config.roughlyenoughitems.technical": "Technical",
+  "config.roughlyenoughitems.cheating": "Cheating:",
+  "config.roughlyenoughitems.clickableRecipeArrows": "Clickable Recipe Arrows:",
+  "config.roughlyenoughitems.clickableRecipeArrows.boolean.true": "Enabled",
+  "config.roughlyenoughitems.clickableRecipeArrows.boolean.false": "Disabled",
+  "config.roughlyenoughitems.darkTheme": "Appearance Theme:",
+  "config.roughlyenoughitems.darkTheme.boolean.true": "Dark Theme",
+  "config.roughlyenoughitems.darkTheme.boolean.false": "Light Theme",
+  "config.roughlyenoughitems.lightGrayRecipeBorder": "Recipe Display Border:",
+  "config.roughlyenoughitems.lightGrayRecipeBorder.boolean.true": "Light Gray",
+  "config.roughlyenoughitems.lightGrayRecipeBorder.boolean.false": "High Contrast",
+  "config.roughlyenoughitems.maxRecipePerPage": "Maximum Recipes Per Page:",
+  "config.roughlyenoughitems.mirrorItemPanel": "Item List Position:",
+  "config.roughlyenoughitems.mirrorItemPanel.boolean.true": "Left Side",
+  "config.roughlyenoughitems.mirrorItemPanel.boolean.false": "Right Side",
+  "config.roughlyenoughitems.sideSearchField": "Search Field Position:",
+  "config.roughlyenoughitems.sideSearchField.boolean.true": "Left / Right Side",
+  "config.roughlyenoughitems.sideSearchField.boolean.false": "Middle",
+  "config.roughlyenoughitems.villagerScreenPermanentScrollBar": "Villager Recipe Screen Scrollbar:",
+  "config.roughlyenoughitems.villagerScreenPermanentScrollBar.boolean.true": "Permanent",
+  "config.roughlyenoughitems.villagerScreenPermanentScrollBar.boolean.false": "Auto Fade Out",
+  "config.roughlyenoughitems.disableRecipeBook": "Vanilla Recipe Book:",
+  "config.roughlyenoughitems.disableRecipeBook.boolean.true": "Disabled",
+  "config.roughlyenoughitems.disableRecipeBook.boolean.false": "Enabled",
+  "config.roughlyenoughitems.enableCraftableOnlyButton": "Craftable Filter:",
+  "config.roughlyenoughitems.enableCraftableOnlyButton.boolean.true": "Enabled",
+  "config.roughlyenoughitems.enableCraftableOnlyButton.boolean.false": "Disabled",
+  "config.roughlyenoughitems.showUtilsButtons": "Utils Buttons:",
+  "config.roughlyenoughitems.showUtilsButtons.boolean.true": "Enabled",
+  "config.roughlyenoughitems.showUtilsButtons.boolean.false": "Disabled",
+  "config.roughlyenoughitems.gamemodeCommand": "Game Mode Command:",
+  "config.roughlyenoughitems.giveCommand": "Give Command:",
+  "config.roughlyenoughitems.loadDefaultPlugin": "Load Default Plugin:",
+  "config.roughlyenoughitems.loadDefaultPlugin.boolean.false": "§cNo (Dangerous)",
+  "config.roughlyenoughitems.registerRecipesInAnotherThread": "Recipe Sync Thread:",
+  "config.roughlyenoughitems.registerRecipesInAnotherThread.boolean.true": "New REI Thread",
+  "config.roughlyenoughitems.registerRecipesInAnotherThread.boolean.false": "§cPacket Thread",
+  "config.roughlyenoughitems.weatherCommand": "Weather Command:",
+  "config.roughlyenoughitems.itemListOrdering": "Item List Ordering:",
+  "config.roughlyenoughitems.list_ordering_button": "%s [%s]",
+  "config.roughlyenoughitems.recipeScreenType": "Recipe Screen Type:",
+  "config.roughlyenoughitems.recipeScreenType.unset": "Not Set",
+  "config.roughlyenoughitems.recipeScreenType.original": "Original",
+  "config.roughlyenoughitems.recipeScreenType.villager": "Villager Like",
+  "config.roughlyenoughitems.itemCheatingMode": "Item Cheating Amount:",
+  "config.roughlyenoughitems.itemCheatingMode.rei_like": "Normal",
+  "config.roughlyenoughitems.itemCheatingMode.jei_like": "Inverted",
+  "config.roughlyenoughitems.appendModNames": "Append Mod Names",
   "text.rei.credit.text": "§lRoughly Enough Items (v%s)\n§7Originally a fork for Almost Enough Items.\n\n§lDevelopers\n  Originally by ZenDarva\n  Rewritten by Danielshe\n  Old Plugin Support by TehNut\n\n§lLanguage Translation\n%s\n\n§lLicense\n§7Roughly Enough Items is licensed with MIT."
 }