From 1f75b2307afb1f40c52caecddc6cd694e940acd3 Mon Sep 17 00:00:00 2001 From: Sakura Ryoko Date: Wed, 11 Sep 2024 00:52:34 -0400 Subject: [PATCH 1/4] fix: craftRecipeBook feat: add itemSorting fixes from Snapshot --- build.gradle | 6 +- gradle.properties | 5 +- .../dy/masa/itemscroller/config/Configs.java | 21 ++- .../itemscroller/event/KeybindCallbacks.java | 23 ++- .../itemscroller/util/InventoryUtils.java | 127 ++++++++++++- .../itemscroller/util/SortingCategory.java | 172 ++++++++++++++++++ .../masa/itemscroller/util/SortingMethod.java | 84 +++++++++ .../assets/itemscroller/lang/en_us.json | 38 +++- .../assets/itemscroller/lang/es_es.json | 34 ++++ .../assets/itemscroller/lang/zh_cn.json | 34 ++++ .../assets/itemscroller/lang/zh_tw.json | 34 ++++ src/main/resources/fabric.mod.json | 2 +- 12 files changed, 564 insertions(+), 16 deletions(-) create mode 100644 src/main/java/fi/dy/masa/itemscroller/util/SortingCategory.java create mode 100644 src/main/java/fi/dy/masa/itemscroller/util/SortingMethod.java diff --git a/build.gradle b/build.gradle index 63ef5a4c5..763752216 100644 --- a/build.gradle +++ b/build.gradle @@ -9,7 +9,7 @@ repositories { maven { url 'https://masa.dy.fi/maven' } maven { url 'https://maven.terraformersmc.com/releases/' } // maven { url 'https://maven.quiltmc.org/repository/release/' } - // maven { url 'https://jitpack.io' } + maven { url 'https://jitpack.io' } } loom { @@ -22,8 +22,8 @@ dependencies { modImplementation "net.fabricmc:fabric-loader:${project.fabric_loader_version}" implementation "com.google.code.findbugs:jsr305:3.0.2" - modImplementation "fi.dy.masa.malilib:malilib-fabric-${project.minecraft_version_out}:${project.malilib_version}" - //modImplementation "com.github.sakura-ryoko:malilib:${project.malilib_id}" + //modImplementation "fi.dy.masa.malilib:malilib-fabric-${project.minecraft_version_out}:${project.malilib_version}" + modImplementation "com.github.sakura-ryoko:malilib:${project.malilib_id}" // Fabric API. This is technically optional, but you probably want it anyway. //modCompile "net.fabricmc.fabric-api:fabric-api:" + project.fabric_version diff --git a/gradle.properties b/gradle.properties index b19c839a8..af49b590c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,10 +9,11 @@ author = masa mod_file_name = itemscroller-fabric # Current mod version -mod_version = 0.24.0 +mod_version = 0.24.1-sakura.1 # Required malilib version -malilib_version = 0.20.0 +malilib_version = 0.20.3-sakura.1 +malilib_id = 51615eb976 # Minecraft, Fabric Loader and API and mappings versions minecraft_version_out = 1.21 diff --git a/src/main/java/fi/dy/masa/itemscroller/config/Configs.java b/src/main/java/fi/dy/masa/itemscroller/config/Configs.java index 65b878409..e4bac14c5 100644 --- a/src/main/java/fi/dy/masa/itemscroller/config/Configs.java +++ b/src/main/java/fi/dy/masa/itemscroller/config/Configs.java @@ -11,6 +11,7 @@ import net.minecraft.client.gui.screen.ingame.InventoryScreen; import net.minecraft.screen.slot.CraftingResultSlot; import fi.dy.masa.malilib.config.ConfigUtils; +import fi.dy.masa.malilib.config.IConfigBase; import fi.dy.masa.malilib.config.IConfigHandler; import fi.dy.masa.malilib.config.IConfigValue; import fi.dy.masa.malilib.config.options.*; @@ -19,11 +20,16 @@ import fi.dy.masa.itemscroller.Reference; import fi.dy.masa.itemscroller.recipes.CraftingHandler; import fi.dy.masa.itemscroller.recipes.CraftingHandler.SlotRange; +import fi.dy.masa.itemscroller.util.SortingCategory; +import fi.dy.masa.itemscroller.util.SortingMethod; public class Configs implements IConfigHandler { private static final String CONFIG_FILE_NAME = Reference.MOD_ID + ".json"; + private static final ImmutableList DEFAULT_TOP_SORTING = ImmutableList.of("minecraft:diamond_sword","minecraft:diamond_pickaxe","minecraft:diamond_axe","minecraft:diamond_shovel","minecraft:diamond_hoe","minecraft:netherite_sword","minecraft:netherite_pickaxe","minecraft:netherite_axe","minecraft:netherite_shovel","minecraft:netherite_hoe"); + private static final ImmutableList DEFAULT_BOTTOM_SORTING = ImmutableList.of(); + public static class Generic { public static final ConfigBoolean CARPET_CTRL_Q_CRAFTING = new ConfigBoolean("carpetCtrlQCraftingEnabledOnServer", false, "itemscroller.config.generic.comment.carpetCtrlQCraftingEnabledOnServer").translatedName("itemscroller.config.generic.name.carpetCtrlQCraftingEnabledOnServer"); @@ -46,10 +52,16 @@ public static class Generic public static final ConfigBoolean SLOT_POSITION_AWARE_SCROLL_DIRECTION = new ConfigBoolean("useSlotPositionAwareScrollDirection", false, "itemscroller.config.generic.comment.useSlotPositionAwareScrollDirection").translatedName("itemscroller.config.generic.name.useSlotPositionAwareScrollDirection"); public static final ConfigBoolean VILLAGER_TRADE_USE_GLOBAL_FAVORITES = new ConfigBoolean("villagerTradeUseGlobalFavorites", true, "itemscroller.config.generic.comment.villagerTradeUseGlobalFavorites").translatedName("itemscroller.config.generic.name.villagerTradeUseGlobalFavorites"); public static final ConfigBoolean VILLAGER_TRADE_LIST_REMEMBER_SCROLL = new ConfigBoolean("villagerTradeListRememberScrollPosition", true, "itemscroller.config.generic.comment.villagerTradeListRememberScrollPosition").translatedName("itemscroller.config.generic.name.villagerTradeListRememberScrollPosition"); + + public static final ConfigBoolean SORT_INVENTORY_TOGGLE = new ConfigBoolean("sortInventoryToggle", false, "itemscroller.config.generic.comment.sortInventoryToggle").translatedName("itemscroller.config.generic.name.sortInventoryToggle"); public static final ConfigBoolean SORT_ASSUME_EMPTY_BOX_STACKS = new ConfigBoolean("sortAssumeEmptyBoxStacks", true, "itemscroller.config.generic.comment.sortAssumeEmptyBoxStacks").translatedName("itemscroller.config.generic.name.sortAssumeEmptyBoxStacks"); public static final ConfigBoolean SORT_SHULKER_BOXES_AT_END = new ConfigBoolean("sortShulkerBoxesAtEnd", true, "itemscroller.config.generic.comment.sortShulkerBoxesAtEnd").translatedName("itemscroller.config.generic.name.sortShulkerBoxesAtEnd"); + public static final ConfigStringList SORT_TOP_PRIORITY_INVENTORY = new ConfigStringList("sortTopPriorityInventory", DEFAULT_TOP_SORTING, "itemscroller.config.generic.comment.sortTopPriorityInventory").translatedName("itemscroller.config.generic.name.sortTopPriorityInventory"); + public static final ConfigStringList SORT_BOTTOM_PRIORITY_INVENTORY = new ConfigStringList("sortBottomPriorityInventory", DEFAULT_BOTTOM_SORTING, "itemscroller.config.generic.comment.sortBottomPriorityInventory").translatedName("itemscroller.config.generic.name.sortBottomPriorityInventory"); + public static final ConfigOptionList SORT_METHOD_DEFAULT = new ConfigOptionList("sortMethodDefault", SortingMethod.CATEGORY_NAME, "itemscroller.config.generic.comment.sortMethodDefault").translatedName("itemscroller.config.generic.name.sortMethodDefault"); + public static final ConfigLockedList SORT_CATEGORY_ORDER = new ConfigLockedList("sortCategoryOrder", SortingCategory.INSTANCE, "itemscroller.config.generic.comment.sortCategoryOrder").translatedName("itemscroller.config.generic.name.sortCategoryOrder"); - public static final ImmutableList OPTIONS = ImmutableList.of( + public static final ImmutableList OPTIONS = ImmutableList.of( CARPET_CTRL_Q_CRAFTING, CLIENT_CRAFTING_FIX, CRAFTING_RENDER_RECIPE_ITEMS, @@ -71,8 +83,13 @@ public static class Generic VILLAGER_TRADE_USE_GLOBAL_FAVORITES, VILLAGER_TRADE_LIST_REMEMBER_SCROLL, + SORT_INVENTORY_TOGGLE, SORT_ASSUME_EMPTY_BOX_STACKS, - SORT_SHULKER_BOXES_AT_END + SORT_SHULKER_BOXES_AT_END, + SORT_TOP_PRIORITY_INVENTORY, + SORT_BOTTOM_PRIORITY_INVENTORY, + SORT_METHOD_DEFAULT, + SORT_CATEGORY_ORDER ); } diff --git a/src/main/java/fi/dy/masa/itemscroller/event/KeybindCallbacks.java b/src/main/java/fi/dy/masa/itemscroller/event/KeybindCallbacks.java index 8358a9793..ffa338582 100644 --- a/src/main/java/fi/dy/masa/itemscroller/event/KeybindCallbacks.java +++ b/src/main/java/fi/dy/masa/itemscroller/event/KeybindCallbacks.java @@ -172,8 +172,11 @@ else if (key == Hotkeys.SLOT_DEBUG.getKeybind()) } else if (key == Hotkeys.SORT_INVENTORY.getKeybind()) { - InventoryUtils.sortInventory(gui); - return true; + if (Configs.Generic.SORT_INVENTORY_TOGGLE.getBooleanValue()) + { + InventoryUtils.sortInventory(gui); + return true; + } } return false; @@ -239,9 +242,9 @@ public void onClientTick(MinecraftClient mc) for (int i = 0; i < limit; ++i) { // todo -// InventoryUtils.tryClearCursor(gui); -// InventoryUtils.setInhibitCraftingOutputUpdate(true); - InventoryUtils.throwAllCraftingResultsToGround(recipe, gui); + //InventoryUtils.setInhibitCraftingOutputUpdate(true); + //InventoryUtils.tryClearCursor(gui); + //InventoryUtils.throwAllCraftingResultsToGround(recipe, gui); RecipeInputInventory craftingInv = ((IMixinCraftingResultSlot) outputSlot).itemscroller_getCraftingInventory(); if (!recipe.getVanillaRecipe().matches(craftingInv.createRecipeInput(), mc.world)) @@ -274,8 +277,18 @@ public void onClientTick(MinecraftClient mc) } InventoryUtils.shiftClickSlot(gui, outputSlot.id); + + // This isn't required after 1.21, it only needs a single dropStack + for (int k = 0; k < recipe.getResult().getMaxCount(); k++) + { + InventoryUtils.dropStack(gui, outputSlot.id); + } + recipeBookClicks = true; } + + InventoryUtils.tryClearCursor(gui); + InventoryUtils.throwAllCraftingResultsToGround(recipe, gui); } else if (Configs.Generic.MASS_CRAFT_SWAPS.getBooleanValue()) { diff --git a/src/main/java/fi/dy/masa/itemscroller/util/InventoryUtils.java b/src/main/java/fi/dy/masa/itemscroller/util/InventoryUtils.java index 8b7906c11..51b20b18e 100644 --- a/src/main/java/fi/dy/masa/itemscroller/util/InventoryUtils.java +++ b/src/main/java/fi/dy/masa/itemscroller/util/InventoryUtils.java @@ -26,6 +26,7 @@ import net.minecraft.inventory.Inventory; import net.minecraft.inventory.RecipeInputInventory; import net.minecraft.item.BlockItem; +import net.minecraft.item.ItemGroup; import net.minecraft.item.ItemStack; import net.minecraft.network.listener.ClientPlayPacketListener; import net.minecraft.network.packet.Packet; @@ -77,8 +78,11 @@ public class InventoryUtils private static boolean inhibitCraftResultUpdate; private static Runnable selectedSlotUpdateTask; public static boolean assumeEmptyShulkerStacking = false; + private static List topSortingPriorityList = Configs.Generic.SORT_TOP_PRIORITY_INVENTORY.getStrings(); + private static List bottomSortingPriorityList = Configs.Generic.SORT_BOTTOM_PRIORITY_INVENTORY.getStrings(); public static boolean bufferInvUpdates = false; public static List> invUpdatesBuffer = new ArrayList<>(); + private static ItemGroup.DisplayContext displayContext; public static void setInhibitCraftingOutputUpdate(boolean inhibitUpdate) { @@ -2739,6 +2743,16 @@ private static void quickSort(HandledScreen gui, int start, int end) private static int compareStacks(ItemStack stack1, ItemStack stack2) { + // Check if they have a custom priority + int priority1 = getCustomPriority(stack1); + int priority2 = getCustomPriority(stack2); + + if (priority1 != -1 || priority2 != -1) + { + // Any of both has a priority, compare them using that + return Integer.compare(priority2, priority1); + } + if (Configs.Generic.SORT_SHULKER_BOXES_AT_END.getBooleanValue()) { if (isShulkerBox(stack1) && !isShulkerBox(stack2)) @@ -2766,17 +2780,128 @@ else if (stack2.isEmpty()) return Integer.compare(contents1.size(), contents2.size()); } } + SortingMethod method = (SortingMethod) Configs.Generic.SORT_METHOD_DEFAULT.getOptionListValue(); + + if (method.equals(SortingMethod.CATEGORY_NAME) || + method.equals(SortingMethod.CATEGORY_COUNT) || + method.equals(SortingMethod.CATEGORY_RARITY) || + method.equals(SortingMethod.CATEGORY_RAWID)) + { + // Sort by Catagory + MinecraftClient mc = MinecraftClient.getInstance(); + + if (mc.world != null) + { + if (displayContext == null) + { + displayContext = SortingCategory.INSTANCE.buildDisplayContext(mc); + // This isn't used here, but it is required to build the list of items, as if we are opening the Creative Inventory Screen. + } + + SortingCategory.Entry cat1 = SortingCategory.INSTANCE.fromItemStack(stack1); + SortingCategory.Entry cat2 = SortingCategory.INSTANCE.fromItemStack(stack2); + + if (cat1.getStringValue().equals(cat2.getStringValue()) == false) + { + int index1 = Configs.Generic.SORT_CATEGORY_ORDER.getEntryIndex(cat1); + int index2 = Configs.Generic.SORT_CATEGORY_ORDER.getEntryIndex(cat2); + + //ItemScroller.logger.error("SORT - Category1[{}]: [{}] // Category2[{}]: [{}]", index1, cat1.getDisplayName(), index2, cat2.getDisplayName()); + + if (index1 < 0) + { + return 1; + } + else if (index2 < 0) + { + return -1; + } + + return Integer.compare(index1, index2); + } + } + } + if (stack1.getItem() != stack2.getItem()) { - return Integer.compare(Registries.ITEM.getRawId(stack1.getItem()), Registries.ITEM.getRawId(stack2.getItem())); + if (method.equals(SortingMethod.CATEGORY_NAME) || method.equals(SortingMethod.ITEM_NAME)) + { + // Sort by Item Name + return stack1.getName().getString().compareTo(stack2.getName().getString()) >= 0 ? 1 : -1; + } + else if (method.equals(SortingMethod.CATEGORY_COUNT) || method.equals(SortingMethod.ITEM_COUNT)) + { + // Sort by Item Count + int result = Integer.compare(-stack1.getCount(), -stack2.getCount()); + return result == 0 ? Integer.compare(Registries.ITEM.getRawId(stack1.getItem()), Registries.ITEM.getRawId(stack2.getItem())) : result; + } + else if (method.equals(SortingMethod.CATEGORY_RARITY) || method.equals(SortingMethod.ITEM_RARITY)) + { + // Sort by Item Rarity + int result = stack1.getRarity().compareTo(stack2.getRarity()); + return result == 0 ? Integer.compare(Registries.ITEM.getRawId(stack1.getItem()), Registries.ITEM.getRawId(stack2.getItem())) : result; + } + else + { + // Sort by Item RawID + return Integer.compare(Registries.ITEM.getRawId(stack1.getItem()), Registries.ITEM.getRawId(stack2.getItem())); + } } if (!areStacksEqual(stack1, stack2)) { + // Sort's Data Components by Hash Code return Integer.compare(stack1.getComponents().hashCode(), stack2.getComponents().hashCode()); } + return Integer.compare(-stack1.getCount(), -stack2.getCount()); } + private static int getCustomPriority(ItemStack stack) { + if (stack == null || stack.isEmpty()) { + return -1; // No priority for empty stacks + } + + // Get item ID and name to check against custom priority lists + String itemID = Registries.ITEM.getId(stack.getItem()).toString(); + String itemName = stack.getName().getString(); + + if (itemID.equals(itemName)) + { + itemName = null; + } + + // Top priority check + int idTopPriority = topSortingPriorityList.indexOf(itemID); + int nameTopPriority = itemName != null ? topSortingPriorityList.indexOf(itemName) : -1; + + // Bottom priority check + int idBottomPriority = bottomSortingPriorityList.indexOf(itemID); + int nameBottomPriority = itemName != null ? bottomSortingPriorityList.indexOf(itemName) : -1; + + // Sort at the top: Prefer name priority if it exists + if (nameTopPriority != -1) + { + return topSortingPriorityList.size() - nameTopPriority; + } + if (idTopPriority != -1) + { + return topSortingPriorityList.size() - idTopPriority; + } + + // Sort at the bottom: Prefer name priority if it exists + if (nameBottomPriority != -1) + { + return -bottomSortingPriorityList.size() - nameBottomPriority - 2; + } + if (idBottomPriority != -1) + { + return -bottomSortingPriorityList.size() - idBottomPriority - 2; + } + + // Default: no specific priority found + return -1; + } + public static boolean onPong(StatisticsS2CPacket packet) { if (selectedSlotUpdateTask != null) diff --git a/src/main/java/fi/dy/masa/itemscroller/util/SortingCategory.java b/src/main/java/fi/dy/masa/itemscroller/util/SortingCategory.java new file mode 100644 index 000000000..5aef71d40 --- /dev/null +++ b/src/main/java/fi/dy/masa/itemscroller/util/SortingCategory.java @@ -0,0 +1,172 @@ +package fi.dy.masa.itemscroller.util; + +import java.util.Collection; +import java.util.Iterator; +import javax.annotation.Nullable; +import com.google.common.collect.ImmutableList; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.item.ItemGroup; +import net.minecraft.item.ItemStack; +import net.minecraft.registry.Registries; +import net.minecraft.util.Identifier; + +import fi.dy.masa.malilib.config.IConfigLockedListEntry; +import fi.dy.masa.malilib.config.IConfigLockedListType; +import fi.dy.masa.malilib.util.StringUtils; +import fi.dy.masa.itemscroller.Reference; + +public class SortingCategory implements IConfigLockedListType +{ + public static final SortingCategory INSTANCE = new SortingCategory(); + public ImmutableList VALUES = ImmutableList.copyOf(Entry.values()); + + @Nullable + public ItemGroup.DisplayContext buildDisplayContext(MinecraftClient mc) + { + if (mc.world == null) + { + return null; + } + + ItemGroup.DisplayContext ctx = new ItemGroup.DisplayContext(mc.world.getEnabledFeatures(), true, mc.world.getRegistryManager()); + + Registries.ITEM_GROUP.stream().filter((group) -> + group.getType() == ItemGroup.Type.CATEGORY).forEach((group) -> + group.updateEntries(ctx)); + + return ctx; + } + + public Entry fromItemStack(ItemStack stack) + { + for (int i = 0; i < Registries.ITEM_GROUP.size(); i++) + { + ItemGroup itemGroup = Registries.ITEM_GROUP.get(i); + + if (itemGroup != null && itemGroup.getType().equals(ItemGroup.Type.CATEGORY)) + { + Collection stacks; + Iterator iter; + + if (itemGroup.hasStacks()) + { + stacks = itemGroup.getDisplayStacks(); + iter = stacks.iterator(); + + while (iter.hasNext()) + { + if (ItemStack.areItemsEqual(iter.next(), stack)) + { + return fromItemGroup(itemGroup); + } + } + + } + + stacks = itemGroup.getSearchTabStacks(); + iter = stacks.iterator(); + + while (iter.hasNext()) + { + if (ItemStack.areItemsEqual(iter.next(), stack)) + { + return fromItemGroup(itemGroup); + } + } + + } + } + + return Entry.OTHER; + } + + @Nullable + public Entry fromItemGroup(ItemGroup group) + { + Identifier id = Registries.ITEM_GROUP.getId(group); + + if (id != null) + { + return Entry.fromString(id.getPath()); + } + + return Entry.OTHER; + } + + @Override + public ImmutableList getDefaultEntries() + { + ImmutableList.Builder list = ImmutableList.builder(); + + VALUES.forEach((list::add)); + + return list.build(); + } + + @Override + @Nullable + public IConfigLockedListEntry fromString(String string) + { + return Entry.fromString(string); + } + + public enum Entry implements IConfigLockedListEntry + { + BUILDING_BLOCKS ("building_blocks", "building_blocks"), + COLORED_BLOCKS ("colored_blocks", "colored_blocks"), + NATURAL ("natural_blocks", "natural_blocks"), + FUNCTIONAL ("functional_blocks", "functional_blocks"), + REDSTONE ("redstone_blocks", "redstone_blocks"), + TOOLS ("tools_and_utilities", "tools_and_utilities"), + COMBAT ("combat", "combat"), + FOOD_AND_DRINK ("food_and_drinks", "food_and_drinks"), + INGREDIENTS ("ingredients", "ingredients"), + SPAWN_EGGS ("spawn_eggs", "spawn_eggs"), + OPERATOR ("op_blocks", "op_blocks"), + OTHER ("other", "other"); + + private final String configKey; + private final String translationKey; + + Entry(String configKey, String translationKey) + { + this.configKey = configKey; + this.translationKey = Reference.MOD_ID+".gui.label.sorting_category."+translationKey; + } + + @Override + public String getStringValue() + { + return this.configKey; + } + + @Override + public String getDisplayName() + { + return StringUtils.getTranslatedOrFallback(this.translationKey, this.configKey); + } + + @Nullable + public static Entry fromString(String key) + { + for (Entry entry : values()) + { + if (entry.configKey.equalsIgnoreCase(key)) + { + return entry; + } + else if (entry.translationKey.equalsIgnoreCase(key)) + { + return entry; + } + else if (StringUtils.hasTranslation(entry.translationKey) && StringUtils.translate(entry.translationKey).equalsIgnoreCase(key)) + { + return entry; + } + } + + return null; + } + } +} diff --git a/src/main/java/fi/dy/masa/itemscroller/util/SortingMethod.java b/src/main/java/fi/dy/masa/itemscroller/util/SortingMethod.java new file mode 100644 index 000000000..4d9f52012 --- /dev/null +++ b/src/main/java/fi/dy/masa/itemscroller/util/SortingMethod.java @@ -0,0 +1,84 @@ +package fi.dy.masa.itemscroller.util; + +import com.google.common.collect.ImmutableList; + +import fi.dy.masa.malilib.config.IConfigOptionListEntry; +import fi.dy.masa.malilib.util.StringUtils; +import fi.dy.masa.itemscroller.Reference; + +public enum SortingMethod implements IConfigOptionListEntry +{ + CATEGORY_NAME ("category_name", "category_name"), + CATEGORY_COUNT ("category_count", "category_count"), + CATEGORY_RARITY ("category_rarity", "category_rarity"), + CATEGORY_RAWID ("category_rawid", "category_rawid"), + ITEM_NAME ("item_name", "item_name"), + ITEM_COUNT ("item_count", "item_count"), + ITEM_RARITY ("item_rarity", "item_rarity"), + ITEM_RAWID ("item_rawid", "item_rawid"); + + public static final ImmutableList VALUES = ImmutableList.copyOf(values()); + + private final String configString; + private final String translationKey; + + SortingMethod(String configString, String translationKey) + { + this.configString = configString; + this.translationKey = Reference.MOD_ID+".gui.label.sorting_method."+translationKey; + } + + @Override + public String getStringValue() + { + return this.configString; + } + + @Override + public String getDisplayName() + { + return StringUtils.getTranslatedOrFallback(this.translationKey, this.configString); + } + + @Override + public IConfigOptionListEntry cycle(boolean forward) + { + int id = this.ordinal(); + + if (forward) + { + if (++id >= values().length) + { + id = 0; + } + } + else + { + if (--id < 0) + { + id = values().length - 1; + } + } + + return values()[id % values().length]; + } + + @Override + public SortingMethod fromString(String value) + { + return fromStringStatic(value); + } + + public static SortingMethod fromStringStatic(String name) + { + for (SortingMethod val : VALUES) + { + if (val.configString.equalsIgnoreCase(name)) + { + return val; + } + } + + return SortingMethod.CATEGORY_NAME; + } +} diff --git a/src/main/resources/assets/itemscroller/lang/en_us.json b/src/main/resources/assets/itemscroller/lang/en_us.json index 9b1ee6c4c..c54061f83 100644 --- a/src/main/resources/assets/itemscroller/lang/en_us.json +++ b/src/main/resources/assets/itemscroller/lang/en_us.json @@ -8,6 +8,7 @@ "itemscroller.config.generic.name.massCraftInterval": "massCraftInterval", "itemscroller.config.generic.name.massCraftIterations": "massCraftIterations", "itemscroller.config.generic.name.massCraftSwapsOnly": "massCraftSwapsOnly", + "itemscroller.config.generic.name.massCraftUseRecipeBook": "massCraftUseRecipeBook", "itemscroller.config.generic.name.packetRateLimit": "packetRateLimit", "itemscroller.config.generic.name.craftingRecipesSaveToFile": "craftingRecipesSaveToFile", "itemscroller.config.generic.name.craftingRecipesSaveFileIsGlobal": "craftingRecipesSaveFileIsGlobal", @@ -18,8 +19,13 @@ "itemscroller.config.generic.name.useSlotPositionAwareScrollDirection": "useSlotPositionAwareScrollDirection", "itemscroller.config.generic.name.villagerTradeUseGlobalFavorites": "villagerTradeUseGlobalFavorites", "itemscroller.config.generic.name.villagerTradeListRememberScrollPosition": "villagerTradeListRememberScrollPosition", + "itemscroller.config.generic.name.sortInventoryToggle": "sortInventoryToggle", "itemscroller.config.generic.name.sortAssumeEmptyBoxStacks": "sortAssumeEmptyBoxStacks", "itemscroller.config.generic.name.sortShulkerBoxesAtEnd": "sortShulkerBoxesAtEnd", + "itemscroller.config.generic.name.sortTopPriorityInventory": "sortTopPriorityInventory", + "itemscroller.config.generic.name.sortBottomPriorityInventory": "sortBottomPriorityInventory", + "itemscroller.config.generic.name.sortMethodDefault": "sortMethodDefault", + "itemscroller.config.generic.name.sortCategoryOrder": "sortCategoryOrder", "itemscroller.config.generic.comment.carpetCtrlQCraftingEnabledOnServer": "Set to true if the server is running the Carpet mod,\nand has the ctrlQCrafting option enabled.\nThis just changes which method Item Scroller uses\nfor the Drop key + Shift + Right click crafting.", "itemscroller.config.generic.comment.clientCraftingFixOn1_12": "Enable updating the crafting recipe output directly on the client side.\nThis fixes the quick/mass crafting and right-click-to-craft-a-stack\nfeatures otherwise being broken in 1.12.", @@ -30,18 +36,24 @@ "itemscroller.config.generic.comment.massCraftInterval": "The interval in game ticks the massCraft operation is repeated at", "itemscroller.config.generic.comment.massCraftIterations": "How many massCraft iterations/attempts to do per execution.\nWith unstackable items or a full inventory and \"small recipe\"\nthis will need to be larger, as a shift + click craft to the inventory\nmight only craft 1 or 2 items per operation.", "itemscroller.config.generic.comment.massCraftSwapsOnly": "Uses a newer method of filling the crafting grid,\nusing only swap slot packets.\n\nNote: Due to only using slot swap packets,\nno partial crafts are possible! And also no\nstack splitting will happen, at all.", + "itemscroller.config.generic.comment.massCraftUseRecipeBook": "Uses the \"Recipe Book Protocol\" inspired by Andrew54757's \"Craft-Fix\" fork,\nbut re-implemented in a way that works for 1.21+.\n§6NOTE: This is NOT compatible with \"rateLimitClickPackets\" enabled.", "itemscroller.config.generic.comment.packetRateLimit": "The limit of sent emulated slot click packets per game tick,\nif 'rateLimitClickPackets' is enabled", "itemscroller.config.generic.comment.craftingRecipesSaveToFile": "If enabled, then the crafting features recipes are saved to a file\ninside minecraft/itemscroller/recipes_worldorservername.nbt.\nThis makes the recipes persistent across game restarts.", "itemscroller.config.generic.comment.craftingRecipesSaveFileIsGlobal": "If true, then the recipe file is global, instead\n of being saved per-world or server", - "itemscroller.config.generic.comment.rateLimitClickPackets": "This is meant for compatibility with Spigot servers and similar,\nwhich apply rate limits to packets from the client.\nThis queues up the emulated slot click packets and sends\nthem rate limited over time. The limit per game tick can be set in 'packetRateLimit´.", + "itemscroller.config.generic.comment.rateLimitClickPackets": "This is meant for compatibility with Spigot servers and similar,\nwhich apply rate limits to packets from the client.\nThis queues up the emulated slot click packets and sends\nthem rate limited over time. The limit per game tick can be set in 'packetRateLimit´.\n§6NOTE: This is NOT compatible with \"massCraftUseRecipeBook\" enabled.", "itemscroller.config.generic.comment.reverseScrollDirectionSingle": "Reverse the scrolling direction for single item mode.", "itemscroller.config.generic.comment.reverseScrollDirectionStacks": "Reverse the scrolling direction for full stacks mode.", "itemscroller.config.generic.comment.useRecipeCaching": "Enables caching the last used recipe in the crafting\nrecipe output item fetching code. This can help a lot\nwith lowering CPU usage when mass crafting stuff.", "itemscroller.config.generic.comment.useSlotPositionAwareScrollDirection": "When enabled, the item movement direction depends\non the slots' y-position on screen. Might be derpy with more\ncomplex inventories, use with caution!", "itemscroller.config.generic.comment.villagerTradeUseGlobalFavorites": "Whether or not global (per-item-type) villager trade\nfavorites should be used.", "itemscroller.config.generic.comment.villagerTradeListRememberScrollPosition": "Remember and restore the last scroll position in the\ntrade list when re-opening the GUI", - "itemscroller.config.generic.comment.sortAssumeEmptyBoxStacks": "Assume that empty boxes are stacking items\nwhen sorting the inventory.\nThis is useful if you installed mods to stack\nshulker boxes on remote server.\nThis will send an extra packet to ensure the\ninventory is synced.", + "itemscroller.config.generic.comment.sortInventoryToggle": "Enables the Quick Inventory Sorting feature.\nThis is activated by hovering over any item,\nand then pressing the bound §a\"sortInventory\"§r key bind.", + "itemscroller.config.generic.comment.sortAssumeEmptyBoxStacks": "Assume that empty shulker boxes can\nstack when sorting the inventory.\nThis is useful if you installed mods\nto allow shulker boxes to stack.\nThis will send an extra packet to ensure the\ninventory is synced.", "itemscroller.config.generic.comment.sortShulkerBoxesAtEnd": "Sort shulker boxes at the end of the inventory\nwhen sorting the inventory.", + "itemscroller.config.generic.comment.sortTopPriorityInventory": "These items will have a priority to be sorted first.\nYou can put item IDs (eg. \"minecraft:diamond_sword\") or custom names (eg. \"Wither killer\").\nIf you put custom names, those will be preferred before the id.\nItems in the list will be sorted to the front according to the list.", + "itemscroller.config.generic.comment.sortBottomPriorityInventory": "These items will have a priority to be sorted last.\nYou can put item IDs (eg. \"minecraft:diamond_sword\") or custom names (eg. \"Wither killer\").\nIf you put custom names, those will be preferred before the id.\nItems in the list will be sorted to the back according to the list.", + "itemscroller.config.generic.comment.sortMethodDefault": "The default sorting method used to compare items.\n\n§6Category [ANY]§r = Sort by §a\"sortCategoryOrder\"§r first, and then followed by:\n§6Item Name§r - Sort by Items' Name\n§6Item Count§r - Sort by Item's Stack Size, then by RawID\n§6Item Rarity§r - Sort by Rarity, then by RawID\n§6Item RawID§r - Sort by RawID\n§d(RawID is the built-in Vanilla Item Index value)§r\n\n§cData Components are compared by Hash Code afterwards, and then\n§cby Item Stack Size after all other checks have passed.§r", + "itemscroller.config.generic.comment.sortCategoryOrder": "The Category order to be used for sorting items.\nThe Vanilla Creative Menu Categories are used.", "itemscroller.config.toggles.name.enableCraftingFeatures": "enableCraftingFeatures", "itemscroller.config.toggles.name.enableDropkeyDropMatching": "enableDropkeyDropMatching", @@ -143,6 +155,28 @@ "itemscroller.gui.label.trades": "Trades", "itemscroller.gui.label.trade_uses": "Trade uses: %d / %d", + "itemscroller.gui.label.sorting_method.category_name": "Category Name", + "itemscroller.gui.label.sorting_method.category_count": "Category Count", + "itemscroller.gui.label.sorting_method.category_rarity": "Category Rarity", + "itemscroller.gui.label.sorting_method.category_rawid": "Category RawID", + "itemscroller.gui.label.sorting_method.item_name": "Item Name", + "itemscroller.gui.label.sorting_method.item_count": "Item Count", + "itemscroller.gui.label.sorting_method.item_rarity": "Item Rarity", + "itemscroller.gui.label.sorting_method.item_rawid": "Item Raw ID", + + "itemscroller.gui.label.sorting_category.building_blocks": "Building Blocks", + "itemscroller.gui.label.sorting_category.colored_blocks": "Colored Blocks", + "itemscroller.gui.label.sorting_category.natural_blocks": "Natural Blocks", + "itemscroller.gui.label.sorting_category.functional_blocks": "Functional Blocks", + "itemscroller.gui.label.sorting_category.redstone_blocks": "Redstone Items", + "itemscroller.gui.label.sorting_category.tools_and_utilities": "Tools and Utilities", + "itemscroller.gui.label.sorting_category.combat": "Combat Items", + "itemscroller.gui.label.sorting_category.food_and_drinks": "Food and Drinks", + "itemscroller.gui.label.sorting_category.ingredients": "Ingredients", + "itemscroller.gui.label.sorting_category.spawn_eggs": "Spawn Eggs", + "itemscroller.gui.label.sorting_category.op_blocks": "Operator Blocks", + "itemscroller.gui.label.sorting_category.other": "Other Items", + "itemscroller.gui.title.configs": "Item Scroller Configs - %s", "itemscroller.message.toggled_mod_off": "Toggled all Item Scroller functionality §cOFF", diff --git a/src/main/resources/assets/itemscroller/lang/es_es.json b/src/main/resources/assets/itemscroller/lang/es_es.json index 4291d5ce3..75aa107c2 100644 --- a/src/main/resources/assets/itemscroller/lang/es_es.json +++ b/src/main/resources/assets/itemscroller/lang/es_es.json @@ -8,6 +8,7 @@ "itemscroller.config.generic.name.massCraftInhibitMidUpdates": "massCraftInhibitMidUpdates", "itemscroller.config.generic.name.massCraftInterval": "massCraftInterval", "itemscroller.config.generic.name.massCraftIterations": "massCraftIterations", + "itemscroller.config.generic.name.massCraftUseRecipeBook": "massCraftUseRecipeBook", "itemscroller.config.generic.name.massCraftSwapsOnly": "massCraftSwapsOnly", "itemscroller.config.generic.name.modMainToggle": "modMainToggle", "itemscroller.config.generic.name.packetRateLimit": "packetRateLimit", @@ -18,8 +19,13 @@ "itemscroller.config.generic.name.useSlotPositionAwareScrollDirection": "useSlotPositionAwareScrollDirection", "itemscroller.config.generic.name.villagerTradeListRememberScrollPosition": "villagerTradeListRememberScrollPosition", "itemscroller.config.generic.name.villagerTradeUseGlobalFavorites": "villagerTradeUseGlobalFavorites", + "itemscroller.config.generic.name.sortInventoryToggle": "sortInventoryToggle", "itemscroller.config.generic.name.sortAssumeEmptyBoxStacks": "sortAssumeEmptyBoxStacks", "itemscroller.config.generic.name.sortShulkerBoxesAtEnd": "sortShulkerBoxesAtEnd", + "itemscroller.config.generic.name.sortTopPriorityInventory": "sortTopPriorityInventory", + "itemscroller.config.generic.name.sortBottomPriorityInventory": "sortBottomPriorityInventory", + "itemscroller.config.generic.name.sortMethodDefault": "sortMethodDefault", + "itemscroller.config.generic.name.sortCategoryOrder": "sortCategoryOrder", "itemscroller.config.generic.comment.carpetCtrlQCraftingEnabledOnServer": "Activalo si el servidor tiene Carpet mod,\ny tienes activado el ctrlQCrafting .\nEsto solo cambia la forma en la forma en la que el mod de Item Scroller\nfunciona con la combiacion de teclas + Shift + Clik derecho en mesas de crafteo/inventario y demás.", "itemscroller.config.generic.comment.clientCraftingFixOn1_12": "Arregla el problema del crafteo masivo en la 1.12.", @@ -30,6 +36,7 @@ "itemscroller.config.generic.comment.massCraftInhibitMidUpdates": "Previene las actualizaciones de la preview del item a crafear \nmientras se mueven los items en el crafteo, esto deberia reducir el uso de CPU\n al no estar el sistema consultando cada actualizacion necesaria.", "itemscroller.config.generic.comment.massCraftInterval": "El intervalo en tick del juego mientras se relaiza el crafteo masivo", "itemscroller.config.generic.comment.massCraftIterations": "Cuantas interacciones/intentos para el crafteo masivo relaizar por ejeccucion\nCon stacks de objetos, objetos no apilables,el inventario lleno o una receta pequeña\nEsto se usa para un crafteo masivo, ya que de lo contrario solo creara 1 o 2 crafteos por operacion.", + "itemscroller.config.generic.comment.massCraftUseRecipeBook": "Uses the \"Recipe Book Protocol\" inspired by Andrew54757's \"Craft-Fix\" fork,\nbut re-implemented in a way that works for 1.21+.\n§6NOTE: This is NOT compatible with \"rateLimitClickPackets\" enabled.", "itemscroller.config.generic.comment.massCraftSwapsOnly": "Usa el metodo mas reciente para llenar la grid de crafteo,\nutilizando solo intercambios de espacios.\n\nNota: Debido a que solo se utilizan paquetes de intercambio de los espacios,\nno son posibles los crafteos parciales\ntampoco se dividira ningun stack.", "itemscroller.config.generic.comment.modMainToggle": "Desactiva todas las funcionalidades del mod.", "itemscroller.config.generic.comment.packetRateLimit": "El límite de paquetes de clics en tragamonedas emulados enviados por tick de juego,\nsi 'rateLimitClickPackets' está habilitado", @@ -40,8 +47,13 @@ "itemscroller.config.generic.comment.useSlotPositionAwareScrollDirection": "Cuando esta activado, el movimiento de los items, depende de la posicion en las ranuras en la pantalla\n Esto puede tener un comportamiento inesperado con invetarios mas complejos\n¡Usalo con precaucion!", "itemscroller.config.generic.comment.villagerTradeListRememberScrollPosition": "Recuerda y restaura la ultima posicion de scroll en\nla lista de tradeos al abrir la GUI", "itemscroller.config.generic.comment.villagerTradeUseGlobalFavorites": "Activa los intercambios favoritos con aldenos que tenga guardados como Globales o Locales", + "itemscroller.config.generic.comment.sortInventoryToggle": "Enables the Quick Inventory Sorting feature.\nThis is activated by hovering over any item,\nand then pressing the bound §a\"sortInventory\"§r key bind.", "itemscroller.config.generic.comment.sortAssumeEmptyBoxStacks": "Assume that empty boxes are stacking items\nwhen sorting the inventory.\nThis is useful if you installed mods to stack\nshulker boxes on remote server.\nThis will send an extra packet to ensure the\ninventory is synced.", "itemscroller.config.generic.comment.sortShulkerBoxesAtEnd": "Sort shulker boxes at the end of the inventory\nwhen sorting the inventory.", + "itemscroller.config.generic.comment.sortTopPriorityInventory": "Estos ítems tendrán prioridad para ser ordenados primero.\nPuedes poner IDs de ítems (ej. \"minecraft:diamond_sword\") o nombres personalizados (ej. \"Matador de Withers\").\nSi pones nombres personalizados, estos serán preferidos antes que los IDs.\nLos ítems en la lista se ordenarán al frente según la lista.", + "itemscroller.config.generic.comment.sortBottomPriorityInventory": "Estos ítems tendrán prioridad para ser ordenados al final.\nPuedes poner IDs de ítems (ej. \"minecraft:diamond_sword\") o nombres personalizados (ej. \"Matador de Withers\").\nSi pones nombres personalizados, estos serán preferidos antes que los IDs.\nLos ítems en la lista se ordenarán al final según la lista.", + "itemscroller.config.generic.comment.sortMethodDefault": "The default sorting method used to compare items.\n\n§6Category [ANY]§r = Sort by §a\"sortCategoryOrder\"§r first, and then followed by:\n§6Item Name§r - Sort by Items' Name\n§6Item Count§r - Sort by Item's Stack Size, then by RawID\n§6Item Rarity§r - Sort by Rarity, then by RawID\n§6Item RawID§r - Sort by RawID\n§d(RawID is the built-in Vanilla Item Index value)§r\n\n§cData Components are compared by Hash Code afterwards, and then\n§cby Item Stack Size after all other checks have passed.§r", + "itemscroller.config.generic.comment.sortCategoryOrder": "The Category order to be used for sorting items.\nThe Vanilla Creative Menu Categories are used.", "itemscroller.config.toggles.name.enableCraftingFeatures": "enableCraftingFeatures", "itemscroller.config.toggles.name.enableDropkeyDropMatching": "enableDropkeyDropMatching", @@ -143,6 +155,28 @@ "itemscroller.gui.label.trade_uses": "Uso de Tradeos: %d / %d", "itemscroller.gui.label.trades": "Tradeos", + "itemscroller.gui.label.sorting_method.category_name": "Category Name", + "itemscroller.gui.label.sorting_method.category_count": "Category Count", + "itemscroller.gui.label.sorting_method.category_rarity": "Category Rarity", + "itemscroller.gui.label.sorting_method.category_rawid": "Category RawID", + "itemscroller.gui.label.sorting_method.item_name": "Item Name", + "itemscroller.gui.label.sorting_method.item_count": "Item Count", + "itemscroller.gui.label.sorting_method.item_rarity": "Item Rarity", + "itemscroller.gui.label.sorting_method.item_rawid": "Item Raw ID", + + "itemscroller.gui.label.sorting_category.building_blocks": "Building Blocks", + "itemscroller.gui.label.sorting_category.colored_blocks": "Colored Blocks", + "itemscroller.gui.label.sorting_category.natural_blocks": "Natural Blocks", + "itemscroller.gui.label.sorting_category.functional_blocks": "Functional Blocks", + "itemscroller.gui.label.sorting_category.redstone_blocks": "Redstone Items", + "itemscroller.gui.label.sorting_category.tools_and_utilities": "Tools and Utilities", + "itemscroller.gui.label.sorting_category.combat": "Combat Items", + "itemscroller.gui.label.sorting_category.food_and_drinks": "Food and Drinks", + "itemscroller.gui.label.sorting_category.ingredients": "Ingredients", + "itemscroller.gui.label.sorting_category.spawn_eggs": "Spawn Eggs", + "itemscroller.gui.label.sorting_category.op_blocks": "Operator Blocks", + "itemscroller.gui.label.sorting_category.other": "Other Items", + "itemscroller.gui.title.configs": "Item Scroller Configuraciones - %s", "itemscroller.message.toggled_mod_off": "Activar todas las funionalidades de Item Scroller §cOFF", diff --git a/src/main/resources/assets/itemscroller/lang/zh_cn.json b/src/main/resources/assets/itemscroller/lang/zh_cn.json index 5c19e3b1f..7c42330e6 100644 --- a/src/main/resources/assets/itemscroller/lang/zh_cn.json +++ b/src/main/resources/assets/itemscroller/lang/zh_cn.json @@ -7,6 +7,7 @@ "itemscroller.config.generic.name.massCraftInhibitMidUpdates": "批量合成刷新优化", "itemscroller.config.generic.name.massCraftInterval": "批量合成速度", "itemscroller.config.generic.name.massCraftIterations": "批量合成尝试", + "itemscroller.config.generic.name.massCraftUseRecipeBook": "massCraftUseRecipeBook", "itemscroller.config.generic.name.massCraftSwapsOnly": "批量合成新方法", "itemscroller.config.generic.name.packetRateLimit": "最大数据流", "itemscroller.config.generic.name.craftingRecipesSaveToFile": "配方文件保存", @@ -18,8 +19,13 @@ "itemscroller.config.generic.name.useSlotPositionAwareScrollDirection": "栏位滚动方向", "itemscroller.config.generic.name.villagerTradeUseGlobalFavorites": "置顶收藏交易", "itemscroller.config.generic.name.villagerTradeListRememberScrollPosition": "交易书签", + "itemscroller.config.generic.name.sortInventoryToggle": "sortInventoryToggle", "itemscroller.config.generic.name.sortAssumeEmptyBoxStacks": "sortAssumeEmptyBoxStacks", "itemscroller.config.generic.name.sortShulkerBoxesAtEnd": "sortShulkerBoxesAtEnd", + "itemscroller.config.generic.name.sortTopPriorityInventory": "优先排序物品清单", + "itemscroller.config.generic.name.sortBottomPriorityInventory": "最后排序物品清单", + "itemscroller.config.generic.name.sortMethodDefault": "sortMethodDefault", + "itemscroller.config.generic.name.sortCategoryOrder": "sortCategoryOrder", "itemscroller.config.generic.comment.carpetCtrlQCraftingEnabledOnServer": "如果服务器正在运行地毯端则建议开启\n这将改变物品滚轮整组丢弃的使用方式\n变为丢弃+Shift+右击丢弃整组。", "itemscroller.config.generic.comment.clientCraftingFixOn1_12": "在客户端直接更新合成配方输出\n这修正了在1.12中快速/批量制造和右键点击制造所产生的不可使用的问题。", @@ -29,6 +35,7 @@ "itemscroller.config.generic.comment.massCraftInhibitMidUpdates": "禁止批量合成时配方输出槽在材料移动到合成GUI的过程中更新。\n这将会减少CPU的占用,因为游戏会在每次合成配方更改后\n不断地查询配方并显示产物。", "itemscroller.config.generic.comment.massCraftInterval": "每tick中批量合成的次数。", "itemscroller.config.generic.comment.massCraftIterations": "设置每次执行多少次批量合成的迭代或者尝试,\n适用于当玩家合成不可堆叠的物品或玩家背包已满这类情况,\n这将需要玩家拥有更大的背包空间,\n因为使用shift单击合成时,\n背包中可能只进行1或2次合成操作。", + "itemscroller.config.generic.comment.massCraftUseRecipeBook": "Uses the \"Recipe Book Protocol\" inspired by Andrew54757's \"Craft-Fix\" fork,\nbut re-implemented in a way that works for 1.21+.\n§6NOTE: This is NOT compatible with \"rateLimitClickPackets\" enabled.", "itemscroller.config.generic.comment.massCraftSwapsOnly": "使用新的方法填充配方GUI,\n即只使用slot swap packets(槽位交换包)\n注意:由于只使用槽位交换包,不可能有部分的合成!\n而且也不会发生整组物品拆开的现象。", "itemscroller.config.generic.comment.packetRateLimit": "当 §6[连点限制]§f 开启后每个tick发送的最大模拟操作数据包的数量。", "itemscroller.config.generic.comment.craftingRecipesSaveToFile": "启用后,合成配方将保存到文件\n\"minecraft/itemscroller/recipes_worldorservername.nbt\"\n中。这可以使得合成配方在游戏重启后能保持不变。", @@ -40,8 +47,13 @@ "itemscroller.config.generic.comment.useSlotPositionAwareScrollDirection": "启用后,物品的移动方向将取决于屏幕上栏位的y轴位置。\n可能导致更复杂的背包,谨慎使用!", "itemscroller.config.generic.comment.villagerTradeUseGlobalFavorites": "启用后,收藏的交易项目将会被置顶。", "itemscroller.config.generic.comment.villagerTradeListRememberScrollPosition": "启用后,退出村民交易GUI将不会重置滚动条。", + "itemscroller.config.generic.comment.sortInventoryToggle": "Enables the Quick Inventory Sorting feature.\nThis is activated by hovering over any item,\nand then pressing the bound §a\"sortInventory\"§r key bind.", "itemscroller.config.generic.comment.sortAssumeEmptyBoxStacks": "整理物品栏时假设空潜影盒可以堆叠。\n当你在远程服务器启用此特性时会很有用。\n这会多发送一个网络包来确保服务器同步", "itemscroller.config.generic.comment.sortShulkerBoxesAtEnd": "整理物品栏时将潜影盒放在最后。", + "itemscroller.config.generic.comment.sortTopPriorityInventory": "这些物品将优先排序。\n你可以输入物品ID(例如 \"minecraft:diamond_sword\")或自定义名称(例如 \"凋零杀手\")。\n如果输入了自定义名称,则优先使用自定义名称。\n列表中的物品将根据列表顺序排在前面。", + "itemscroller.config.generic.comment.sortBottomPriorityInventory": "这些物品将最后排序。\n你可以输入物品ID(例如 \"minecraft:diamond_sword\")或自定义名称(例如 \"凋零杀手\")。\n如果输入了自定义名称,则优先使用自定义名称。\n列表中的物品将根据列表顺序排在后面。", + "itemscroller.config.generic.comment.sortMethodDefault": "The default sorting method used to compare items.\n\n§6Category [ANY]§r = Sort by §a\"sortCategoryOrder\"§r first, and then followed by:\n§6Item Name§r - Sort by Items' Name\n§6Item Count§r - Sort by Item's Stack Size, then by RawID\n§6Item Rarity§r - Sort by Rarity, then by RawID\n§6Item RawID§r - Sort by RawID\n§d(RawID is the built-in Vanilla Item Index value)§r\n\n§cData Components are compared by Hash Code afterwards, and then\n§cby Item Stack Size after all other checks have passed.§r", + "itemscroller.config.generic.comment.sortCategoryOrder": "The Category order to be used for sorting items.\nThe Vanilla Creative Menu Categories are used.", "itemscroller.config.toggles.name.enableCraftingFeatures": "启用配方视图", "itemscroller.config.toggles.name.enableDropkeyDropMatching": "全部丢弃", @@ -143,6 +155,28 @@ "itemscroller.gui.label.trades": "交易", "itemscroller.gui.label.trade_uses": "交易使用: %d / %", + "itemscroller.gui.label.sorting_method.category_name": "Category Name", + "itemscroller.gui.label.sorting_method.category_count": "Category Count", + "itemscroller.gui.label.sorting_method.category_rarity": "Category Rarity", + "itemscroller.gui.label.sorting_method.category_rawid": "Category RawID", + "itemscroller.gui.label.sorting_method.item_name": "Item Name", + "itemscroller.gui.label.sorting_method.item_count": "Item Count", + "itemscroller.gui.label.sorting_method.item_rarity": "Item Rarity", + "itemscroller.gui.label.sorting_method.item_rawid": "Item Raw ID", + + "itemscroller.gui.label.sorting_category.building_blocks": "Building Blocks", + "itemscroller.gui.label.sorting_category.colored_blocks": "Colored Blocks", + "itemscroller.gui.label.sorting_category.natural_blocks": "Natural Blocks", + "itemscroller.gui.label.sorting_category.functional_blocks": "Functional Blocks", + "itemscroller.gui.label.sorting_category.redstone_blocks": "Redstone Items", + "itemscroller.gui.label.sorting_category.tools_and_utilities": "Tools and Utilities", + "itemscroller.gui.label.sorting_category.combat": "Combat Items", + "itemscroller.gui.label.sorting_category.food_and_drinks": "Food and Drinks", + "itemscroller.gui.label.sorting_category.ingredients": "Ingredients", + "itemscroller.gui.label.sorting_category.spawn_eggs": "Spawn Eggs", + "itemscroller.gui.label.sorting_category.op_blocks": "Operator Blocks", + "itemscroller.gui.label.sorting_category.other": "Other Items", + "itemscroller.gui.title.configs": "Item Scroller 设置 - %s", "itemscroller.message.toggled_mod_off": "Item Scroller 功能调整为:§c关闭", diff --git a/src/main/resources/assets/itemscroller/lang/zh_tw.json b/src/main/resources/assets/itemscroller/lang/zh_tw.json index ee8600a09..94a01249b 100644 --- a/src/main/resources/assets/itemscroller/lang/zh_tw.json +++ b/src/main/resources/assets/itemscroller/lang/zh_tw.json @@ -7,6 +7,7 @@ "itemscroller.config.generic.name.massCraftInhibitMidUpdates": "批量合成刷新優化", "itemscroller.config.generic.name.massCraftInterval": "批量合成速度", "itemscroller.config.generic.name.massCraftIterations": "批量合成嘗試", + "itemscroller.config.generic.name.massCraftUseRecipeBook": "massCraftUseRecipeBook", "itemscroller.config.generic.name.massCraftSwapsOnly": "批量合成新方法", "itemscroller.config.generic.name.packetRateLimit": "最大數據流", "itemscroller.config.generic.name.craftingRecipesSaveToFile": "配方檔案保存", @@ -18,8 +19,13 @@ "itemscroller.config.generic.name.useSlotPositionAwareScrollDirection": "欄位滾動方向", "itemscroller.config.generic.name.villagerTradeUseGlobalFavorites": "置頂收藏交易", "itemscroller.config.generic.name.villagerTradeListRememberScrollPosition": "交易書籤", + "itemscroller.config.generic.name.sortInventoryToggle": "sortInventoryToggle", "itemscroller.config.generic.name.sortAssumeEmptyBoxStacks": "嘗試堆疊空界伏盒", "itemscroller.config.generic.name.sortShulkerBoxesAtEnd": "界伏盒置底", + "itemscroller.config.generic.name.sortTopPriorityInventory": "優先排序物品清單", + "itemscroller.config.generic.name.sortBottomPriorityInventory": "最後排序物品清單", + "itemscroller.config.generic.name.sortMethodDefault": "sortMethodDefault", + "itemscroller.config.generic.name.sortCategoryOrder": "sortCategoryOrder", "itemscroller.config.generic.comment.carpetCtrlQCraftingEnabledOnServer": "如果伺服器正在運行地毯端則建議開啟\n這將改變物品滾輪整組丟棄的使用方式\n變成 丟棄鍵+Shift+右鍵 丟棄整組。", "itemscroller.config.generic.comment.clientCraftingFixOn1_12": "在客戶端直接更新合成配方輸出\n這修正了在1.12中快速/大量製造和右鍵點擊製造所產生的不可使用的問題。", @@ -29,6 +35,7 @@ "itemscroller.config.generic.comment.massCraftInhibitMidUpdates": "禁止批量合成時配方輸出槽在材料移動到合成GUI的過程中更新。\n這將會減少CPU的佔用,因為遊戲會在每次合成配方更改後\n不斷地查詢配方並顯示產物。", "itemscroller.config.generic.comment.massCraftInterval": "每tick中批量合成的次數。", "itemscroller.config.generic.comment.massCraftIterations": "設定每次執行多少次批次合成的迭代或嘗試,\n適用於玩家合成不可堆疊的物品或玩家背包已滿這類情況,\n這將需要玩家擁有更大的背包空間,\n因為使用shift單擊合成時,\n背包中可能只進行1或2次合成操作。", + "itemscroller.config.generic.comment.massCraftUseRecipeBook": "Uses the \"Recipe Book Protocol\" inspired by Andrew54757's \"Craft-Fix\" fork,\nbut re-implemented in a way that works for 1.21+.\n§6NOTE: This is NOT compatible with \"rateLimitClickPackets\" enabled.", "itemscroller.config.generic.comment.massCraftSwapsOnly": "使用新的方法填滿配方GUI,\n即只使用槽位交換包\n注意:由於只使用槽位交換包,所以不可能有部分的合成!\n而且也不會發生整組物品拆開的現象。", "itemscroller.config.generic.comment.packetRateLimit": "當 §6[連點限制]§f 開啟後每個tick發送的最大模擬操作數據包的數量。", "itemscroller.config.generic.comment.craftingRecipesSaveToFile": "啟用後,合成配方將保存到檔案\n\"minecraft/itemscroller/recipes_worldorservername.nbt\"\n中。這可以使得合成配方在遊戲重啟後能保持不變。", @@ -40,8 +47,13 @@ "itemscroller.config.generic.comment.useSlotPositionAwareScrollDirection": "啟用後,物品的移動方向將取決於螢幕上欄位的y軸位置。\n可能導致更複雜的背包,謹慎使用!", "itemscroller.config.generic.comment.villagerTradeUseGlobalFavorites": "啟用後,收藏的交易項目將會被置頂。", "itemscroller.config.generic.comment.villagerTradeListRememberScrollPosition": "啟用後,退出村民交易GUI將不會重置滾動條。", + "itemscroller.config.generic.comment.sortInventoryToggle": "Enables the Quick Inventory Sorting feature.\nThis is activated by hovering over any item,\nand then pressing the bound §a\"sortInventory\"§r key bind.", "itemscroller.config.generic.comment.sortAssumeEmptyBoxStacks": "整理物品欄時假設空界伏盒可以堆疊。\n當你在遠端伺服器啟用此特性時會很有用。\n這會多發送一個網路封包來確保伺服器同步", "itemscroller.config.generic.comment.sortShulkerBoxesAtEnd": "整理物品欄時將界伏盒放在最後。", + "itemscroller.config.generic.comment.sortTopPriorityInventory": "這些物品將優先排序。\n你可以輸入物品ID(例如 \"minecraft:diamond_sword\")或自定義名稱(例如 \"凋零殺手\")。\n如果輸入了自定義名稱,則會優先使用自定義名稱。\n列表中的物品將根據列表順序排在前面。", + "itemscroller.config.generic.comment.sortBottomPriorityInventory": "這些物品將最後排序。\n你可以輸入物品ID(例如 \"minecraft:diamond_sword\")或自定義名稱(例如 \"凋零殺手\")。\n如果輸入了自定義名稱,則會優先使用自定義名稱。\n列表中的物品將根據列表順序排在後面。", + "itemscroller.config.generic.comment.sortMethodDefault": "The default sorting method used to compare items.\n\n§6Category [ANY]§r = Sort by §a\"sortCategoryOrder\"§r first, and then followed by:\n§6Item Name§r - Sort by Items' Name\n§6Item Count§r - Sort by Item's Stack Size, then by RawID\n§6Item Rarity§r - Sort by Rarity, then by RawID\n§6Item RawID§r - Sort by RawID\n§d(RawID is the built-in Vanilla Item Index value)§r\n\n§cData Components are compared by Hash Code afterwards, and then\n§cby Item Stack Size after all other checks have passed.§r", + "itemscroller.config.generic.comment.sortCategoryOrder": "The Category order to be used for sorting items.\nThe Vanilla Creative Menu Categories are used.", "itemscroller.config.toggles.name.enableCraftingFeatures": "啟用配方視圖", "itemscroller.config.toggles.name.enableDropkeyDropMatching": "全部丟棄", @@ -143,6 +155,28 @@ "itemscroller.gui.label.trades": "交易", "itemscroller.gui.label.trade_uses": "交易使用: %d / %", + "itemscroller.gui.label.sorting_method.category_name": "Category Name", + "itemscroller.gui.label.sorting_method.category_count": "Category Count", + "itemscroller.gui.label.sorting_method.category_rarity": "Category Rarity", + "itemscroller.gui.label.sorting_method.category_rawid": "Category RawID", + "itemscroller.gui.label.sorting_method.item_name": "Item Name", + "itemscroller.gui.label.sorting_method.item_count": "Item Count", + "itemscroller.gui.label.sorting_method.item_rarity": "Item Rarity", + "itemscroller.gui.label.sorting_method.item_rawid": "Item Raw ID", + + "itemscroller.gui.label.sorting_category.building_blocks": "Building Blocks", + "itemscroller.gui.label.sorting_category.colored_blocks": "Colored Blocks", + "itemscroller.gui.label.sorting_category.natural_blocks": "Natural Blocks", + "itemscroller.gui.label.sorting_category.functional_blocks": "Functional Blocks", + "itemscroller.gui.label.sorting_category.redstone_blocks": "Redstone Items", + "itemscroller.gui.label.sorting_category.tools_and_utilities": "Tools and Utilities", + "itemscroller.gui.label.sorting_category.combat": "Combat Items", + "itemscroller.gui.label.sorting_category.food_and_drinks": "Food and Drinks", + "itemscroller.gui.label.sorting_category.ingredients": "Ingredients", + "itemscroller.gui.label.sorting_category.spawn_eggs": "Spawn Eggs", + "itemscroller.gui.label.sorting_category.op_blocks": "Operator Blocks", + "itemscroller.gui.label.sorting_category.other": "Other Items", + "itemscroller.gui.title.configs": "Item Scroller 配置 - %s", "itemscroller.message.toggled_mod_off": "Item Scroller 功能調整為:§c關閉", diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 93b73a3ac..861c38ea6 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -34,6 +34,6 @@ "accessWidener": "itemscroller.accesswidener", "depends": { "minecraft": ">=1.21 <=1.21.1", - "malilib": ">=0.20.0" + "malilib": ">=0.20.3-sakura.1" } } From ed90d287de928d963fd7ad90bd4718a553a4c4d6 Mon Sep 17 00:00:00 2001 From: Sakura Ryoko Date: Wed, 11 Sep 2024 20:50:15 -0400 Subject: [PATCH 2/4] Missed a backport item --- gradle.properties | 2 +- .../fi/dy/masa/itemscroller/mixin/MixinItemStack.java | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/gradle.properties b/gradle.properties index af49b590c..112d60708 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,7 +9,7 @@ author = masa mod_file_name = itemscroller-fabric # Current mod version -mod_version = 0.24.1-sakura.1 +mod_version = 0.24.1-sakura.2 # Required malilib version malilib_version = 0.20.3-sakura.1 diff --git a/src/main/java/fi/dy/masa/itemscroller/mixin/MixinItemStack.java b/src/main/java/fi/dy/masa/itemscroller/mixin/MixinItemStack.java index fca0f3c53..e03816af9 100644 --- a/src/main/java/fi/dy/masa/itemscroller/mixin/MixinItemStack.java +++ b/src/main/java/fi/dy/masa/itemscroller/mixin/MixinItemStack.java @@ -17,7 +17,9 @@ public class MixinItemStack private void dontCap(int maxCount, CallbackInfo ci) { // Client-side fx for empty shulker box stacking - if (MinecraftClient.getInstance().isOnThread() && Configs.Generic.SORT_ASSUME_EMPTY_BOX_STACKS.getBooleanValue()) + if (MinecraftClient.getInstance().isOnThread() && + Configs.Generic.SORT_INVENTORY_TOGGLE.getBooleanValue() && + Configs.Generic.SORT_ASSUME_EMPTY_BOX_STACKS.getBooleanValue()) { ci.cancel(); } @@ -27,7 +29,10 @@ private void dontCap(int maxCount, CallbackInfo ci) private void getMaxCount(CallbackInfoReturnable cir) { // Client-side fx for empty shulker box stacking - if (MinecraftClient.getInstance().isOnThread() && Configs.Generic.SORT_ASSUME_EMPTY_BOX_STACKS.getBooleanValue() && InventoryUtils.assumeEmptyShulkerStacking) + if (MinecraftClient.getInstance().isOnThread() && + Configs.Generic.SORT_INVENTORY_TOGGLE.getBooleanValue() && + Configs.Generic.SORT_ASSUME_EMPTY_BOX_STACKS.getBooleanValue() && + InventoryUtils.assumeEmptyShulkerStacking) { cir.setReturnValue(InventoryUtils.stackMaxSize((ItemStack) (Object) this, true)); } From c5ece3d9bf6a696c6ee41844e1e235cf86a3b674 Mon Sep 17 00:00:00 2001 From: Sakura Ryoko Date: Sat, 14 Sep 2024 16:42:12 -0400 Subject: [PATCH 3/4] fix Freeze when sorting with shulker box screen open, add sortBundlesLast add sortShulkers/Bundles Inverted. --- gradle.properties | 6 +- .../dy/masa/itemscroller/config/Configs.java | 16 +- .../itemscroller/util/InventoryUtils.java | 211 ++++++++++++++++-- .../assets/itemscroller/lang/en_us.json | 6 + 4 files changed, 215 insertions(+), 24 deletions(-) diff --git a/gradle.properties b/gradle.properties index 112d60708..96d2edba2 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,11 +9,11 @@ author = masa mod_file_name = itemscroller-fabric # Current mod version -mod_version = 0.24.1-sakura.2 +mod_version = 0.24.1-sakura.3 # Required malilib version -malilib_version = 0.20.3-sakura.1 -malilib_id = 51615eb976 +malilib_version = 0.20.3-sakura.2 +malilib_id = a881371c32 # Minecraft, Fabric Loader and API and mappings versions minecraft_version_out = 1.21 diff --git a/src/main/java/fi/dy/masa/itemscroller/config/Configs.java b/src/main/java/fi/dy/masa/itemscroller/config/Configs.java index e4bac14c5..c15833889 100644 --- a/src/main/java/fi/dy/masa/itemscroller/config/Configs.java +++ b/src/main/java/fi/dy/masa/itemscroller/config/Configs.java @@ -53,13 +53,16 @@ public static class Generic public static final ConfigBoolean VILLAGER_TRADE_USE_GLOBAL_FAVORITES = new ConfigBoolean("villagerTradeUseGlobalFavorites", true, "itemscroller.config.generic.comment.villagerTradeUseGlobalFavorites").translatedName("itemscroller.config.generic.name.villagerTradeUseGlobalFavorites"); public static final ConfigBoolean VILLAGER_TRADE_LIST_REMEMBER_SCROLL = new ConfigBoolean("villagerTradeListRememberScrollPosition", true, "itemscroller.config.generic.comment.villagerTradeListRememberScrollPosition").translatedName("itemscroller.config.generic.name.villagerTradeListRememberScrollPosition"); - public static final ConfigBoolean SORT_INVENTORY_TOGGLE = new ConfigBoolean("sortInventoryToggle", false, "itemscroller.config.generic.comment.sortInventoryToggle").translatedName("itemscroller.config.generic.name.sortInventoryToggle"); + public static final ConfigBoolean SORT_INVENTORY_TOGGLE = new ConfigBoolean("sortInventoryToggle", false, "itemscroller.config.generic.comment.sortInventoryToggle").translatedName("itemscroller.config.generic.name.sortInventoryToggle"); public static final ConfigBoolean SORT_ASSUME_EMPTY_BOX_STACKS = new ConfigBoolean("sortAssumeEmptyBoxStacks", true, "itemscroller.config.generic.comment.sortAssumeEmptyBoxStacks").translatedName("itemscroller.config.generic.name.sortAssumeEmptyBoxStacks"); public static final ConfigBoolean SORT_SHULKER_BOXES_AT_END = new ConfigBoolean("sortShulkerBoxesAtEnd", true, "itemscroller.config.generic.comment.sortShulkerBoxesAtEnd").translatedName("itemscroller.config.generic.name.sortShulkerBoxesAtEnd"); - public static final ConfigStringList SORT_TOP_PRIORITY_INVENTORY = new ConfigStringList("sortTopPriorityInventory", DEFAULT_TOP_SORTING, "itemscroller.config.generic.comment.sortTopPriorityInventory").translatedName("itemscroller.config.generic.name.sortTopPriorityInventory"); - public static final ConfigStringList SORT_BOTTOM_PRIORITY_INVENTORY = new ConfigStringList("sortBottomPriorityInventory", DEFAULT_BOTTOM_SORTING, "itemscroller.config.generic.comment.sortBottomPriorityInventory").translatedName("itemscroller.config.generic.name.sortBottomPriorityInventory"); - public static final ConfigOptionList SORT_METHOD_DEFAULT = new ConfigOptionList("sortMethodDefault", SortingMethod.CATEGORY_NAME, "itemscroller.config.generic.comment.sortMethodDefault").translatedName("itemscroller.config.generic.name.sortMethodDefault"); - public static final ConfigLockedList SORT_CATEGORY_ORDER = new ConfigLockedList("sortCategoryOrder", SortingCategory.INSTANCE, "itemscroller.config.generic.comment.sortCategoryOrder").translatedName("itemscroller.config.generic.name.sortCategoryOrder"); + public static final ConfigBoolean SORT_SHULKER_BOXES_INVERTED = new ConfigBoolean("sortShulkerBoxesInverted", false, "itemscroller.config.generic.comment.sortShulkerBoxesInverted").translatedName("itemscroller.config.generic.name.sortShulkerBoxesInverted"); + public static final ConfigBoolean SORT_BUNDLES_AT_END = new ConfigBoolean("sortBundlesAtEnd", true, "itemscroller.config.generic.comment.sortBundlesAtEnd").translatedName("itemscroller.config.generic.name.sortBundlesAtEnd"); + public static final ConfigBoolean SORT_BUNDLES_INVERTED = new ConfigBoolean("sortBundlesInverted", false, "itemscroller.config.generic.comment.sortBundlesInverted").translatedName("itemscroller.config.generic.name.sortBundlesInverted"); + public static final ConfigStringList SORT_TOP_PRIORITY_INVENTORY = new ConfigStringList("sortTopPriorityInventory", DEFAULT_TOP_SORTING, "itemscroller.config.generic.comment.sortTopPriorityInventory").translatedName("itemscroller.config.generic.name.sortTopPriorityInventory"); + public static final ConfigStringList SORT_BOTTOM_PRIORITY_INVENTORY = new ConfigStringList("sortBottomPriorityInventory", DEFAULT_BOTTOM_SORTING, "itemscroller.config.generic.comment.sortBottomPriorityInventory").translatedName("itemscroller.config.generic.name.sortBottomPriorityInventory"); + public static final ConfigOptionList SORT_METHOD_DEFAULT = new ConfigOptionList("sortMethodDefault", SortingMethod.CATEGORY_NAME, "itemscroller.config.generic.comment.sortMethodDefault").translatedName("itemscroller.config.generic.name.sortMethodDefault"); + public static final ConfigLockedList SORT_CATEGORY_ORDER = new ConfigLockedList("sortCategoryOrder", SortingCategory.INSTANCE, "itemscroller.config.generic.comment.sortCategoryOrder").translatedName("itemscroller.config.generic.name.sortCategoryOrder"); public static final ImmutableList OPTIONS = ImmutableList.of( CARPET_CTRL_Q_CRAFTING, @@ -86,6 +89,9 @@ public static class Generic SORT_INVENTORY_TOGGLE, SORT_ASSUME_EMPTY_BOX_STACKS, SORT_SHULKER_BOXES_AT_END, + SORT_SHULKER_BOXES_INVERTED, + SORT_BUNDLES_AT_END, + SORT_BUNDLES_INVERTED, SORT_TOP_PRIORITY_INVENTORY, SORT_BOTTOM_PRIORITY_INVENTORY, SORT_METHOD_DEFAULT, diff --git a/src/main/java/fi/dy/masa/itemscroller/util/InventoryUtils.java b/src/main/java/fi/dy/masa/itemscroller/util/InventoryUtils.java index 51b20b18e..0181ba4f1 100644 --- a/src/main/java/fi/dy/masa/itemscroller/util/InventoryUtils.java +++ b/src/main/java/fi/dy/masa/itemscroller/util/InventoryUtils.java @@ -9,25 +9,25 @@ import it.unimi.dsi.fastutil.ints.IntComparator; import it.unimi.dsi.fastutil.ints.IntIntMutablePair; +import org.apache.commons.lang3.math.Fraction; + import net.minecraft.block.ShulkerBoxBlock; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.screen.Screen; -import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen; -import net.minecraft.client.gui.screen.ingame.HandledScreen; -import net.minecraft.client.gui.screen.ingame.InventoryScreen; -import net.minecraft.client.gui.screen.ingame.MerchantScreen; +import net.minecraft.client.gui.screen.ingame.*; import net.minecraft.client.network.ClientPlayerEntity; import net.minecraft.client.world.ClientWorld; +import net.minecraft.component.ComponentChanges; +import net.minecraft.component.ComponentMap; import net.minecraft.component.DataComponentTypes; +import net.minecraft.component.type.BundleContentsComponent; import net.minecraft.component.type.ContainerComponent; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerInventory; import net.minecraft.inventory.CraftingResultInventory; import net.minecraft.inventory.Inventory; import net.minecraft.inventory.RecipeInputInventory; -import net.minecraft.item.BlockItem; -import net.minecraft.item.ItemGroup; -import net.minecraft.item.ItemStack; +import net.minecraft.item.*; import net.minecraft.network.listener.ClientPlayPacketListener; import net.minecraft.network.packet.Packet; import net.minecraft.network.packet.c2s.play.ClientStatusC2SPacket; @@ -44,6 +44,7 @@ import net.minecraft.screen.slot.SlotActionType; import net.minecraft.screen.slot.TradeOutputSlot; import net.minecraft.util.Identifier; +import net.minecraft.util.collection.DefaultedList; import net.minecraft.village.TradeOffer; import net.minecraft.village.TradeOfferList; import net.minecraft.world.GameRules; @@ -84,6 +85,9 @@ public class InventoryUtils public static List> invUpdatesBuffer = new ArrayList<>(); private static ItemGroup.DisplayContext displayContext; + private static Pair lastSwapTry = Pair.of(-1, -1); + private static List> hotbarSwaps = new ArrayList<>(); + public static void setInhibitCraftingOutputUpdate(boolean inhibitUpdate) { inhibitCraftResultUpdate = inhibitUpdate; @@ -1709,6 +1713,7 @@ public static void setCraftingGridContentsUsingSwaps(HandledScreen= 0) { Slot ingredientSlot = gui.getScreenHandler().getSlot(index); + if (ingredientSlot.inventory instanceof PlayerInventory && ingredientSlot.getIndex() < 9) { // hotbar @@ -2625,19 +2630,47 @@ public static void sortInventory(HandledScreen gui) { Pair range = new IntIntMutablePair(Integer.MAX_VALUE, 0); Slot focusedSlot = AccessorUtils.getSlotUnderMouse(gui); - if (focusedSlot == null) { + lastSwapTry = Pair.of(-1, -1); + hotbarSwaps.clear(); + + if (focusedSlot == null || focusedSlot.hasStack() == false) + { return; } + //System.out.printf("sort - focusedSlot[%d]: %s\n", focusedSlot.id, focusedSlot.hasStack() ? focusedSlot.getStack().getName().getString() : ""); ScreenHandler container = gui.getScreenHandler(); + int limit = container.slots.size(); + if (gui instanceof CreativeInventoryScreen creative && !creative.isInventoryTabSelected()) { return; } + if (gui instanceof InventoryScreen && (focusedSlot.id < 9 || focusedSlot.id > 44)) + { + return; + } + if (gui instanceof ShulkerBoxScreen) + { + //System.out.printf("sort - ShulkerBoxScreen %s\n", true); + + // Free Hotbar slots if sorting inside the Shulker Box + // Might cause an endless loop if hotbar is full. + if (focusedSlot.id < 27) + { + if (tryFreeHotbarForShulkerSwaps(gui, container) == false) + { + ItemScroller.logger.warn("sortInventory: Free Hotbar slots are required in order to complete a Shulker box sorting task."); + return; + } + } + } int focusedIndex = -1; - for (int i = 0; i < container.slots.size(); i++) + for (int i = 0; i < limit; i++) { Slot slot = container.slots.get(i); + + //System.out.printf("sort - slot[%d]: %s\n", i, slot.hasStack() ? slot.getStack().getName().getString() : ""); if (slot == focusedSlot) { focusedIndex = i; @@ -2686,8 +2719,8 @@ else if (range.right() - range.left() == 36) } } - //System.out.printf("Sorting [%d, %d)\n", range.first(), range.second()); - //ItemScroller.printDebug("Sorting [{}, {}]", range.first(), range.second()); + //System.out.printf("Sorting [%d, %d] (first, second)\n", range.first(), range.second()); + //System.out.printf("Sorting [%d, %d] (left, right)\n", range.left(), range.right()); tryClearCursor(gui); tryMergeItems(gui, range.left(), range.right() - 1); @@ -2701,6 +2734,82 @@ else if (range.right() - range.left() == 36) { quickSort(gui, range.first(), range.second() - 1); } + + if (hotbarSwaps.isEmpty() == false) + { + restoreHotbarForShulkerSwaps(gui, container); + } + } + + /** + * Free Hotbar slots for sorting operation. Restore to Original locations when done using restoreHotbarForShulkerSwaps() + * + * @param gui + * @param container + */ + private static boolean tryFreeHotbarForShulkerSwaps(HandledScreen gui, ScreenHandler container) + { + final int lastSlot = container.slots.size() - 1; + final int firstSlot = lastSlot - 9; + final int freeTarget = 2; + int emptySlot = -1; + int freeCount = 0; + + //System.out.printf("tryFreeHotbarForShulkerSwaps first %d, last %d, target %d\n", firstSlot, lastSlot, freeTarget); + + for (int i = lastSlot; i > firstSlot; i--) + { + Slot slot = container.slots.get(i); + + if (slot.hasStack() == false && freeCount < freeTarget) + { + freeCount++; + } + else if (freeCount > freeTarget) + { + break; + } + else if (slot.hasStack()) + { + emptySlot = fi.dy.masa.malilib.util.InventoryUtils.findEmptySlotInPlayerInventory(container, false, true); + + if (emptySlot >= 0) + { + //System.out.printf("tryFreeHotbarForShulkerSwaps SWAP [%d -> %d]\n", i, emptySlot); + + hotbarSwaps.add(Pair.of(i, emptySlot)); + swapSlots(gui, i, emptySlot); + emptySlot = -1; + freeCount++; + } + } + } + + return freeCount > 0; + } + + /** + * Restore Temporary Hotbar Swaps to their original locations. + * + * @param gui + * @param container + */ + private static void restoreHotbarForShulkerSwaps(HandledScreen gui, ScreenHandler container) + { + //System.out.printf("sort - restoring hotbar slots\n"); + + hotbarSwaps.reversed().forEach((pair) -> + { + swapSlots(gui, pair.right(), pair.left()); + }); + + // Seem that we need to swap the first entry a second time inverted, for it to be correct + if (hotbarSwaps.isEmpty() == false) + { + swapSlots(gui, hotbarSwaps.getFirst().left(), hotbarSwaps.getFirst().right()); + } + + hotbarSwaps.clear(); } /** @@ -2710,9 +2819,11 @@ private static void quickSort(HandledScreen gui, int start, int end) { if (start >= end) return; + //System.out.printf("quickSort - start %d, end %d\n", start, end); ItemStack mid = gui.getScreenHandler().getSlot(end).getStack(); int l = start; int r = end - 1; + while (l < r) { while (l < r && compareStacks(gui.getScreenHandler().getSlot(l).getStack(), mid) < 0) @@ -2723,9 +2834,17 @@ private static void quickSort(HandledScreen gui, int start, int end) { r--; } - if (l != r) + + // This is ugly, but it stops infinite loops. + if (l != r && lastSwapTry.left() != l && lastSwapTry.right() != r) { swapSlots(gui, l, r); + lastSwapTry = Pair.of(l, r); + } + else if (l != r) + { + ItemScroller.logger.warn("quickSort: Item swap failure. Duplicate pair of [{}, {}], cancelling sort task", l, r); + return; } } if (compareStacks(gui.getScreenHandler().getSlot(l).getStack(), gui.getScreenHandler().getSlot(end).getStack()) >= 0) @@ -2764,6 +2883,18 @@ private static int compareStacks(ItemStack stack1, ItemStack stack2) return -1; } } + if (Configs.Generic.SORT_BUNDLES_AT_END.getBooleanValue()) + { + if (isBundle(stack1) && !isBundle(stack2)) + { + return 1; + } + if (!isBundle(stack1) && isBundle(stack2)) + { + return -1; + } + } + if (stack1.isEmpty() && stack2.isEmpty()) return 0; else if (stack1.isEmpty()) @@ -2771,21 +2902,57 @@ else if (stack1.isEmpty()) else if (stack2.isEmpty()) return -1; + //System.out.printf("sort - compareStacks stack1 [%s], stack2 [%s]\n", stack1.toString(), stack2.toString()); + if (isShulkerBox(stack1) && isShulkerBox(stack2)) { List contents1 = stack1.getOrDefault(DataComponentTypes.CONTAINER, ContainerComponent.DEFAULT).streamNonEmpty().toList(); List contents2 = stack2.getOrDefault(DataComponentTypes.CONTAINER, ContainerComponent.DEFAULT).streamNonEmpty().toList(); + if (contents1.size() != contents2.size()) { - return Integer.compare(contents1.size(), contents2.size()); + if (Configs.Generic.SORT_SHULKER_BOXES_INVERTED.getBooleanValue()) + { + return Integer.compare(contents1.size(), contents2.size()); + } + + return Integer.compare(contents2.size(), contents1.size()); + } + } + if (isBundle(stack1) && isBundle(stack2)) + { + if (isEmptyBundle(stack1) && isEmptyBundle(stack2)) + { + return 0; } + else if (isEmptyBundle(stack1)) + { + return 1; + } + else if (isEmptyBundle(stack2)) + { + return -1; + } + + BundleContentsComponent bundle1 = stack1.getOrDefault(DataComponentTypes.BUNDLE_CONTENTS, BundleContentsComponent.DEFAULT); + BundleContentsComponent bundle2 = stack2.getOrDefault(DataComponentTypes.BUNDLE_CONTENTS, BundleContentsComponent.DEFAULT); + Fraction occupancy1 = bundle1.getOccupancy(); + Fraction occupancy2 = bundle2.getOccupancy(); + + if (Configs.Generic.SORT_BUNDLES_INVERTED.getBooleanValue()) + { + return occupancy1.compareTo(occupancy2); + } + + return occupancy2.compareTo(occupancy1); } + SortingMethod method = (SortingMethod) Configs.Generic.SORT_METHOD_DEFAULT.getOptionListValue(); if (method.equals(SortingMethod.CATEGORY_NAME) || - method.equals(SortingMethod.CATEGORY_COUNT) || - method.equals(SortingMethod.CATEGORY_RARITY) || - method.equals(SortingMethod.CATEGORY_RAWID)) + method.equals(SortingMethod.CATEGORY_COUNT) || + method.equals(SortingMethod.CATEGORY_RARITY) || + method.equals(SortingMethod.CATEGORY_RAWID)) { // Sort by Catagory MinecraftClient mc = MinecraftClient.getInstance(); @@ -2923,6 +3090,16 @@ private static boolean isEmptyShulkerBox(ItemStack stack) return isShulkerBox(stack) && stack.getOrDefault(DataComponentTypes.CONTAINER, ContainerComponent.DEFAULT).streamNonEmpty().findAny().isEmpty(); } + private static boolean isBundle(ItemStack stack) + { + return stack.isOf(Items.BUNDLE) || stack.getComponents().contains(DataComponentTypes.BUNDLE_CONTENTS); + } + + private static boolean isEmptyBundle(ItemStack stack) + { + return isBundle(stack) && fi.dy.masa.malilib.util.InventoryUtils.bundleCountItems(stack) < 1; + } + public static int stackMaxSize(ItemStack stack, boolean assumeShulkerStacking) { if (stack.isEmpty()) @@ -3167,6 +3344,8 @@ public static void dropStack(HandledScreen gui, int slo public static void swapSlots(HandledScreen gui, int slotNum, int otherSlot) { + //System.out.printf("swapSlots: [%d -> %d]\n", slotNum, otherSlot); + clickSlot(gui, slotNum, 8, SlotActionType.SWAP); clickSlot(gui, otherSlot, 8, SlotActionType.SWAP); clickSlot(gui, slotNum, 8, SlotActionType.SWAP); diff --git a/src/main/resources/assets/itemscroller/lang/en_us.json b/src/main/resources/assets/itemscroller/lang/en_us.json index c54061f83..228461fa6 100644 --- a/src/main/resources/assets/itemscroller/lang/en_us.json +++ b/src/main/resources/assets/itemscroller/lang/en_us.json @@ -22,6 +22,9 @@ "itemscroller.config.generic.name.sortInventoryToggle": "sortInventoryToggle", "itemscroller.config.generic.name.sortAssumeEmptyBoxStacks": "sortAssumeEmptyBoxStacks", "itemscroller.config.generic.name.sortShulkerBoxesAtEnd": "sortShulkerBoxesAtEnd", + "itemscroller.config.generic.name.sortShulkerBoxesInverted": "sortShulkerBoxesInverted", + "itemscroller.config.generic.name.sortBundlesAtEnd": "sortBundlesAtEnd", + "itemscroller.config.generic.name.sortBundlesInverted": "sortBundlesInverted", "itemscroller.config.generic.name.sortTopPriorityInventory": "sortTopPriorityInventory", "itemscroller.config.generic.name.sortBottomPriorityInventory": "sortBottomPriorityInventory", "itemscroller.config.generic.name.sortMethodDefault": "sortMethodDefault", @@ -50,6 +53,9 @@ "itemscroller.config.generic.comment.sortInventoryToggle": "Enables the Quick Inventory Sorting feature.\nThis is activated by hovering over any item,\nand then pressing the bound §a\"sortInventory\"§r key bind.", "itemscroller.config.generic.comment.sortAssumeEmptyBoxStacks": "Assume that empty shulker boxes can\nstack when sorting the inventory.\nThis is useful if you installed mods\nto allow shulker boxes to stack.\nThis will send an extra packet to ensure the\ninventory is synced.", "itemscroller.config.generic.comment.sortShulkerBoxesAtEnd": "Sort shulker boxes at the end of the inventory\nwhen sorting the inventory.", + "itemscroller.config.generic.comment.sortShulkerBoxesInverted": "Sort shulker boxes so that the most-empty\nboxes are first, and the full boxes are last.", + "itemscroller.config.generic.comment.sortBundlesAtEnd": "Sort bundles at the end of the inventory\nwhen sorting the inventory.", + "itemscroller.config.generic.comment.sortBundlesInverted": "Sort bundles so that the most-empty\nbundles are first, and the full bundles are last.", "itemscroller.config.generic.comment.sortTopPriorityInventory": "These items will have a priority to be sorted first.\nYou can put item IDs (eg. \"minecraft:diamond_sword\") or custom names (eg. \"Wither killer\").\nIf you put custom names, those will be preferred before the id.\nItems in the list will be sorted to the front according to the list.", "itemscroller.config.generic.comment.sortBottomPriorityInventory": "These items will have a priority to be sorted last.\nYou can put item IDs (eg. \"minecraft:diamond_sword\") or custom names (eg. \"Wither killer\").\nIf you put custom names, those will be preferred before the id.\nItems in the list will be sorted to the back according to the list.", "itemscroller.config.generic.comment.sortMethodDefault": "The default sorting method used to compare items.\n\n§6Category [ANY]§r = Sort by §a\"sortCategoryOrder\"§r first, and then followed by:\n§6Item Name§r - Sort by Items' Name\n§6Item Count§r - Sort by Item's Stack Size, then by RawID\n§6Item Rarity§r - Sort by Rarity, then by RawID\n§6Item RawID§r - Sort by RawID\n§d(RawID is the built-in Vanilla Item Index value)§r\n\n§cData Components are compared by Hash Code afterwards, and then\n§cby Item Stack Size after all other checks have passed.§r", From da1d799271c0320fa61c7353b263a8b0bf3d88ba Mon Sep 17 00:00:00 2001 From: Sakura Ryoko Date: Sat, 14 Sep 2024 16:44:40 -0400 Subject: [PATCH 4/4] add missing lang entries (needs translations) --- src/main/resources/assets/itemscroller/lang/es_es.json | 6 ++++++ src/main/resources/assets/itemscroller/lang/zh_cn.json | 6 ++++++ src/main/resources/assets/itemscroller/lang/zh_tw.json | 6 ++++++ 3 files changed, 18 insertions(+) diff --git a/src/main/resources/assets/itemscroller/lang/es_es.json b/src/main/resources/assets/itemscroller/lang/es_es.json index 75aa107c2..1f9c829fe 100644 --- a/src/main/resources/assets/itemscroller/lang/es_es.json +++ b/src/main/resources/assets/itemscroller/lang/es_es.json @@ -22,6 +22,9 @@ "itemscroller.config.generic.name.sortInventoryToggle": "sortInventoryToggle", "itemscroller.config.generic.name.sortAssumeEmptyBoxStacks": "sortAssumeEmptyBoxStacks", "itemscroller.config.generic.name.sortShulkerBoxesAtEnd": "sortShulkerBoxesAtEnd", + "itemscroller.config.generic.name.sortShulkerBoxesInverted": "sortShulkerBoxesInverted", + "itemscroller.config.generic.name.sortBundlesAtEnd": "sortBundlesAtEnd", + "itemscroller.config.generic.name.sortBundlesInverted": "sortBundlesInverted", "itemscroller.config.generic.name.sortTopPriorityInventory": "sortTopPriorityInventory", "itemscroller.config.generic.name.sortBottomPriorityInventory": "sortBottomPriorityInventory", "itemscroller.config.generic.name.sortMethodDefault": "sortMethodDefault", @@ -50,6 +53,9 @@ "itemscroller.config.generic.comment.sortInventoryToggle": "Enables the Quick Inventory Sorting feature.\nThis is activated by hovering over any item,\nand then pressing the bound §a\"sortInventory\"§r key bind.", "itemscroller.config.generic.comment.sortAssumeEmptyBoxStacks": "Assume that empty boxes are stacking items\nwhen sorting the inventory.\nThis is useful if you installed mods to stack\nshulker boxes on remote server.\nThis will send an extra packet to ensure the\ninventory is synced.", "itemscroller.config.generic.comment.sortShulkerBoxesAtEnd": "Sort shulker boxes at the end of the inventory\nwhen sorting the inventory.", + "itemscroller.config.generic.comment.sortShulkerBoxesInverted": "Sort shulker boxes so that the most-empty\nboxes are first, and the full boxes are last.", + "itemscroller.config.generic.comment.sortBundlesAtEnd": "Sort bundles at the end of the inventory\nwhen sorting the inventory.", + "itemscroller.config.generic.comment.sortBundlesInverted": "Sort bundles so that the most-empty\nbundles are first, and the full bundles are last.", "itemscroller.config.generic.comment.sortTopPriorityInventory": "Estos ítems tendrán prioridad para ser ordenados primero.\nPuedes poner IDs de ítems (ej. \"minecraft:diamond_sword\") o nombres personalizados (ej. \"Matador de Withers\").\nSi pones nombres personalizados, estos serán preferidos antes que los IDs.\nLos ítems en la lista se ordenarán al frente según la lista.", "itemscroller.config.generic.comment.sortBottomPriorityInventory": "Estos ítems tendrán prioridad para ser ordenados al final.\nPuedes poner IDs de ítems (ej. \"minecraft:diamond_sword\") o nombres personalizados (ej. \"Matador de Withers\").\nSi pones nombres personalizados, estos serán preferidos antes que los IDs.\nLos ítems en la lista se ordenarán al final según la lista.", "itemscroller.config.generic.comment.sortMethodDefault": "The default sorting method used to compare items.\n\n§6Category [ANY]§r = Sort by §a\"sortCategoryOrder\"§r first, and then followed by:\n§6Item Name§r - Sort by Items' Name\n§6Item Count§r - Sort by Item's Stack Size, then by RawID\n§6Item Rarity§r - Sort by Rarity, then by RawID\n§6Item RawID§r - Sort by RawID\n§d(RawID is the built-in Vanilla Item Index value)§r\n\n§cData Components are compared by Hash Code afterwards, and then\n§cby Item Stack Size after all other checks have passed.§r", diff --git a/src/main/resources/assets/itemscroller/lang/zh_cn.json b/src/main/resources/assets/itemscroller/lang/zh_cn.json index 7c42330e6..5ce19ac98 100644 --- a/src/main/resources/assets/itemscroller/lang/zh_cn.json +++ b/src/main/resources/assets/itemscroller/lang/zh_cn.json @@ -22,6 +22,9 @@ "itemscroller.config.generic.name.sortInventoryToggle": "sortInventoryToggle", "itemscroller.config.generic.name.sortAssumeEmptyBoxStacks": "sortAssumeEmptyBoxStacks", "itemscroller.config.generic.name.sortShulkerBoxesAtEnd": "sortShulkerBoxesAtEnd", + "itemscroller.config.generic.name.sortShulkerBoxesInverted": "sortShulkerBoxesInverted", + "itemscroller.config.generic.name.sortBundlesAtEnd": "sortBundlesAtEnd", + "itemscroller.config.generic.name.sortBundlesInverted": "sortBundlesInverted", "itemscroller.config.generic.name.sortTopPriorityInventory": "优先排序物品清单", "itemscroller.config.generic.name.sortBottomPriorityInventory": "最后排序物品清单", "itemscroller.config.generic.name.sortMethodDefault": "sortMethodDefault", @@ -50,6 +53,9 @@ "itemscroller.config.generic.comment.sortInventoryToggle": "Enables the Quick Inventory Sorting feature.\nThis is activated by hovering over any item,\nand then pressing the bound §a\"sortInventory\"§r key bind.", "itemscroller.config.generic.comment.sortAssumeEmptyBoxStacks": "整理物品栏时假设空潜影盒可以堆叠。\n当你在远程服务器启用此特性时会很有用。\n这会多发送一个网络包来确保服务器同步", "itemscroller.config.generic.comment.sortShulkerBoxesAtEnd": "整理物品栏时将潜影盒放在最后。", + "itemscroller.config.generic.comment.sortShulkerBoxesInverted": "Sort shulker boxes so that the most-empty\nboxes are first, and the full boxes are last.", + "itemscroller.config.generic.comment.sortBundlesAtEnd": "Sort bundles at the end of the inventory\nwhen sorting the inventory.", + "itemscroller.config.generic.comment.sortBundlesInverted": "Sort bundles so that the most-empty\nbundles are first, and the full bundles are last.", "itemscroller.config.generic.comment.sortTopPriorityInventory": "这些物品将优先排序。\n你可以输入物品ID(例如 \"minecraft:diamond_sword\")或自定义名称(例如 \"凋零杀手\")。\n如果输入了自定义名称,则优先使用自定义名称。\n列表中的物品将根据列表顺序排在前面。", "itemscroller.config.generic.comment.sortBottomPriorityInventory": "这些物品将最后排序。\n你可以输入物品ID(例如 \"minecraft:diamond_sword\")或自定义名称(例如 \"凋零杀手\")。\n如果输入了自定义名称,则优先使用自定义名称。\n列表中的物品将根据列表顺序排在后面。", "itemscroller.config.generic.comment.sortMethodDefault": "The default sorting method used to compare items.\n\n§6Category [ANY]§r = Sort by §a\"sortCategoryOrder\"§r first, and then followed by:\n§6Item Name§r - Sort by Items' Name\n§6Item Count§r - Sort by Item's Stack Size, then by RawID\n§6Item Rarity§r - Sort by Rarity, then by RawID\n§6Item RawID§r - Sort by RawID\n§d(RawID is the built-in Vanilla Item Index value)§r\n\n§cData Components are compared by Hash Code afterwards, and then\n§cby Item Stack Size after all other checks have passed.§r", diff --git a/src/main/resources/assets/itemscroller/lang/zh_tw.json b/src/main/resources/assets/itemscroller/lang/zh_tw.json index 94a01249b..2f1f2e944 100644 --- a/src/main/resources/assets/itemscroller/lang/zh_tw.json +++ b/src/main/resources/assets/itemscroller/lang/zh_tw.json @@ -22,6 +22,9 @@ "itemscroller.config.generic.name.sortInventoryToggle": "sortInventoryToggle", "itemscroller.config.generic.name.sortAssumeEmptyBoxStacks": "嘗試堆疊空界伏盒", "itemscroller.config.generic.name.sortShulkerBoxesAtEnd": "界伏盒置底", + "itemscroller.config.generic.name.sortShulkerBoxesInverted": "sortShulkerBoxesInverted", + "itemscroller.config.generic.name.sortBundlesAtEnd": "sortBundlesAtEnd", + "itemscroller.config.generic.name.sortBundlesInverted": "sortBundlesInverted", "itemscroller.config.generic.name.sortTopPriorityInventory": "優先排序物品清單", "itemscroller.config.generic.name.sortBottomPriorityInventory": "最後排序物品清單", "itemscroller.config.generic.name.sortMethodDefault": "sortMethodDefault", @@ -50,6 +53,9 @@ "itemscroller.config.generic.comment.sortInventoryToggle": "Enables the Quick Inventory Sorting feature.\nThis is activated by hovering over any item,\nand then pressing the bound §a\"sortInventory\"§r key bind.", "itemscroller.config.generic.comment.sortAssumeEmptyBoxStacks": "整理物品欄時假設空界伏盒可以堆疊。\n當你在遠端伺服器啟用此特性時會很有用。\n這會多發送一個網路封包來確保伺服器同步", "itemscroller.config.generic.comment.sortShulkerBoxesAtEnd": "整理物品欄時將界伏盒放在最後。", + "itemscroller.config.generic.comment.sortShulkerBoxesInverted": "Sort shulker boxes so that the most-empty\nboxes are first, and the full boxes are last.", + "itemscroller.config.generic.comment.sortBundlesAtEnd": "Sort bundles at the end of the inventory\nwhen sorting the inventory.", + "itemscroller.config.generic.comment.sortBundlesInverted": "Sort bundles so that the most-empty\nbundles are first, and the full bundles are last.", "itemscroller.config.generic.comment.sortTopPriorityInventory": "這些物品將優先排序。\n你可以輸入物品ID(例如 \"minecraft:diamond_sword\")或自定義名稱(例如 \"凋零殺手\")。\n如果輸入了自定義名稱,則會優先使用自定義名稱。\n列表中的物品將根據列表順序排在前面。", "itemscroller.config.generic.comment.sortBottomPriorityInventory": "這些物品將最後排序。\n你可以輸入物品ID(例如 \"minecraft:diamond_sword\")或自定義名稱(例如 \"凋零殺手\")。\n如果輸入了自定義名稱,則會優先使用自定義名稱。\n列表中的物品將根據列表順序排在後面。", "itemscroller.config.generic.comment.sortMethodDefault": "The default sorting method used to compare items.\n\n§6Category [ANY]§r = Sort by §a\"sortCategoryOrder\"§r first, and then followed by:\n§6Item Name§r - Sort by Items' Name\n§6Item Count§r - Sort by Item's Stack Size, then by RawID\n§6Item Rarity§r - Sort by Rarity, then by RawID\n§6Item RawID§r - Sort by RawID\n§d(RawID is the built-in Vanilla Item Index value)§r\n\n§cData Components are compared by Hash Code afterwards, and then\n§cby Item Stack Size after all other checks have passed.§r",