From b4c362041ced74f226edfafdb287cd6416cc14fb Mon Sep 17 00:00:00 2001 From: Unknown Date: Sun, 4 Aug 2019 20:12:38 +0800 Subject: Better Config Screen --- gradle.properties | 2 +- .../java/me/shedaniel/rei/api/ItemRegistry.java | 8 +- .../java/me/shedaniel/rei/client/ConfigObject.java | 2 +- .../shedaniel/rei/utils/ClothScreenRegistry.java | 148 +++++++++++++++------ .../assets/roughlyenoughitems/lang/en_us.json | 32 +++-- 5 files changed, 128 insertions(+), 64 deletions(-) diff --git a/gradle.properties b/gradle.properties index c09f5e5ce..8db87ff32 100644 --- a/gradle.properties +++ b/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.3.1.23 -cloth_config_version=0.4.1 +cloth_config_version=0.5.0 modmenu_version=1.5.4-85 fabric_api=0.3.0+build.198 diff --git a/src/main/java/me/shedaniel/rei/api/ItemRegistry.java b/src/main/java/me/shedaniel/rei/api/ItemRegistry.java index d64bdd878..93b4510eb 100644 --- a/src/main/java/me/shedaniel/rei/api/ItemRegistry.java +++ b/src/main/java/me/shedaniel/rei/api/ItemRegistry.java @@ -50,9 +50,11 @@ public interface ItemRegistry { * @param stacks the stacks to register */ default void registerItemStack(Item afterItem, ItemStack... stacks) { - for(ItemStack stack : stacks) + for(int i = stacks.length - 1; i >= 0; i--) { + ItemStack stack = stacks[i]; if (stack != null && !stack.isEmpty()) registerItemStack(afterItem, stack); + } } /** @@ -61,9 +63,7 @@ public interface ItemRegistry { * @param stacks the stacks to register */ default void registerItemStack(ItemStack... stacks) { - for(ItemStack stack : stacks) - if (stack != null && !stack.isEmpty()) - registerItemStack(null, stack); + registerItemStack(null, stacks); } /** diff --git a/src/main/java/me/shedaniel/rei/client/ConfigObject.java b/src/main/java/me/shedaniel/rei/client/ConfigObject.java index 24f697471..eadc9532a 100644 --- a/src/main/java/me/shedaniel/rei/client/ConfigObject.java +++ b/src/main/java/me/shedaniel/rei/client/ConfigObject.java @@ -20,7 +20,7 @@ public class ConfigObject { @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 = true; + public boolean enableCraftableOnlyButton = false; @Comment("True: search field will be on the side (left / right), false: in the middle") public boolean sideSearchField = false; diff --git a/src/main/java/me/shedaniel/rei/utils/ClothScreenRegistry.java b/src/main/java/me/shedaniel/rei/utils/ClothScreenRegistry.java index e3f58b089..3453b0480 100644 --- a/src/main/java/me/shedaniel/rei/utils/ClothScreenRegistry.java +++ b/src/main/java/me/shedaniel/rei/utils/ClothScreenRegistry.java @@ -9,10 +9,6 @@ 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.clothconfig2.gui.entries.BooleanListEntry; -import me.shedaniel.clothconfig2.gui.entries.EnumListEntry; -import me.shedaniel.clothconfig2.gui.entries.IntegerSliderEntry; -import me.shedaniel.clothconfig2.gui.entries.StringListEntry; import me.shedaniel.rei.RoughlyEnoughItemsCore; import me.shedaniel.rei.api.ConfigManager; import me.shedaniel.rei.client.ScreenHelper; @@ -30,9 +26,9 @@ import java.util.Locale; import java.util.Optional; 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(); @@ -45,56 +41,120 @@ public class ClothScreenRegistry { }); ConfigEntryBuilder eb = ConfigEntryBuilder.create(); ConfigCategory general = builder.getOrCreateCategory("text.rei.config.general"); - general.addEntry(new BooleanListEntry("text.rei.config.cheating", configManager.getConfig().cheating, RESET, () -> false, bool -> configManager.getConfig().cheating = bool) { - @Override - public Optional getTooltip() { - String s = null; - if (!getValue()) - 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}); - } - }); + 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.dark_theme", ScreenHelper.isDarkModeEnabled()).setDefaultValue(() -> false).setSaveConsumer(bool -> configManager.getConfig().darkTheme = bool).setTooltipSupplier(() -> getConfigTooltip("dark_theme")).buildEntry()); - 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).setTooltipSupplier(() -> getConfigTooltip("recipe_screen_type")).buildEntry()); - appearance.addEntry(eb.startBooleanToggle("text.rei.config.side_search_box", configManager.getConfig().sideSearchField).setDefaultValue(() -> false).setSaveConsumer(bool -> configManager.getConfig().sideSearchField = bool).setTooltipSupplier(() -> getConfigTooltip("side_search_box")).buildEntry()); - 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(); - }).setTooltipSupplier(() -> getConfigTooltip("list_ordering", ItemListOrderingConfig.REGISTRY_ASCENDING.toString())).buildEntry()); - 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).buildEntry()); - appearance.addEntry(new IntegerSliderEntry("text.rei.config.max_recipes_per_page", 2, 99, configManager.getConfig().maxRecipePerPage, RESET, () -> 3, i -> configManager.getConfig().maxRecipePerPage = i, () -> getConfigTooltip("max_recipes_per_page"))); - appearance.addEntry(new BooleanListEntry("text.rei.config.light_gray_recipe_border", configManager.getConfig().lightGrayRecipeBorder, RESET, () -> false, bool -> configManager.getConfig().lightGrayRecipeBorder = bool, () -> getConfigTooltip("light_gray_recipe_border"))); - appearance.addEntry(new BooleanListEntry("text.rei.config.villager_screen_permanent_scroll_bar", configManager.getConfig().villagerScreenPermanentScrollBar, RESET, () -> false, bool -> configManager.getConfig().villagerScreenPermanentScrollBar = bool, () -> getConfigTooltip("villager_screen_permanent_scroll_bar"))); + appearance.addEntry(eb.startBooleanToggle("text.rei.config.dark_theme", ScreenHelper.isDarkModeEnabled()) + .setDefaultValue(false) + .setSaveConsumer(bool -> configManager.getConfig().darkTheme = bool) + .setTooltip(getConfigTooltip("dark_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(new EnumListEntry<>("text.rei.config.item_cheating_mode", ItemCheatingMode.class, configManager.getConfig().itemCheatingMode, RESET, () -> ItemCheatingMode.REI_LIKE, i -> configManager.getConfig().itemCheatingMode = i, e -> { - return I18n.translate("text.rei.config.item_cheating_mode." + e.name().toLowerCase(Locale.ROOT)); - }, () -> getConfigTooltip("item_cheating_mode"))); - action.addEntry(new StringListEntry("text.rei.give_command", configManager.getConfig().giveCommand, RESET, () -> "/give {player_name} {item_identifier}{nbt} {count}", s -> configManager.getConfig().giveCommand = s, () -> getConfigTooltip("give_command"))); - action.addEntry(new StringListEntry("text.rei.gamemode_command", configManager.getConfig().gamemodeCommand, RESET, () -> "/gamemode {gamemode}", s -> configManager.getConfig().gamemodeCommand = s, () -> getConfigTooltip("gamemode_command"))); - action.addEntry(new StringListEntry("text.rei.weather_command", configManager.getConfig().weatherCommand, RESET, () -> "/weather {weather}", s -> configManager.getConfig().weatherCommand = s, () -> getConfigTooltip("weather_command"))); - action.addEntry(new BooleanListEntry("text.rei.config.register_in_other_thread", configManager.getConfig().registerRecipesInAnotherThread, RESET, () -> true, bool -> configManager.getConfig().registerRecipesInAnotherThread = bool, () -> getConfigTooltip("register_in_other_thread"))); + 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()); ConfigCategory modules = builder.getOrCreateCategory("text.rei.config.modules"); - modules.addEntry(new BooleanListEntry("text.rei.config.enable_craftable_only", configManager.getConfig().enableCraftableOnlyButton, RESET, () -> true, bool -> configManager.getConfig().enableCraftableOnlyButton = bool, () -> getConfigTooltip("enable_craftable_only"))); - modules.addEntry(new BooleanListEntry("text.rei.config.enable_util_buttons", configManager.getConfig().showUtilsButtons, RESET, () -> false, bool -> configManager.getConfig().showUtilsButtons = bool, () -> getConfigTooltip("enable_util_buttons"))); - modules.addEntry(new BooleanListEntry("text.rei.config.disable_recipe_book", configManager.getConfig().disableRecipeBook, RESET, () -> false, bool -> configManager.getConfig().disableRecipeBook = bool, () -> getConfigTooltip("disable_recipe_book"))); + modules.addEntry(eb.startBooleanToggle("text.rei.config.enable_craftable_only", configManager.getConfig().enableCraftableOnlyButton) + .setDefaultValue(false) + .setSaveConsumer(bool -> configManager.getConfig().enableCraftableOnlyButton = bool) + .setTooltip(getConfigTooltip("enable_craftable_only")) + .build()); + modules.addEntry(eb.startBooleanToggle("text.rei.config.enable_util_buttons", configManager.getConfig().showUtilsButtons) + .setDefaultValue(false) + .setSaveConsumer(bool -> configManager.getConfig().showUtilsButtons = bool) + .setTooltip(getConfigTooltip("enable_util_buttons")) + .build()); + modules.addEntry(eb.startBooleanToggle("text.rei.config.disable_recipe_book", configManager.getConfig().disableRecipeBook) + .setDefaultValue(false) + .setSaveConsumer(bool -> configManager.getConfig().disableRecipeBook = bool) + .setTooltip(getConfigTooltip("disable_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 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(); } - + } diff --git a/src/main/resources/assets/roughlyenoughitems/lang/en_us.json b/src/main/resources/assets/roughlyenoughitems/lang/en_us.json index 8ba68087a..bf126452b 100755 --- a/src/main/resources/assets/roughlyenoughitems/lang/en_us.json +++ b/src/main/resources/assets/roughlyenoughitems/lang/en_us.json @@ -32,7 +32,7 @@ "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": "Side Search Box: ", + "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", @@ -58,7 +58,7 @@ "text.rei.left_arrow": "<", "text.rei.right_arrow": ">", "text.rei.give_command": "Cheat Give Command:", - "text.rei.gamemode_command": "GameMode Command:", + "text.rei.gamemode_command": "Game Mode Command:", "text.rei.weather_command": "Weather Command:", "text.rei.give_command.tooltip": "This command is only used in servers when cheating.", "text.rei.give_command.suggestion": "Enter command.", @@ -71,7 +71,7 @@ "text.rei.choose_page": "Choose Page", "text.rei.config.max_recipes_per_page": "Maximum Recipes Each Page:", "text.rei.config.enable_util_buttons": "Enable Utils Buttons:", - "text.rei.gamemode_button.tooltip": "Switch GameMode\n§7Switch to %s mode.\n\n§7Shift-Click to switch in a reverse cycle.", + "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", "text.rei.disabled": "No", @@ -92,14 +92,14 @@ "text.rei.config.april_fools": "April Fools", "text.rei.config.april_fools.2019": "Force 2019 REI April Fools' joke: ", "text.rei.config.dark_theme": "Dark Mode:", - "text.rei.config.villager_screen_permanent_scroll_bar": "Villager Screen Permanent Scroll Bar:", + "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": "Light Gray Recipe Border:", + "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": "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", @@ -110,13 +110,17 @@ "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:\nYes: Left / Right\nNo: Center\n \nDefaulted: No", - "tooltip.rei.config.list_ordering": "Declares the ordering of the side item list:\nValues: Registry / Name / Item Groups\n(Ascending / Descending)\n \nDefaulted: %s", - "tooltip.rei.config.item_list_position": "Declares the position of the side item list:\nValues: Left / Right\n \nDefaulted: Right", - "tooltip.rei.config.max_recipes_per_page": "Declares the maximum possible displayed recipes:\nValues: 2 - 99\n \nDefaulted: 3", - "tooltip.rei.config.light_gray_recipe_border": "Declares the appearance of the recipe border:\nYes: Light Gray\nNo: Hard Black\n \nDefaulted: No", - "tooltip.rei.config.april_fools.2019": "Forcefully enables the 2019 april fools joke:\nValues: Yes / No\n \nDefaulted: No", - "tooltip.rei.config.register_in_other_thread": "Requires datapacks to be reloaded.\nCan be done by rejoin the world / server", - "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 Continued by Danielshe\n Plugin Support by TehNut\n\n§lLanguage Translation\n%s\n\n§lLicense\n§7Roughly Enough Items is licensed with MIT." + "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", + "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." } -- cgit