From 0df40845e4c0deb83a985e20b89ac9e02a87888b Mon Sep 17 00:00:00 2001 From: Ethan Date: Tue, 3 Jun 2025 15:10:12 -0700 Subject: [PATCH 1/2] move changes into branch based off main --- build.gradle | 16 +- gradle.properties | 16 +- .../LightweightInventorySortingClient.java | 13 +- .../config/ButtonSize.java | 131 +++++---- ...nventorySortingConfig.java => Config.java} | 141 ++++----- .../config/ModMenu.java | 186 ++++++------ .../config/SortTypes.java | 94 +++--- ....java => GenericContainerScreenMixin.java} | 131 +++++---- ...yScreen.java => InventoryScreenMixin.java} | 152 +++++----- ...Screen.java => ShulkerBoxScreenMixin.java} | 130 ++++----- .../sorting/ClickOperation.java | 91 ++++++ .../sorting/HandHelper.java | 19 -- .../SortButton.java} | 118 ++++---- .../sorting/SortComparator.java | 12 + .../sorting/SortSnapshotClientside.java | 55 ++++ .../sorting/SortableSlot.java | 49 ---- .../sorting/SortableSlotComparator.java | 11 - .../sorting/Sorter.java | 270 ++++++++++++++++++ .../sorting/SortingHelper.java | 226 --------------- .../LightweightInventorySorting.java | 35 ++- .../SortSnapshotServerside.java | 64 +++++ .../lang/en_us.json | 2 +- src/main/resources/fabric.mod.json | 8 +- 23 files changed, 1072 insertions(+), 898 deletions(-) rename src/client/java/borknbeans/lightweightinventorysorting/config/{LightweightInventorySortingConfig.java => Config.java} (87%) rename src/client/java/borknbeans/lightweightinventorysorting/mixin/client/{MixinGenericContainerScreen.java => GenericContainerScreenMixin.java} (59%) rename src/client/java/borknbeans/lightweightinventorysorting/mixin/client/{MixinInventoryScreen.java => InventoryScreenMixin.java} (51%) rename src/client/java/borknbeans/lightweightinventorysorting/mixin/client/{MixinShulkerBoxScreen.java => ShulkerBoxScreenMixin.java} (60%) create mode 100644 src/client/java/borknbeans/lightweightinventorysorting/sorting/ClickOperation.java delete mode 100644 src/client/java/borknbeans/lightweightinventorysorting/sorting/HandHelper.java rename src/client/java/borknbeans/lightweightinventorysorting/{ContainerSortButton.java => sorting/SortButton.java} (53%) create mode 100644 src/client/java/borknbeans/lightweightinventorysorting/sorting/SortComparator.java create mode 100644 src/client/java/borknbeans/lightweightinventorysorting/sorting/SortSnapshotClientside.java delete mode 100644 src/client/java/borknbeans/lightweightinventorysorting/sorting/SortableSlot.java delete mode 100644 src/client/java/borknbeans/lightweightinventorysorting/sorting/SortableSlotComparator.java create mode 100644 src/client/java/borknbeans/lightweightinventorysorting/sorting/Sorter.java delete mode 100644 src/client/java/borknbeans/lightweightinventorysorting/sorting/SortingHelper.java create mode 100644 src/main/java/borknbeans/lightweightinventorysorting/SortSnapshotServerside.java diff --git a/build.gradle b/build.gradle index ecd2d81..8f7d7ee 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'fabric-loom' version '1.7-SNAPSHOT' + id 'fabric-loom' version "${loom_version}" id 'maven-publish' } @@ -11,12 +11,6 @@ base { } repositories { - // Add repositories to retrieve artifacts from in here. - // You should only use this when depending on other mods because - // Loom adds the essential maven repositories to download Minecraft and libraries from automatically. - // See https://docs.gradle.org/current/userguide/declaring_repositories.html - // for more information about repositories. - maven { name = "Terraformers" url = "https://maven.terraformersmc.com/" @@ -46,7 +40,7 @@ dependencies { // Fabric API. This is technically optional, but you probably want it anyway. modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" - + modImplementation("com.terraformersmc:modmenu:${project.modmenu_version}") modApi("me.shedaniel.cloth:cloth-config-fabric:${project.cloth_version}") { @@ -58,7 +52,7 @@ processResources { inputs.property "version", project.version filesMatching("fabric.mod.json") { - expand "version": project.version + expand "version": inputs.properties.version } } @@ -77,8 +71,10 @@ java { } jar { + inputs.property "archivesName", project.base.archivesName + from("LICENSE") { - rename { "${it}_${project.base.archivesName.get()}"} + rename { "${it}_${inputs.properties.archivesName}"} } } diff --git a/gradle.properties b/gradle.properties index bbb621e..b09104c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,16 +4,18 @@ org.gradle.parallel=true # Fabric Properties # check these on https://fabricmc.net/develop -minecraft_version=1.21.2 -yarn_mappings=1.21.2+build.1 -loader_version=0.16.9 + +minecraft_version=1.21.4 +yarn_mappings=1.21.4+build.8 +loader_version=0.16.14 +loom_version=1.10-SNAPSHOT # Mod Properties -mod_version=1.1.3+1.21.2 +mod_version=1.1.4+1.21.4 maven_group=borknbeans.lightweightinventorysorting archives_base_name=lightweight-inventory-sorting # Dependencies -fabric_version=0.106.1+1.21.2 -modmenu_version=12.0.0 -cloth_version=16.0.143 \ No newline at end of file +fabric_version=0.119.2+1.21.4 +modmenu_version=13.0.3 +cloth_version=17.0.144 \ No newline at end of file diff --git a/src/client/java/borknbeans/lightweightinventorysorting/LightweightInventorySortingClient.java b/src/client/java/borknbeans/lightweightinventorysorting/LightweightInventorySortingClient.java index 463ae08..061fe28 100644 --- a/src/client/java/borknbeans/lightweightinventorysorting/LightweightInventorySortingClient.java +++ b/src/client/java/borknbeans/lightweightinventorysorting/LightweightInventorySortingClient.java @@ -1,6 +1,6 @@ package borknbeans.lightweightinventorysorting; -import borknbeans.lightweightinventorysorting.config.LightweightInventorySortingConfig; +import borknbeans.lightweightinventorysorting.config.Config; import net.fabricmc.api.ClientModInitializer; import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; import net.minecraft.client.option.KeyBinding; @@ -13,17 +13,16 @@ public class LightweightInventorySortingClient implements ClientModInitializer { @Override public void onInitializeClient() { - LightweightInventorySortingConfig.load(); - + Config.load(); registerKeyBindings(); } private void registerKeyBindings() { sortKeyBind = KeyBindingHelper.registerKeyBinding(new KeyBinding( - "key.lightweight-inventory-sorting.sort", - InputUtil.Type.KEYSYM, - GLFW.GLFW_KEY_R, - "category.lightweight-inventory-sorting.title" + "key.lightweight-inventory-sorting.sort", + InputUtil.Type.KEYSYM, + GLFW.GLFW_KEY_R, + "category.lightweight-inventory-sorting.title" )); } } \ No newline at end of file diff --git a/src/client/java/borknbeans/lightweightinventorysorting/config/ButtonSize.java b/src/client/java/borknbeans/lightweightinventorysorting/config/ButtonSize.java index 5c5f6a5..278b312 100644 --- a/src/client/java/borknbeans/lightweightinventorysorting/config/ButtonSize.java +++ b/src/client/java/borknbeans/lightweightinventorysorting/config/ButtonSize.java @@ -1,66 +1,65 @@ -package borknbeans.lightweightinventorysorting.config; - -import borknbeans.lightweightinventorysorting.LightweightInventorySorting; -import net.minecraft.util.Identifier; - -public enum ButtonSize { - SMALL, - MEDIUM, - LARGE; - - public int getButtonSize() { - switch (this) { - case SMALL: - return 6; - case MEDIUM: - return 9; - case LARGE: - return 12; - } - - return 0; - } - - public Identifier getButtonTexture() { - String fileName = "sort_button_large"; - switch (this) { - case SMALL: - fileName = "sort_button_small"; - break; - case MEDIUM: - fileName = "sort_button_medium"; - break; - case LARGE: - fileName = "sort_button_large"; - break; - } - - if (LightweightInventorySortingConfig.sortType == SortTypes.REVERSE_ALPHANUMERIC) { - fileName += "_z"; - } - - return Identifier.of(LightweightInventorySorting.MOD_ID, fileName); - } - - public Identifier getButtonHoverTexture() { - String fileName = "sort_button_large_hover"; - switch (this) { - case SMALL: - fileName = "sort_button_small_hover"; - break; - case MEDIUM: - fileName = "sort_button_medium_hover"; - break; - case LARGE: - fileName = "sort_button_large_hover"; - break; - } - - if (LightweightInventorySortingConfig.sortType == SortTypes.REVERSE_ALPHANUMERIC) { - fileName += "_z"; - } - - return Identifier.of(LightweightInventorySorting.MOD_ID, fileName); - } - -} +package borknbeans.lightweightinventorysorting.config; + +import borknbeans.lightweightinventorysorting.LightweightInventorySorting; +import net.minecraft.util.Identifier; + +public enum ButtonSize { + SMALL, + MEDIUM, + LARGE; + + public int getButtonSize() { + switch (this) { + case SMALL: + return 6; + case MEDIUM: + return 9; + case LARGE: + return 12; + } + + return 0; + } + + public Identifier getButtonTexture() { + String fileName = "sort_button_large"; + switch (this) { + case SMALL: + fileName = "sort_button_small"; + break; + case MEDIUM: + fileName = "sort_button_medium"; + break; + case LARGE: + fileName = "sort_button_large"; + break; + } + + if (Config.sortType == SortTypes.REVERSE_ALPHANUMERIC) { + fileName += "_z"; + } + + return Identifier.of(LightweightInventorySorting.MOD_ID, fileName); + } + + public Identifier getButtonHoverTexture() { + String fileName = "sort_button_large_hover"; + switch (this) { + case SMALL: + fileName = "sort_button_small_hover"; + break; + case MEDIUM: + fileName = "sort_button_medium_hover"; + break; + case LARGE: + fileName = "sort_button_large_hover"; + break; + } + + if (Config.sortType == SortTypes.REVERSE_ALPHANUMERIC) { + fileName += "_z"; + } + + return Identifier.of(LightweightInventorySorting.MOD_ID, fileName); + } +} diff --git a/src/client/java/borknbeans/lightweightinventorysorting/config/LightweightInventorySortingConfig.java b/src/client/java/borknbeans/lightweightinventorysorting/config/Config.java similarity index 87% rename from src/client/java/borknbeans/lightweightinventorysorting/config/LightweightInventorySortingConfig.java rename to src/client/java/borknbeans/lightweightinventorysorting/config/Config.java index e1af09c..ca0a81e 100644 --- a/src/client/java/borknbeans/lightweightinventorysorting/config/LightweightInventorySortingConfig.java +++ b/src/client/java/borknbeans/lightweightinventorysorting/config/Config.java @@ -1,69 +1,72 @@ -package borknbeans.lightweightinventorysorting.config; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import net.fabricmc.loader.api.FabricLoader; - -import java.io.File; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.IOException; - -public class LightweightInventorySortingConfig { - - private static final File CONFIG_FILE = new File(FabricLoader.getInstance().getConfigDir().toFile(), "lightweight-inventory-sorting.json"); - private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); - - public static SortTypes sortType = SortTypes.ALPHANUMERIC; - - public static ButtonSize buttonSize = ButtonSize.LARGE; - public static int xOffsetInventory = 0; - public static int yOffsetInventory = 0; - public static int xOffsetContainer = 0; - public static int yOffsetContainer = 0; - - public static int sortDelay = 0; - - public static void load() { - if (CONFIG_FILE.exists()) { - try (FileReader reader = new FileReader(CONFIG_FILE)) { - ConfigData data = GSON.fromJson(reader, ConfigData.class); - sortType = data.sortType == null ? SortTypes.ALPHANUMERIC : data.sortType; - buttonSize = data.buttonSize == null ? ButtonSize.MEDIUM : data.buttonSize; - xOffsetInventory = data.xOffsetInventory; - yOffsetInventory = data.yOffsetInventory; - xOffsetContainer = data.xOffsetContainer; - yOffsetContainer = data.yOffsetContainer; - sortDelay = data.sortDelay; - } catch (IOException e) { - e.printStackTrace(); - } - } - } - - public static void save() { - try (FileWriter writer = new FileWriter(CONFIG_FILE)) { - ConfigData data = new ConfigData(); - data.sortType = sortType; - data.buttonSize = buttonSize; - data.xOffsetInventory = xOffsetInventory; - data.yOffsetInventory = yOffsetInventory; - data.xOffsetContainer = xOffsetContainer; - data.yOffsetContainer = yOffsetContainer; - data.sortDelay = sortDelay; - GSON.toJson(data, writer); - } catch (IOException e) { - e.printStackTrace(); - } - } - - private static class ConfigData { - SortTypes sortType; - ButtonSize buttonSize; - int xOffsetInventory; - int yOffsetInventory; - int xOffsetContainer; - int yOffsetContainer; - int sortDelay; - } -} +package borknbeans.lightweightinventorysorting.config; + +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import borknbeans.lightweightinventorysorting.LightweightInventorySorting; +import net.fabricmc.loader.api.FabricLoader; + +public class Config { + private static final File CONFIG_FILE = new File(FabricLoader.getInstance().getConfigDir().toFile(), "lightweight-inventory-sorting.json"); + private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); + + + public static SortTypes sortType = SortTypes.ALPHANUMERIC; + public static ButtonSize buttonSize = ButtonSize.LARGE; + + public static int xOffsetInventory = 0; + public static int yOffsetInventory = 0; + public static int xOffsetContainer = 0; + public static int yOffsetContainer = 0; + + public static int sortDelay = 0; + + private static class ConfigData { + SortTypes sortType; + ButtonSize buttonSize; + int xOffsetInventory; + int yOffsetInventory; + int xOffsetContainer; + int yOffsetContainer; + int sortDelay; + } + + public static void load() { + if (CONFIG_FILE.exists()) { + try (FileReader reader = new FileReader(CONFIG_FILE)) { + ConfigData data = GSON.fromJson(reader, ConfigData.class); + sortType = data.sortType == null ? SortTypes.ALPHANUMERIC : data.sortType; + buttonSize = data.buttonSize == null ? ButtonSize.MEDIUM : data.buttonSize; + xOffsetInventory = data.xOffsetInventory; + yOffsetInventory = data.yOffsetInventory; + xOffsetContainer = data.xOffsetContainer; + yOffsetContainer = data.yOffsetContainer; + sortDelay = data.sortDelay; + } catch (IOException e) { + LightweightInventorySorting.LOGGER.error("Failed to load config file (resetting to default): {}", e.getMessage()); + save(); + } + } + } + + public static void save() { + try (FileWriter writer = new FileWriter(CONFIG_FILE)) { + ConfigData data = new ConfigData(); + data.sortType = sortType; + data.buttonSize = buttonSize; + data.xOffsetInventory = xOffsetInventory; + data.yOffsetInventory = yOffsetInventory; + data.xOffsetContainer = xOffsetContainer; + data.yOffsetContainer = yOffsetContainer; + data.sortDelay = sortDelay; + GSON.toJson(data, writer); + } catch (IOException e) { + LightweightInventorySorting.LOGGER.error("Failed to save config file: {}", e.getMessage()); + } + } +} diff --git a/src/client/java/borknbeans/lightweightinventorysorting/config/ModMenu.java b/src/client/java/borknbeans/lightweightinventorysorting/config/ModMenu.java index 7082e8c..756af72 100644 --- a/src/client/java/borknbeans/lightweightinventorysorting/config/ModMenu.java +++ b/src/client/java/borknbeans/lightweightinventorysorting/config/ModMenu.java @@ -1,93 +1,93 @@ -package borknbeans.lightweightinventorysorting.config; - -import com.terraformersmc.modmenu.api.ConfigScreenFactory; -import com.terraformersmc.modmenu.api.ModMenuApi; -import me.shedaniel.clothconfig2.api.ConfigBuilder; -import me.shedaniel.clothconfig2.api.ConfigCategory; -import me.shedaniel.clothconfig2.api.ConfigEntryBuilder; -import net.minecraft.client.gui.screen.Screen; -import net.minecraft.text.Text; - -public class ModMenu implements ModMenuApi { - - @Override - public ConfigScreenFactory getModConfigScreenFactory() { - return parent -> createConfigScreen(parent); - } - - private Screen createConfigScreen(Screen parent) { - ConfigBuilder builder = ConfigBuilder.create() - .setParentScreen(parent) - .setTitle(Text.translatable("category.lightweight-inventory-sorting.title")); - ConfigEntryBuilder entryBuilder = builder.entryBuilder(); - - ConfigCategory generalSettings = builder.getOrCreateCategory(Text.translatable("category.lightweight-inventory-sorting.general")); - - generalSettings.addEntry(entryBuilder.startTextDescription(Text.translatable("category.lightweight-inventory-sorting.sort-options")) - .build()); - - generalSettings.addEntry(entryBuilder.startEnumSelector( - Text.translatable("category.lightweight-inventory-sorting.sort-type"), - SortTypes.class, - LightweightInventorySortingConfig.sortType - ).setDefaultValue(SortTypes.ALPHANUMERIC) - .setSaveConsumer(newValue -> LightweightInventorySortingConfig.sortType = newValue) - .setTooltip(Text.translatable("category.lightweight-inventory-sorting.sort-type-tooltip")) - .build()); - - generalSettings.addEntry(entryBuilder.startIntField( - Text.translatable("category.lightweight-inventory-sorting.sort-delay"), - LightweightInventorySortingConfig.sortDelay - ).setDefaultValue(0) - .setSaveConsumer(newValue -> LightweightInventorySortingConfig.sortDelay = newValue) - .setTooltip(Text.translatable("category.lightweight-inventory-sorting.sort-delay-tooltip")) - .build()); - - generalSettings.addEntry(entryBuilder.startTextDescription(Text.translatable("category.lightweight-inventory-sorting.button-options")) - .build()); - - generalSettings.addEntry(entryBuilder.startEnumSelector( - Text.translatable("category.lightweight-inventory-sorting.button-size"), - ButtonSize.class, - LightweightInventorySortingConfig.buttonSize - ).setDefaultValue(ButtonSize.LARGE) - .setSaveConsumer(newValue -> LightweightInventorySortingConfig.buttonSize = newValue) - .build()); - - generalSettings.addEntry(entryBuilder.startIntField( - Text.translatable("category.lightweight-inventory-sorting.inventory-x"), - LightweightInventorySortingConfig.xOffsetInventory - ).setDefaultValue(0) - .setSaveConsumer(newValue -> LightweightInventorySortingConfig.xOffsetInventory = newValue) - .setTooltip(Text.translatable("category.lightweight-inventory-sorting.inventory-x-tooltip")) - .build()); - - generalSettings.addEntry(entryBuilder.startIntField( - Text.translatable("category.lightweight-inventory-sorting.inventory-y"), - LightweightInventorySortingConfig.yOffsetInventory - ).setDefaultValue(0) - .setSaveConsumer(newValue -> LightweightInventorySortingConfig.yOffsetInventory = newValue) - .setTooltip(Text.translatable("category.lightweight-inventory-sorting.inventory-y-tooltip")) - .build()); - - generalSettings.addEntry(entryBuilder.startIntField( - Text.translatable("category.lightweight-inventory-sorting.container-x"), - LightweightInventorySortingConfig.xOffsetContainer - ).setDefaultValue(0) - .setSaveConsumer(newValue -> LightweightInventorySortingConfig.xOffsetContainer = newValue) - .setTooltip(Text.translatable("category.lightweight-inventory-sorting.container-x-tooltip")) - .build()); - - generalSettings.addEntry(entryBuilder.startIntField( - Text.translatable("category.lightweight-inventory-sorting.container-y"), - LightweightInventorySortingConfig.yOffsetContainer - ).setDefaultValue(0) - .setSaveConsumer(newValue -> LightweightInventorySortingConfig.yOffsetContainer = newValue) - .setTooltip(Text.translatable("category.lightweight-inventory-sorting.container-y-tooltip")) - .build()); - - builder.setSavingRunnable(() -> LightweightInventorySortingConfig.save()); - - return builder.build(); - } -} +package borknbeans.lightweightinventorysorting.config; + +import com.terraformersmc.modmenu.api.ConfigScreenFactory; +import com.terraformersmc.modmenu.api.ModMenuApi; +import me.shedaniel.clothconfig2.api.ConfigBuilder; +import me.shedaniel.clothconfig2.api.ConfigCategory; +import me.shedaniel.clothconfig2.api.ConfigEntryBuilder; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.text.Text; + +public class ModMenu implements ModMenuApi { + + @Override + public ConfigScreenFactory getModConfigScreenFactory() { + return parent -> createConfigScreen(parent); + } + + private Screen createConfigScreen(Screen parent) { + ConfigBuilder builder = ConfigBuilder.create() + .setParentScreen(parent) + .setTitle(Text.translatable("category.lightweight-inventory-sorting.title")); + ConfigEntryBuilder entryBuilder = builder.entryBuilder(); + + ConfigCategory generalSettings = builder.getOrCreateCategory(Text.translatable("category.lightweight-inventory-sorting.general")); + + generalSettings.addEntry(entryBuilder.startTextDescription(Text.translatable("category.lightweight-inventory-sorting.sort-options")) + .build()); + + generalSettings.addEntry(entryBuilder.startEnumSelector( + Text.translatable("category.lightweight-inventory-sorting.sort-type"), + SortTypes.class, + Config.sortType + ).setDefaultValue(SortTypes.ALPHANUMERIC) + .setSaveConsumer(newValue -> Config.sortType = newValue) + .setTooltip(Text.translatable("category.lightweight-inventory-sorting.sort-type-tooltip")) + .build()); + + generalSettings.addEntry(entryBuilder.startIntField( + Text.translatable("category.lightweight-inventory-sorting.sort-delay"), + Config.sortDelay + ).setDefaultValue(0) + .setSaveConsumer(newValue -> Config.sortDelay = newValue) + .setTooltip(Text.translatable("category.lightweight-inventory-sorting.sort-delay-tooltip")) + .build()); + + generalSettings.addEntry(entryBuilder.startTextDescription(Text.translatable("category.lightweight-inventory-sorting.button-options")) + .build()); + + generalSettings.addEntry(entryBuilder.startEnumSelector( + Text.translatable("category.lightweight-inventory-sorting.button-size"), + ButtonSize.class, + Config.buttonSize + ).setDefaultValue(ButtonSize.LARGE) + .setSaveConsumer(newValue -> Config.buttonSize = newValue) + .build()); + + generalSettings.addEntry(entryBuilder.startIntField( + Text.translatable("category.lightweight-inventory-sorting.inventory-x"), + Config.xOffsetInventory + ).setDefaultValue(0) + .setSaveConsumer(newValue -> Config.xOffsetInventory = newValue) + .setTooltip(Text.translatable("category.lightweight-inventory-sorting.inventory-x-tooltip")) + .build()); + + generalSettings.addEntry(entryBuilder.startIntField( + Text.translatable("category.lightweight-inventory-sorting.inventory-y"), + Config.yOffsetInventory + ).setDefaultValue(0) + .setSaveConsumer(newValue -> Config.yOffsetInventory = newValue) + .setTooltip(Text.translatable("category.lightweight-inventory-sorting.inventory-y-tooltip")) + .build()); + + generalSettings.addEntry(entryBuilder.startIntField( + Text.translatable("category.lightweight-inventory-sorting.container-x"), + Config.xOffsetContainer + ).setDefaultValue(0) + .setSaveConsumer(newValue -> Config.xOffsetContainer = newValue) + .setTooltip(Text.translatable("category.lightweight-inventory-sorting.container-x-tooltip")) + .build()); + + generalSettings.addEntry(entryBuilder.startIntField( + Text.translatable("category.lightweight-inventory-sorting.container-y"), + Config.yOffsetContainer + ).setDefaultValue(0) + .setSaveConsumer(newValue -> Config.yOffsetContainer = newValue) + .setTooltip(Text.translatable("category.lightweight-inventory-sorting.container-y-tooltip")) + .build()); + + builder.setSavingRunnable(() -> Config.save()); + + return builder.build(); + } +} diff --git a/src/client/java/borknbeans/lightweightinventorysorting/config/SortTypes.java b/src/client/java/borknbeans/lightweightinventorysorting/config/SortTypes.java index 1348f63..863d7b7 100644 --- a/src/client/java/borknbeans/lightweightinventorysorting/config/SortTypes.java +++ b/src/client/java/borknbeans/lightweightinventorysorting/config/SortTypes.java @@ -1,53 +1,41 @@ -package borknbeans.lightweightinventorysorting.config; - -import net.minecraft.item.Item; -import net.minecraft.item.ItemStack; - -public enum SortTypes { - ALPHANUMERIC, - REVERSE_ALPHANUMERIC, - RAW_ID; - - public int compare(ItemStack left, ItemStack right) { - switch (this) { - case ALPHANUMERIC: - return alphanumeric(left, right); - case REVERSE_ALPHANUMERIC: - return reverseAlphanumeric(left, right); - case RAW_ID: - return rawId(left, right); - } - - return 0; - } - - private int alphanumeric(ItemStack left, ItemStack right) { - if (left.isEmpty() && !right.isEmpty()) { - return 0; - } else if (right.isEmpty() && !left.isEmpty()) { - return -1; - } - - return (left.getName().getString().compareTo(right.getName().getString())); - } - - private int reverseAlphanumeric(ItemStack left, ItemStack right) { - if (left.isEmpty() && !right.isEmpty()) { - return 0; - } else if (right.isEmpty() && !left.isEmpty()) { - return -1; - } - - return (left.getName().getString().compareTo(right.getName().getString())) * -1; - } - - private int rawId(ItemStack left, ItemStack right) { - if (left.isEmpty() && !right.isEmpty()) { - return 0; - } else if (right.isEmpty() && !left.isEmpty()) { - return -1; - } - - return Integer.compare(Item.getRawId(left.getItem()), Item.getRawId(right.getItem())); - } -} +package borknbeans.lightweightinventorysorting.config; + +import net.minecraft.item.ItemStack; + +public enum SortTypes { + ALPHANUMERIC, + REVERSE_ALPHANUMERIC; + + public int compare(ItemStack left, ItemStack right) { + switch (this) { + case ALPHANUMERIC: + return alphanumeric(left, right); + case REVERSE_ALPHANUMERIC: + return reverseAlphanumeric(left, right); + } + + return 0; + } + + private int alphanumeric(ItemStack left, ItemStack right) { + if (left.isEmpty() && !right.isEmpty()) { + return 1; + } else if (right.isEmpty() && !left.isEmpty()) { + return -1; + } + + var result = left.getName().getString().compareTo(right.getName().getString()); + return result == 0 ? right.getCount() - left.getCount() : result; + } + + private int reverseAlphanumeric(ItemStack left, ItemStack right) { + if (left.isEmpty() && !right.isEmpty()) { + return 1; + } else if (right.isEmpty() && !left.isEmpty()) { + return -1; + } + + var result = left.getName().getString().compareTo(right.getName().getString()) * -1; + return result == 0 ? left.getCount() - right.getCount() : result; + } +} diff --git a/src/client/java/borknbeans/lightweightinventorysorting/mixin/client/MixinGenericContainerScreen.java b/src/client/java/borknbeans/lightweightinventorysorting/mixin/client/GenericContainerScreenMixin.java similarity index 59% rename from src/client/java/borknbeans/lightweightinventorysorting/mixin/client/MixinGenericContainerScreen.java rename to src/client/java/borknbeans/lightweightinventorysorting/mixin/client/GenericContainerScreenMixin.java index 7f4b52d..7695598 100644 --- a/src/client/java/borknbeans/lightweightinventorysorting/mixin/client/MixinGenericContainerScreen.java +++ b/src/client/java/borknbeans/lightweightinventorysorting/mixin/client/GenericContainerScreenMixin.java @@ -1,66 +1,65 @@ -package borknbeans.lightweightinventorysorting.mixin.client; - -import borknbeans.lightweightinventorysorting.ContainerSortButton; -import borknbeans.lightweightinventorysorting.LightweightInventorySorting; -import borknbeans.lightweightinventorysorting.LightweightInventorySortingClient; -import borknbeans.lightweightinventorysorting.config.LightweightInventorySortingConfig; -import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.gui.screen.ingame.GenericContainerScreen; -import net.minecraft.client.gui.screen.ingame.HandledScreen; -import net.minecraft.entity.player.PlayerInventory; -import net.minecraft.screen.GenericContainerScreenHandler; -import net.minecraft.text.Text; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Unique; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -@Mixin(GenericContainerScreen.class) -public abstract class MixinGenericContainerScreen extends HandledScreen { - @Unique - private ContainerSortButton containerSortButton; - - public MixinGenericContainerScreen(GenericContainerScreenHandler handler, PlayerInventory inventory, Text title) { - super(handler, inventory, title); - } - - @Override - public void init() { - super.init(); - - // Initialize button - int x = this.x + this.backgroundWidth - 20 + LightweightInventorySortingConfig.xOffsetContainer; - int y = this.y + 4 + LightweightInventorySortingConfig.yOffsetContainer; - int size = LightweightInventorySortingConfig.buttonSize.getButtonSize(); - containerSortButton = new ContainerSortButton(x, y, size, size, Text.literal("S"), 0, getScreenHandler().slots.size() - 37, this); - - // Add button to the screen - this.addDrawableChild(containerSortButton); - } - - @Inject(method = "render", at = @At("RETURN")) - private void onRender(DrawContext context, int mouseX, int mouseY, float delta, CallbackInfo ci) { - if (containerSortButton != null) { - containerSortButton.render(context, mouseX, mouseY, delta); - } - } - - @Override - public boolean keyPressed(int keyCode, int scanCode, int modifiers) { - if (LightweightInventorySortingClient.sortKeyBind.matchesKey(keyCode, scanCode)) { - containerSortButton.onClick(0f, 0f); // Simulate a click - } - - return super.keyPressed(keyCode, scanCode, modifiers); - } - - @Override - public boolean mouseClicked(double mouseX, double mouseY, int button) { - if (LightweightInventorySortingClient.sortKeyBind.matchesMouse(button)) { - containerSortButton.onClick(0f, 0f); // Simulate a click - } - - return super.mouseClicked(mouseX, mouseY, button); - } -} +package borknbeans.lightweightinventorysorting.mixin.client; + +import borknbeans.lightweightinventorysorting.LightweightInventorySortingClient; +import borknbeans.lightweightinventorysorting.config.Config; +import borknbeans.lightweightinventorysorting.sorting.SortButton; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.screen.ingame.GenericContainerScreen; +import net.minecraft.client.gui.screen.ingame.HandledScreen; +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.screen.GenericContainerScreenHandler; +import net.minecraft.text.Text; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(GenericContainerScreen.class) +public abstract class GenericContainerScreenMixin extends HandledScreen { + @Unique + private SortButton sortButton; + + public GenericContainerScreenMixin(GenericContainerScreenHandler handler, PlayerInventory inventory, Text title) { + super(handler, inventory, title); + } + + @Override + public void init() { + super.init(); + + // Initialize button + int x = this.x + this.backgroundWidth - 20 + Config.xOffsetContainer; + int y = this.y + 4 + Config.yOffsetContainer; + int size = Config.buttonSize.getButtonSize(); + sortButton = new SortButton(x, y, size, size, Text.literal("S"), 0, getScreenHandler().slots.size() - 37); + + // Add button to the screen + this.addDrawableChild(sortButton); + } + + @Inject(method = "render", at = @At("RETURN")) + private void onRender(DrawContext context, int mouseX, int mouseY, float delta, CallbackInfo ci) { + if (sortButton != null) { + sortButton.render(context, mouseX, mouseY, delta); + } + } + + @Override + public boolean keyPressed(int keyCode, int scanCode, int modifiers) { + if (LightweightInventorySortingClient.sortKeyBind.matchesKey(keyCode, scanCode)) { + sortButton.onClick(0f, 0f); // Simulate a click + } + + return super.keyPressed(keyCode, scanCode, modifiers); + } + + @Override + public boolean mouseClicked(double mouseX, double mouseY, int button) { + if (LightweightInventorySortingClient.sortKeyBind.matchesMouse(button)) { + sortButton.onClick(0f, 0f); // Simulate a click + } + + return super.mouseClicked(mouseX, mouseY, button); + } +} diff --git a/src/client/java/borknbeans/lightweightinventorysorting/mixin/client/MixinInventoryScreen.java b/src/client/java/borknbeans/lightweightinventorysorting/mixin/client/InventoryScreenMixin.java similarity index 51% rename from src/client/java/borknbeans/lightweightinventorysorting/mixin/client/MixinInventoryScreen.java rename to src/client/java/borknbeans/lightweightinventorysorting/mixin/client/InventoryScreenMixin.java index 5cb9411..28fc109 100644 --- a/src/client/java/borknbeans/lightweightinventorysorting/mixin/client/MixinInventoryScreen.java +++ b/src/client/java/borknbeans/lightweightinventorysorting/mixin/client/InventoryScreenMixin.java @@ -1,84 +1,68 @@ -package borknbeans.lightweightinventorysorting.mixin.client; - -import borknbeans.lightweightinventorysorting.ContainerSortButton; -import borknbeans.lightweightinventorysorting.LightweightInventorySortingClient; -import borknbeans.lightweightinventorysorting.config.LightweightInventorySortingConfig; -import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.gui.screen.ingame.HandledScreen; -import net.minecraft.client.gui.screen.ingame.InventoryScreen; -import net.minecraft.entity.player.PlayerInventory; -import net.minecraft.screen.PlayerScreenHandler; -import net.minecraft.text.Text; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Unique; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; - -@Mixin(InventoryScreen.class) -public abstract class MixinInventoryScreen extends HandledScreen { - - @Unique - private ContainerSortButton inventorySortButton; - - - public MixinInventoryScreen(PlayerScreenHandler handler, PlayerInventory inventory, Text title) { - super(handler, inventory, title); - } - - @Inject(method = "init", at = @At("RETURN")) - private void onInit(CallbackInfo ci) { - int size = LightweightInventorySortingConfig.buttonSize.getButtonSize(); - - inventorySortButton = new ContainerSortButton(0, 0, size, size, Text.literal("S"), 9, 35, this); - - setButtonCoordinates(); - - // Add button to the screen - this.addDrawableChild(inventorySortButton); - } - - @Inject(method = "render", at = @At("RETURN")) - private void onRender(DrawContext context, int mouseX, int mouseY, float delta, CallbackInfo ci) { - if (inventorySortButton != null) { - setButtonCoordinates(); - - inventorySortButton.render(context, mouseX, mouseY, delta); - } - } - - // This injection is failing now in 1.21.2 - /* - @Inject(method = "keyPressed", at = @At("RETURN")) - public boolean onKeyPressed(int keyCode, int scanCode, int modifiers, CallbackInfoReturnable cir) { - if (LightweightInventorySortingClient.sortKeyBind.matchesKey(keyCode, scanCode)) { - inventorySortButton.onClick(0f, 0f); // Simulate a click - } - return cir.getReturnValue(); - } - */ - - // This override is NOT an ideal solution as it could lead to conflicts with other mods - @Override - public boolean keyPressed(int keyCode, int scanCode, int modifiers) { - if (LightweightInventorySortingClient.sortKeyBind.matchesKey(keyCode, scanCode)) { - inventorySortButton.onClick(0f, 0f); // Simulate a click - } - return super.keyPressed(keyCode, scanCode, modifiers); - } - - private void setButtonCoordinates() { - inventorySortButton.setX(this.x + this.backgroundWidth - 20 + LightweightInventorySortingConfig.xOffsetInventory); - inventorySortButton.setY(this.height / 2 - 15 + LightweightInventorySortingConfig.yOffsetInventory); - } - - @Override - public boolean mouseClicked(double mouseX, double mouseY, int button) { - if (LightweightInventorySortingClient.sortKeyBind.matchesMouse(button)) { - inventorySortButton.onClick(0f, 0f); // Simulate a click - } - - return super.mouseClicked(mouseX, mouseY, button); - } -} +package borknbeans.lightweightinventorysorting.mixin.client; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import borknbeans.lightweightinventorysorting.LightweightInventorySortingClient; +import borknbeans.lightweightinventorysorting.config.Config; +import borknbeans.lightweightinventorysorting.sorting.SortButton; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.screen.ingame.HandledScreen; +import net.minecraft.client.gui.screen.ingame.InventoryScreen; +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.screen.PlayerScreenHandler; +import net.minecraft.text.Text; + +@Mixin(InventoryScreen.class) +public abstract class InventoryScreenMixin extends HandledScreen { + + @Unique + private SortButton sortButton; + + public InventoryScreenMixin(PlayerScreenHandler handler, PlayerInventory inventory, Text title) { + super(handler, inventory, title); + } + + @Inject(method = "init", at = @At("RETURN")) + private void onInit(CallbackInfo ci) { + int size = Config.buttonSize.getButtonSize(); + sortButton = new SortButton(0, 0, size, size, Text.literal("S"), 9, 35); + setButtonCoordinates(); + + this.addDrawableChild(sortButton); + } + + @Inject(method = "render", at = @At("RETURN")) + private void onRender(DrawContext context, int mouseX, int mouseY, float delta, CallbackInfo ci) { + if (sortButton != null) { + setButtonCoordinates(); + sortButton.render(context, mouseX, mouseY, delta); + } + } + + // This override is NOT an ideal solution as it could lead to conflicts with other mods + @Override + public boolean keyPressed(int keyCode, int scanCode, int modifiers) { + if (LightweightInventorySortingClient.sortKeyBind.matchesKey(keyCode, scanCode)) { + sortButton.onClick(0f, 0f); // Simulate a click + } + return super.keyPressed(keyCode, scanCode, modifiers); + } + + @Override + public boolean mouseClicked(double mouseX, double mouseY, int button) { + if (LightweightInventorySortingClient.sortKeyBind.matchesMouse(button)) { + sortButton.onClick(0f, 0f); // Simulate a click + } + + return super.mouseClicked(mouseX, mouseY, button); + } + + private void setButtonCoordinates() { + sortButton.setX(this.x + this.backgroundWidth - 20 + Config.xOffsetInventory); + sortButton.setY(this.height / 2 - 15 + Config.yOffsetInventory); + } +} diff --git a/src/client/java/borknbeans/lightweightinventorysorting/mixin/client/MixinShulkerBoxScreen.java b/src/client/java/borknbeans/lightweightinventorysorting/mixin/client/ShulkerBoxScreenMixin.java similarity index 60% rename from src/client/java/borknbeans/lightweightinventorysorting/mixin/client/MixinShulkerBoxScreen.java rename to src/client/java/borknbeans/lightweightinventorysorting/mixin/client/ShulkerBoxScreenMixin.java index 4df45b2..2473ecf 100644 --- a/src/client/java/borknbeans/lightweightinventorysorting/mixin/client/MixinShulkerBoxScreen.java +++ b/src/client/java/borknbeans/lightweightinventorysorting/mixin/client/ShulkerBoxScreenMixin.java @@ -1,65 +1,65 @@ -package borknbeans.lightweightinventorysorting.mixin.client; - -import borknbeans.lightweightinventorysorting.ContainerSortButton; -import borknbeans.lightweightinventorysorting.LightweightInventorySortingClient; -import borknbeans.lightweightinventorysorting.config.LightweightInventorySortingConfig; -import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.gui.screen.ingame.HandledScreen; -import net.minecraft.client.gui.screen.ingame.ShulkerBoxScreen; -import net.minecraft.entity.player.PlayerInventory; -import net.minecraft.screen.ShulkerBoxScreenHandler; -import net.minecraft.text.Text; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Unique; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -@Mixin(ShulkerBoxScreen.class) -public abstract class MixinShulkerBoxScreen extends HandledScreen { - @Unique - private ContainerSortButton containerSortButton; - - public MixinShulkerBoxScreen(ShulkerBoxScreenHandler handler, PlayerInventory inventory, Text title) { - super(handler, inventory, title); - } - - @Override - public void init() { - super.init(); - - // Initialize button - int x = this.x + this.backgroundWidth - 20 + LightweightInventorySortingConfig.xOffsetContainer; - int y = this.y + 4 + LightweightInventorySortingConfig.yOffsetContainer; - int size = LightweightInventorySortingConfig.buttonSize.getButtonSize(); - containerSortButton = new ContainerSortButton(x, y, size, size, Text.literal("S"), 0, getScreenHandler().slots.size() - 37, this); - - // Add button to the screen - this.addDrawableChild(containerSortButton); - } - - @Inject(method = "render", at = @At("RETURN")) - private void onRender(DrawContext context, int mouseX, int mouseY, float delta, CallbackInfo ci) { - if (containerSortButton != null) { - containerSortButton.render(context, mouseX, mouseY, delta); - } - } - - @Override - public boolean keyPressed(int keyCode, int scanCode, int modifiers) { - if (LightweightInventorySortingClient.sortKeyBind.matchesKey(keyCode, scanCode)) { - containerSortButton.onClick(0f, 0f); // Simulate a click - } - - return super.keyPressed(keyCode, scanCode, modifiers); - } - - @Override - public boolean mouseClicked(double mouseX, double mouseY, int button) { - if (LightweightInventorySortingClient.sortKeyBind.matchesMouse(button)) { - containerSortButton.onClick(0f, 0f); // Simulate a click - } - - return super.mouseClicked(mouseX, mouseY, button); - } -} +package borknbeans.lightweightinventorysorting.mixin.client; + +import borknbeans.lightweightinventorysorting.LightweightInventorySortingClient; +import borknbeans.lightweightinventorysorting.config.Config; +import borknbeans.lightweightinventorysorting.sorting.SortButton; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.screen.ingame.HandledScreen; +import net.minecraft.client.gui.screen.ingame.ShulkerBoxScreen; +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.screen.ShulkerBoxScreenHandler; +import net.minecraft.text.Text; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ShulkerBoxScreen.class) +public abstract class ShulkerBoxScreenMixin extends HandledScreen { + @Unique + private SortButton sortButton; + + public ShulkerBoxScreenMixin(ShulkerBoxScreenHandler handler, PlayerInventory inventory, Text title) { + super(handler, inventory, title); + } + + @Override + public void init() { + super.init(); + + // Initialize button + int x = this.x + this.backgroundWidth - 20 + Config.xOffsetContainer; + int y = this.y + 4 + Config.yOffsetContainer; + int size = Config.buttonSize.getButtonSize(); + sortButton = new SortButton(x, y, size, size, Text.literal("S"), 0, getScreenHandler().slots.size() - 37); + + // Add button to the screen + this.addDrawableChild(sortButton); + } + + @Inject(method = "render", at = @At("RETURN")) + private void onRender(DrawContext context, int mouseX, int mouseY, float delta, CallbackInfo ci) { + if (sortButton != null) { + sortButton.render(context, mouseX, mouseY, delta); + } + } + + @Override + public boolean keyPressed(int keyCode, int scanCode, int modifiers) { + if (LightweightInventorySortingClient.sortKeyBind.matchesKey(keyCode, scanCode)) { + sortButton.onClick(0f, 0f); // Simulate a click + } + + return super.keyPressed(keyCode, scanCode, modifiers); + } + + @Override + public boolean mouseClicked(double mouseX, double mouseY, int button) { + if (LightweightInventorySortingClient.sortKeyBind.matchesMouse(button)) { + sortButton.onClick(0f, 0f); // Simulate a click + } + + return super.mouseClicked(mouseX, mouseY, button); + } +} diff --git a/src/client/java/borknbeans/lightweightinventorysorting/sorting/ClickOperation.java b/src/client/java/borknbeans/lightweightinventorysorting/sorting/ClickOperation.java new file mode 100644 index 0000000..daee57f --- /dev/null +++ b/src/client/java/borknbeans/lightweightinventorysorting/sorting/ClickOperation.java @@ -0,0 +1,91 @@ +package borknbeans.lightweightinventorysorting.sorting; + +import java.util.List; + +import borknbeans.lightweightinventorysorting.config.Config; +import net.minecraft.client.MinecraftClient; +import net.minecraft.item.ItemStack; +import net.minecraft.screen.slot.SlotActionType; + +public class ClickOperation { + private final MinecraftClient client; + private final int syncId; + private final int targetSlot; + private final ItemStack expectedStartingTargetStack; + private final ItemStack expectedEndingTargetStack; + private final ItemStack expectedStartingMouseStack; + private final ItemStack expectedEndingMouseStack; + + private final List delays = List.of(0, 5, 15); // in milliseconds + + public ClickOperation(MinecraftClient client, int syncId, int targetSlot, ItemStack expectedStartingTargetStack, ItemStack expectedEndingTargetStack, ItemStack expectedStartingMouseStack, ItemStack expectedEndingMouseStack) { + this.client = client; + this.syncId = syncId; + this.targetSlot = targetSlot; + this.expectedStartingTargetStack = expectedStartingTargetStack; + this.expectedEndingTargetStack = expectedEndingTargetStack; + this.expectedStartingMouseStack = expectedStartingMouseStack; + this.expectedEndingMouseStack = expectedEndingMouseStack; + } + + public void execute() throws Exception { + if (client.player == null) { + throw new Exception("Player is null"); + } + + ItemStack startingMouseStack = Sorter.getMouseStack(client); + if (!ItemStack.areItemsAndComponentsEqual(startingMouseStack, expectedStartingMouseStack)) { + throw new Exception("[Target: " + targetSlot + "] Starting mouse stack is not what we expected: (ACTUAL)" + getItemStackString(startingMouseStack) + " != (EXPECTED)" + getItemStackString(expectedStartingMouseStack)); + } + + ItemStack startingTargetStack = Sorter.getInventoryStack(client, targetSlot); + if (!ItemStack.areItemsAndComponentsEqual(startingTargetStack, expectedStartingTargetStack)) { + throw new Exception("[Target: " + targetSlot + "] Starting target stack is not what we expected: (ACTUAL)" + getItemStackString(startingTargetStack) + " != (EXPECTED)" + getItemStackString(expectedStartingTargetStack)); + } + + click(); + + Exception error = null; + // Backoff retry - 5, 10, 15ms delay between each retry + for (int i = 0; i < delays.size(); i++) { + try { + Thread.sleep(delays.get(i)); + } catch (InterruptedException e) {} + + try { + postClickVerification(); + return; + } catch (Exception e) { + error = e; + } + } + + if (error != null) { + throw error; + } + } + + private void click() { + if (client.player == null) { + return; + } + + client.interactionManager.clickSlot(syncId, targetSlot, 0, SlotActionType.PICKUP, client.player); + } + + private void postClickVerification() throws Exception{ + var endingMouseStack = Sorter.getMouseStack(client); + if (!ItemStack.areItemsAndComponentsEqual(endingMouseStack, expectedEndingMouseStack)) { + throw new Exception("[Target: " + targetSlot + "] Ending mouse stack is not what we expected: (ACTUAL)" + getItemStackString(endingMouseStack) + " != (EXPECTED)" + getItemStackString(expectedEndingMouseStack)); + } + + var targetStack = Sorter.getInventoryStack(client, targetSlot); + if (!ItemStack.areItemsAndComponentsEqual(targetStack, expectedEndingTargetStack)) { + throw new Exception("[Target: " + targetSlot + "] Ending target stack is not what we expected: (ACTUAL)" + getItemStackString(targetStack) + " != (EXPECTED)" + getItemStackString(expectedEndingTargetStack)); + } + } + + private String getItemStackString(ItemStack stack) { + return String.format("%dx %s", stack.getCount(), stack.getItem().getName().getString()); + } +} diff --git a/src/client/java/borknbeans/lightweightinventorysorting/sorting/HandHelper.java b/src/client/java/borknbeans/lightweightinventorysorting/sorting/HandHelper.java deleted file mode 100644 index 73f767a..0000000 --- a/src/client/java/borknbeans/lightweightinventorysorting/sorting/HandHelper.java +++ /dev/null @@ -1,19 +0,0 @@ -package borknbeans.lightweightinventorysorting.sorting; - -import net.minecraft.item.ItemStack; - -public class HandHelper { - public boolean exists; - public ItemStack stack; - public int count; - - public HandHelper() { - Reset(); - } - - public void Reset() { - exists = false; - stack = null; - count = 0; - } -} diff --git a/src/client/java/borknbeans/lightweightinventorysorting/ContainerSortButton.java b/src/client/java/borknbeans/lightweightinventorysorting/sorting/SortButton.java similarity index 53% rename from src/client/java/borknbeans/lightweightinventorysorting/ContainerSortButton.java rename to src/client/java/borknbeans/lightweightinventorysorting/sorting/SortButton.java index 66ece03..a94a145 100644 --- a/src/client/java/borknbeans/lightweightinventorysorting/ContainerSortButton.java +++ b/src/client/java/borknbeans/lightweightinventorysorting/sorting/SortButton.java @@ -1,62 +1,56 @@ -package borknbeans.lightweightinventorysorting; - -import borknbeans.lightweightinventorysorting.config.LightweightInventorySortingConfig; -import borknbeans.lightweightinventorysorting.sorting.SortingHelper; -import com.mojang.blaze3d.systems.RenderSystem; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.gui.screen.ingame.HandledScreen; -import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder; -import net.minecraft.client.gui.widget.ClickableWidget; -import net.minecraft.client.render.RenderLayer; -import net.minecraft.text.Text; -import net.minecraft.util.Identifier; - -import java.util.function.Function; - -public class ContainerSortButton extends ClickableWidget { - - private Identifier buttonTexture; - private Identifier buttonHoverTexture; - - private int startIndex, endIndex; - private HandledScreen screen; - - public ContainerSortButton(int x, int y, int width, int height, Text message, int startIndex, int endIndex, HandledScreen screen) { - super(x, y, width, height, message); - - this.startIndex = startIndex; - this.endIndex = endIndex; - - buttonTexture = LightweightInventorySortingConfig.buttonSize.getButtonTexture(); - buttonHoverTexture = LightweightInventorySortingConfig.buttonSize.getButtonHoverTexture(); - - this.screen = screen; - } - - @Override - protected void renderWidget(DrawContext context, int mouseX, int mouseY, float delta) { - if (this.isHovered()) { - context.drawGuiTexture(RenderLayer::getGuiTextured, buttonHoverTexture, this.getX(), this.getY(), this.getWidth(), this.getHeight()); - } else { - context.drawGuiTexture(RenderLayer::getGuiTextured, buttonTexture, this.getX(), this.getY(), this.getWidth(), this.getHeight()); - } - } - - @Override - protected void appendClickableNarrations(NarrationMessageBuilder builder) { - // Narration message if needed - } - - @Override - public void onClick(double mouseX, double mouseY) { - MinecraftClient client = MinecraftClient.getInstance(); - - if (client.player != null) { - SortingHelper.sortInventory(client, startIndex, endIndex); - } else { - System.out.println("Player is not available."); - } - } - -} +package borknbeans.lightweightinventorysorting.sorting; + +import borknbeans.lightweightinventorysorting.LightweightInventorySorting; +import borknbeans.lightweightinventorysorting.config.Config; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder; +import net.minecraft.client.gui.widget.ClickableWidget; +import net.minecraft.client.render.RenderLayer; +import net.minecraft.text.Text; +import net.minecraft.util.Identifier; + +public class SortButton extends ClickableWidget { + + private Identifier buttonTexture; + private Identifier buttonHoverTexture; + + private int sortStartIndex, sortEndIndex; + + public SortButton(int x, int y, int width, int height, Text message, int startIndex, int endIndex) { + super(x, y, width, height, message); + + this.sortStartIndex = startIndex; + this.sortEndIndex = endIndex; + + buttonTexture = Config.buttonSize.getButtonTexture(); + buttonHoverTexture = Config.buttonSize.getButtonHoverTexture(); + } + + @Override + protected void appendClickableNarrations(NarrationMessageBuilder builder) { + // Narration message if needed + } + + @Override + protected void renderWidget(DrawContext context, int mouseX, int mouseY, float delta) { + if (this.isHovered()) { + context.drawGuiTexture(RenderLayer::getGuiTextured, buttonHoverTexture, this.getX(), this.getY(), this.getWidth(), this.getHeight()); + } else { + context.drawGuiTexture(RenderLayer::getGuiTextured, buttonTexture, this.getX(), this.getY(), this.getWidth(), this.getHeight()); + } + } + + @Override + public void onClick(double mouseX, double mouseY) { + MinecraftClient client = MinecraftClient.getInstance(); + + if (client.player != null) { + Sorter.sortContainerClientside(client, sortStartIndex, sortEndIndex); + // TODO: handle server-side sorting if enabled + } else { + LightweightInventorySorting.LOGGER.error("Player is not available."); + } + } + +} diff --git a/src/client/java/borknbeans/lightweightinventorysorting/sorting/SortComparator.java b/src/client/java/borknbeans/lightweightinventorysorting/sorting/SortComparator.java new file mode 100644 index 0000000..24874fb --- /dev/null +++ b/src/client/java/borknbeans/lightweightinventorysorting/sorting/SortComparator.java @@ -0,0 +1,12 @@ +package borknbeans.lightweightinventorysorting.sorting; +import java.util.Comparator; + +import borknbeans.lightweightinventorysorting.config.Config; +import net.minecraft.item.ItemStack; + +public class SortComparator implements Comparator { + @Override + public int compare(ItemStack o1, ItemStack o2) { + return Config.sortType.compare(o1, o2); + } +} \ No newline at end of file diff --git a/src/client/java/borknbeans/lightweightinventorysorting/sorting/SortSnapshotClientside.java b/src/client/java/borknbeans/lightweightinventorysorting/sorting/SortSnapshotClientside.java new file mode 100644 index 0000000..dbf415e --- /dev/null +++ b/src/client/java/borknbeans/lightweightinventorysorting/sorting/SortSnapshotClientside.java @@ -0,0 +1,55 @@ +package borknbeans.lightweightinventorysorting.sorting; + +import com.google.gson.*; +import net.minecraft.item.ItemStack; +import net.minecraft.registry.Registries; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.lang.reflect.Type; +import java.util.Base64; +import java.util.List; +import java.util.zip.GZIPOutputStream; + +public class SortSnapshotClientside { + private final List inventory; + + public SortSnapshotClientside(List inventory) { + this.inventory = inventory; + } + + public String encode() { + var gson = new GsonBuilder() + .registerTypeAdapter(ItemStack.class, new ItemStackSerializer()) + .create(); + var json = gson.toJson(this); + + try { + var baos = new ByteArrayOutputStream(); + var gzipOut = new GZIPOutputStream(baos); + gzipOut.write(json.getBytes()); + gzipOut.close(); + + return Base64.getEncoder().encodeToString(baos.toByteArray()); + } catch (IOException e) { + throw new RuntimeException("Failed to compress inventory data", e); + } + } + + private static class ItemStackSerializer implements JsonSerializer { + @Override + public JsonElement serialize(ItemStack src, Type typeOfSrc, JsonSerializationContext context) { + var json = new JsonObject(); + + if (src.isEmpty()) { + json.addProperty("empty", true); + return json; + } + + json.addProperty("id", Registries.ITEM.getId(src.getItem()).toString()); + json.addProperty("count", src.getCount()); + + return json; + } + } +} \ No newline at end of file diff --git a/src/client/java/borknbeans/lightweightinventorysorting/sorting/SortableSlot.java b/src/client/java/borknbeans/lightweightinventorysorting/sorting/SortableSlot.java deleted file mode 100644 index 6ed1b1a..0000000 --- a/src/client/java/borknbeans/lightweightinventorysorting/sorting/SortableSlot.java +++ /dev/null @@ -1,49 +0,0 @@ -package borknbeans.lightweightinventorysorting.sorting; - -import borknbeans.lightweightinventorysorting.config.LightweightInventorySortingConfig; -import net.minecraft.item.ItemStack; -import org.jetbrains.annotations.NotNull; - -public class SortableSlot implements Comparable { - - private int index; - private ItemStack stack; - - public boolean sorted; - - public SortableSlot(int index, ItemStack stack) { - this.index = index; - this.stack = stack; - sorted = false; - } - - public int getIndex() { - return index; - } - - public void setIndex(int index) { - this.index = index; - } - - public ItemStack getStack() { - return stack; - } - - public void setStack(ItemStack stack) { - this.stack = stack; - } - - @Override - public int compareTo(@NotNull SortableSlot o) { - // Compare the names of the two stacks - int result = LightweightInventorySortingConfig.sortType.compare(this.getStack(), o.getStack()); - - if (result != 0) { // Items have different names - return result; - } else if (!ItemStack.areItemsAndComponentsEqual(this.getStack(), o.getStack())) { // Items have the same name, but different components or items - return 0; - } else { // Items have same name, are same item, and have same components - return o.getStack().getCount() - this.getStack().getCount(); - } - } -} diff --git a/src/client/java/borknbeans/lightweightinventorysorting/sorting/SortableSlotComparator.java b/src/client/java/borknbeans/lightweightinventorysorting/sorting/SortableSlotComparator.java deleted file mode 100644 index ea8fc9e..0000000 --- a/src/client/java/borknbeans/lightweightinventorysorting/sorting/SortableSlotComparator.java +++ /dev/null @@ -1,11 +0,0 @@ -package borknbeans.lightweightinventorysorting.sorting; - -import java.util.Comparator; - -public class SortableSlotComparator implements Comparator { - - @Override - public int compare(SortableSlot o1, SortableSlot o2) { - return o1.compareTo(o2); - } -} \ No newline at end of file diff --git a/src/client/java/borknbeans/lightweightinventorysorting/sorting/Sorter.java b/src/client/java/borknbeans/lightweightinventorysorting/sorting/Sorter.java new file mode 100644 index 0000000..dc419e2 --- /dev/null +++ b/src/client/java/borknbeans/lightweightinventorysorting/sorting/Sorter.java @@ -0,0 +1,270 @@ +package borknbeans.lightweightinventorysorting.sorting; + +import java.util.ArrayList; +import java.util.List; + +import borknbeans.lightweightinventorysorting.LightweightInventorySorting; +import borknbeans.lightweightinventorysorting.config.Config; +import net.fabricmc.api.EnvType; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.client.MinecraftClient; +import net.minecraft.item.BundleItem; +import net.minecraft.item.ItemStack; + +public class Sorter { + + private static boolean isSorting = false; + + public static void sortContainerClientside(MinecraftClient client, int sortStartIndex, int sortEndIndex) { + if (FabricLoader.getInstance().getEnvironmentType() != EnvType.CLIENT) { + return; + } + + if (isSorting) return; + isSorting = true; + + LightweightInventorySorting.LOGGER.info("Starting clientside sort"); + + var syncId = client.player.currentScreenHandler.syncId; + + + + var snapshot = getInventorySnapshot(client, sortStartIndex, sortEndIndex); + var snapshotEncoder = new SortSnapshotClientside(snapshot); + LightweightInventorySorting.LOGGER.info("Encoded snapshot: " + snapshotEncoder.encode()); + + // Run the sort in a new thread + new Thread(() -> { + try { + clearMouseStack(client, syncId, sortStartIndex, sortEndIndex); + combineLikeStacks(client, syncId, sortStartIndex, sortEndIndex); + sort(client, syncId, sortStartIndex, sortEndIndex); + LightweightInventorySorting.LOGGER.info("Clientside sort complete"); + } catch (Exception e) { + LightweightInventorySorting.LOGGER.error(e.getMessage()); + } + + isSorting = false; + }).start(); + } + + private static void clearMouseStack(MinecraftClient client, int syncId, int sortStartIndex, int sortEndIndex) throws Exception { + var snapshot = getInventorySnapshot(client, sortStartIndex, sortEndIndex); + + // Clear any existing item that is on the mouse + var mouseStack = getMouseStack(client).copy(); + if (!mouseStack.isEmpty()) { + var emptyIndex = getEmptySlotIndex(snapshot); + if (emptyIndex == -1) { + throw new Exception("[Sort] No empty slot found to clear mouse stack"); + } + + var emptySlotOperation = new ClickOperation(client, syncId, sortStartIndex + emptyIndex, ItemStack.EMPTY, mouseStack, mouseStack, ItemStack.EMPTY); + emptySlotOperation.execute(); + } + } + + private static void combineLikeStacks(MinecraftClient client, int syncId, int sortStartIndex, int sortEndIndex) throws Exception { + var snapshot = getInventorySnapshot(client, sortStartIndex, sortEndIndex); + var mouseStack = getMouseStack(client); + + if (!mouseStack.isEmpty()) { + throw new Exception("[CombineLikeStacks] Mouse stack is not empty"); + } + + for (int i = 0; i < snapshot.size(); i++) { + var stackOriginal = snapshot.get(i).copy(); + mouseStack = getMouseStack(client); + if (stackOriginal.isEmpty() || stackOriginal.getCount() == stackOriginal.getMaxCount()) { + continue; + } + + for (int j = i + 1; j < snapshot.size(); j++) { + var otherStack = snapshot.get(j).copy(); + if (otherStack.isEmpty() || otherStack.getCount() == otherStack.getMaxCount()) { + continue; + } + + var stack = mouseStack.isEmpty() ? stackOriginal : mouseStack; + + if (ItemStack.areItemsAndComponentsEqual(stack, otherStack)) { + var maxStackSize = stack.getMaxCount(); + var combinedSize = stack.getCount() + otherStack.getCount(); + + var pickupFirstStack = new ClickOperation(client, syncId, i + sortStartIndex, stack, ItemStack.EMPTY, mouseStack, stack); + + var expectedEndingMouseStack = combinedSize > maxStackSize ? stack.copyWithCount(combinedSize - maxStackSize) : ItemStack.EMPTY; + var combineStacks = new ClickOperation(client, syncId, j + sortStartIndex, otherStack, stack.copyWithCount(Math.min(combinedSize, maxStackSize)), stack, expectedEndingMouseStack); + try { + if (mouseStack.isEmpty()) { // Dont pickup first stack if we have a stack in our hand from the previous iteration + pickupFirstStack.execute(); + } + Thread.sleep(Config.sortDelay); + combineStacks.execute(); + } catch (Exception e) { + throw new Exception("Failed to combine like items: " + e.getMessage()); + } + + mouseStack = expectedEndingMouseStack; + + if (mouseStack.isEmpty()) { // If our hand is empty, move to the next stack + break; + } + } + } + + if (!mouseStack.isEmpty()) { + var putBackStack = new ClickOperation(client, syncId, i + sortStartIndex, ItemStack.EMPTY, mouseStack, mouseStack, ItemStack.EMPTY); + + try { + putBackStack.execute(); + } catch (Exception e) { + throw new Exception("Failed to put back item: " + e.getMessage()); + } + } + + snapshot = getInventorySnapshot(client, sortStartIndex, sortEndIndex); // Update the inventory to reflect our changes + } + } + + private static void sort(MinecraftClient client, int syncId, int sortStartIndex, int sortEndIndex) throws Exception { + var snapshot = getInventorySnapshot(client, sortStartIndex, sortEndIndex); + + var sortedStacks = new ArrayList(); + for (int i = 0; i < snapshot.size(); i++) { + var stack = snapshot.get(i).copy(); + if (stack.isEmpty()) { + continue; + } + + sortedStacks.add(stack); + } + + sortedStacks.sort(new SortComparator()); + + var mouseStack = getMouseStack(client); + + if (!mouseStack.isEmpty()) { + throw new Exception("[Sort] Mouse stack is not empty"); + } + + + + for (int i = 0; i < sortedStacks.size(); i++) { + var sortedStack = sortedStacks.get(i); + + var stackCurrIndex = -1; + for (int j = i; j < snapshot.size(); j++) { + if (ItemStack.areItemsAndComponentsEqual(sortedStack, snapshot.get(j)) && sortedStack.getCount() == snapshot.get(j).getCount()) { + stackCurrIndex = j + sortStartIndex; + break; + } + } + + if (stackCurrIndex == -1) { + throw new Exception("[Sort] Stack not found in inventory"); + } + + if (stackCurrIndex == i + sortStartIndex) { + continue; + } + + var pickupOperation = new ClickOperation(client, syncId, stackCurrIndex, sortedStack, ItemStack.EMPTY, ItemStack.EMPTY, sortedStack); + + var existingStack = snapshot.get(i).copy(); + var placeOperation = new ClickOperation(client, syncId, i + sortStartIndex, existingStack, sortedStack, sortedStack, existingStack); + + var emptyHandOperation = new ClickOperation(client, syncId, stackCurrIndex, ItemStack.EMPTY, existingStack, existingStack, ItemStack.EMPTY); + + if (sortedStack.getItem() instanceof BundleItem) { + if (!existingStack.isEmpty()) { + var pickupTargetSlotOperation = new ClickOperation(client, syncId, i + sortStartIndex, existingStack, ItemStack.EMPTY, ItemStack.EMPTY, existingStack); + var emptySlotIndex = getEmptySlotIndex(snapshot); + + if (emptySlotIndex == -1) { + throw new Exception("[Sort] No empty slot found"); + } + + var placeInEmptySlotOperation = new ClickOperation(client, syncId, sortStartIndex + emptySlotIndex, ItemStack.EMPTY, existingStack, existingStack, ItemStack.EMPTY); + + pickupTargetSlotOperation.execute(); + placeInEmptySlotOperation.execute(); + Thread.sleep(Config.sortDelay); + + existingStack = ItemStack.EMPTY; + + // Update place operation to expect the new empty stack + placeOperation = new ClickOperation(client, syncId, i + sortStartIndex, existingStack, sortedStack, sortedStack, existingStack); + } + } + + pickupOperation.execute(); + placeOperation.execute(); + Thread.sleep(Config.sortDelay); + if (!existingStack.isEmpty()) { + emptyHandOperation.execute(); + } + + snapshot = getInventorySnapshot(client, sortStartIndex, sortEndIndex); + } + + for (int i = 0; i < sortedStacks.size(); i++) { + var expectedStack = sortedStacks.get(i).copy(); + var actualStack = snapshot.get(i).copy(); + + if (!ItemStack.areItemsAndComponentsEqual(expectedStack, actualStack)) { + throw new Exception("[Sort] Stack not in correct position"); + } + } + } + + /* + * Player Inventory Slots + * 0 : crafting result + * 1-4: crafting input + * 5-8: armor + * 9-35: main inventory + * 36-44: hotbar + * 45: offhand + */ + private static List getInventorySnapshot(MinecraftClient client, int sortStartIndex, int sortEndIndex) { + var slots = client.player.currentScreenHandler.slots; + + List snapshot = new ArrayList<>(); + for (int i = 0; i < slots.size(); i++) { + // LightweightInventorySorting.LOGGER.info("Slot " + i + ": " + slots.get(i).getStack().toString()); + if (i < sortStartIndex || i > sortEndIndex) { + continue; + } + + snapshot.add(slots.get(i).getStack()); + } + + return snapshot; + } + + private static int getEmptySlotIndex(List snapshot) { + for (int i = 0; i < snapshot.size(); i++) { + if (snapshot.get(i).isEmpty()) { + return i; + } + } + return -1; + } + + public static ItemStack getInventoryStack(MinecraftClient client, int index) { + if (client.player == null) { + return ItemStack.EMPTY; + } + + return client.player.currentScreenHandler.getSlot(index).getStack(); + } + + public static ItemStack getMouseStack(MinecraftClient client) { + if (client.player == null) { + return ItemStack.EMPTY; + } + + return client.player.currentScreenHandler.getCursorStack(); + } +} diff --git a/src/client/java/borknbeans/lightweightinventorysorting/sorting/SortingHelper.java b/src/client/java/borknbeans/lightweightinventorysorting/sorting/SortingHelper.java deleted file mode 100644 index 29f1f21..0000000 --- a/src/client/java/borknbeans/lightweightinventorysorting/sorting/SortingHelper.java +++ /dev/null @@ -1,226 +0,0 @@ -package borknbeans.lightweightinventorysorting.sorting; - -import borknbeans.lightweightinventorysorting.LightweightInventorySorting; -import borknbeans.lightweightinventorysorting.config.LightweightInventorySortingConfig; -import net.minecraft.client.MinecraftClient; -import net.minecraft.item.ItemStack; -import net.minecraft.screen.slot.Slot; -import net.minecraft.screen.slot.SlotActionType; -import net.minecraft.util.collection.DefaultedList; - -import java.util.ArrayList; -import java.util.List; - -public class SortingHelper { - - public static void sortInventory(MinecraftClient client, int startIndex, int endIndex) { - if (client.player == null) return; - - int syncId = client.player.currentScreenHandler.syncId; - - DefaultedList slots = client.player.currentScreenHandler.slots; - List sortableSlots = new ArrayList<>(); - - LightweightInventorySorting.LOGGER.info("Collecting sort details..."); - for (int i = startIndex; i <= endIndex; i++) { - ItemStack stack = slots.get(i).getStack(); - if (stack.isEmpty()) { continue; } - - LightweightInventorySorting.LOGGER.info(i + ": " + stack.getName().getString() + ", " + stack.getCount() + "/" + stack.getMaxCount() + ", " + stack.getItem().getName().getString()); - sortableSlots.add(new SortableSlot(i, stack)); - } - - ItemStack mouseStack = stackAttachedToMouse(client); - if (mouseStack != null) { - LightweightInventorySorting.LOGGER.info("MOUSE: " + mouseStack.getName().getString() + ", " + mouseStack.getCount() + "/" + mouseStack.getMaxCount() + ", " + mouseStack.getItem().getName().getString()); - int index = findEmptySlotIndex(slots, startIndex, endIndex); - - if (index == -1) { - LightweightInventorySorting.LOGGER.info("An error occurred while attempting to sort items in slots with an item already selected!"); - return; - } - - move(client, syncId, -1, index, new HandHelper()); - sortableSlots.add(new SortableSlot(index, mouseStack)); - } - - LightweightInventorySorting.LOGGER.info("Sorting starting now..."); - - sortableSlots.sort(new SortableSlotComparator()); - - new Thread(() -> { - combineLikeItems(client, syncId, slots, sortableSlots, startIndex, endIndex); - - sortableSlots.sort(new SortableSlotComparator()); - - sortItems(client, syncId, slots, sortableSlots, startIndex); - }).start(); - } - - /*** - * This goes through the sorted list and combines like items and reduces the size of the sorted list. - * - * @param client - * @param syncId - * @param sortedSlots - */ - private static void combineLikeItems(MinecraftClient client, int syncId, DefaultedList slots, List sortedSlots, int startIndex, int endIndex) { - for (int i = sortedSlots.size() - 1; i >= 1; i--) { - ItemStack stack = sortedSlots.get(i).getStack(); - - if (stack.getCount() == stack.getMaxCount()) { continue; } - - int index = i - 1; - - HandHelper hand = new HandHelper(); - for (int j = index; j >= 0; j--) { - ItemStack stackPrev = sortedSlots.get(j).getStack(); - - // If we are holding something and the prev does not match OR if our hand is empty and the two checked stacks don't match - // THEN don't combine - if ((hand.stack != null && !ItemStack.areItemsAndComponentsEqual(stackPrev, hand.stack) || (!ItemStack.areItemsAndComponentsEqual(stack, stackPrev) && !hand.exists))) { - if (hand.exists) { // Place item in hand back down - move(client, syncId, 0, sortedSlots.get(i).getIndex(), hand); - sortedSlots.get(i).setStack(hand.stack.copy()); - - hand.Reset(); - } - - break; - } - if (stackPrev.getCount() == stackPrev.getMaxCount()) { continue; } - - int combinedCount = hand.exists ? hand.count + stackPrev.getCount() : stack.getCount() + stackPrev.getCount(); - - if (combinedCount <= stackPrev.getMaxCount()) { - // Move with no remainder - move(client, syncId, sortedSlots.get(i).getIndex(), sortedSlots.get(j).getIndex(), hand); - // remove item from sortedSlots - sortedSlots.remove(i); - - if (hand.exists) { - hand.Reset(); - } - - break; - } else { - // Move with remainder - move(client, syncId, sortedSlots.get(i).getIndex(), sortedSlots.get(j).getIndex(), hand); - // Store hand item information - hand.exists = true; - - ItemStack stackCopy = stackPrev.copy(); - stackCopy.setCount(combinedCount - stackPrev.getMaxCount()); - - hand.stack = stackCopy; - hand.count = stackCopy.getCount(); // TODO: We can remove this count and now just use the stack count as it should be accurate - } - } - - if (hand.exists) { - int emptySlot = -1; - - for (int j = startIndex; j < endIndex; j++) { - if (slots.get(j).getStack().isEmpty()) { - emptySlot = j; - break; - } - } - - if (emptySlot != -1) { - move(client, syncId, 0, emptySlot, hand); - if (sortedSlots.get(i).getIndex() != emptySlot) { - sortedSlots.get(i).setIndex(emptySlot); // placing hand item here - hand.stack.setCount(hand.count); - sortedSlots.get(i).setStack(hand.stack.copy()); - } - hand.Reset(); - } else { - System.out.println("Something went wrong combining items"); - } - } - - try { - Thread.sleep(LightweightInventorySortingConfig.sortDelay); - } catch (InterruptedException e) {} - } - } - - private static void move(MinecraftClient client, int syncId, int source, int dest, HandHelper hand) { - if (!hand.exists) { - client.interactionManager.clickSlot(syncId, source, 0, SlotActionType.PICKUP, client.player); - } - - client.interactionManager.clickSlot(syncId, dest, 0, SlotActionType.PICKUP, client.player); - } - - private static void sortItems(MinecraftClient client, int syncId, DefaultedList slots, List sortedSlots, int startIndex) { - HandHelper hand = new HandHelper(); - - for (int i = 0; i < sortedSlots.size(); i++) { - if (sortedSlots.get(i).sorted) { continue; } - - sortItem(client, syncId, slots, sortedSlots, i, startIndex, hand); - } - } - - private static void sortItem(MinecraftClient client, int syncId, DefaultedList slots, List sortedSlots, int index, int startIndex, HandHelper hand) { - try { - Thread.sleep(LightweightInventorySortingConfig.sortDelay); - } catch (InterruptedException e) {} - - int dest = startIndex + index; - - if (dest == sortedSlots.get(index).getIndex()) { - sortedSlots.get(index).sorted = true; - return; - } - - ItemStack destStack = slots.get(dest).getStack(); - - move(client, syncId, sortedSlots.get(index).getIndex(), dest, hand); - sortedSlots.get(index).sorted = true; - - if (!destStack.isEmpty()) { - hand.exists = true; - hand.stack = destStack; - hand.count = destStack.getCount(); - - // Get item in slot dest - int sortedSlotListIndex = -1; - for (int i = 0; i < sortedSlots.size(); i++) { - if (sortedSlots.get(i).getIndex() == dest) { - sortedSlotListIndex = i; - break; - } - } - - if (sortedSlotListIndex == -1) { - System.out.println("Something went wrong with sorting the items."); - return; - } - - sortItem(client, syncId, slots, sortedSlots, sortedSlotListIndex, startIndex, hand); - } else { - hand.Reset(); - } - } - - private static ItemStack stackAttachedToMouse(MinecraftClient client) { - if (client.player == null) { - return null; - } - - ItemStack cursorStack = client.player.currentScreenHandler.getCursorStack(); - return cursorStack.isEmpty() ? null : cursorStack; - } - - private static int findEmptySlotIndex(DefaultedList slots, int startIndex, int endIndex) { - for (int i = startIndex; i <= endIndex; i++) { - ItemStack stack = slots.get(i).getStack(); - if (stack.isEmpty()) { return i; } - } - - return -1; - } -} diff --git a/src/main/java/borknbeans/lightweightinventorysorting/LightweightInventorySorting.java b/src/main/java/borknbeans/lightweightinventorysorting/LightweightInventorySorting.java index 0ac04a0..3ad5ad3 100644 --- a/src/main/java/borknbeans/lightweightinventorysorting/LightweightInventorySorting.java +++ b/src/main/java/borknbeans/lightweightinventorysorting/LightweightInventorySorting.java @@ -1,23 +1,46 @@ package borknbeans.lightweightinventorysorting; import net.fabricmc.api.ModInitializer; +// import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; +// import net.minecraft.text.Text; +// import static net.minecraft.server.command.CommandManager.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class LightweightInventorySorting implements ModInitializer { + public static final String MOD_ID = "lightweight-inventory-sorting"; + // This logger is used to write text to the console and the log file. // It is considered best practice to use your mod id as the logger's name. // That way, it's clear which mod wrote info, warnings, and errors. - public static final String MOD_ID = "lightweight-inventory-sorting"; - public static final Logger LOGGER = LoggerFactory.getLogger("lightweight-inventory-sorting"); + public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID); + + public static final String ENCODED_SNAPSHOT = "H4sIAAAAAAAA/8XTwQ6CMAyA4XfpmYMIHtyrGEPGNuLiaEkdEkN4d49EbQRE4/3Ltv7bevB4dRiJb6AOPXgLCmqPzrCuomooekJIwFCLEVQ6JC+m9GxORRM0ni+j3P6bZj+QuSA/T5RuBBp90zhbaGbqJuZeQMsWbXDvD2k1WhcmZ5nLPMdR7EWha0I7op3UY0XedcnmbPztZE9BcimIIcYqUOd44Wric3u8JuknyMmybDgOd5oBulY9BAAA"; @Override public void onInitialize() { - // This code runs as soon as Minecraft is in a mod-load-ready state. - // However, some things (like resources) may still be uninitialized. - // Proceed with mild caution. + // CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> dispatcher.register(literal("lis-load") + // .executes(context -> { + // var snapshot = new SortSnapshotServerside().decode(ENCODED_SNAPSHOT); + + // var source = context.getSource(); + // var player = source.getPlayer(); + + // if (player == null) { + // source.sendFeedback(() -> Text.literal("Player is null"), false); + // return 1; + // } + + // var i = 9; + // for (var item : snapshot.inventory) { + // player.getInventory().setStack(i, item); + // i++; + // } - LOGGER.info("Hello Fabric world!"); + // return 1; + // }))); + + LOGGER.info("Lightweight Inventory Sorting initialized on the server!"); } } \ No newline at end of file diff --git a/src/main/java/borknbeans/lightweightinventorysorting/SortSnapshotServerside.java b/src/main/java/borknbeans/lightweightinventorysorting/SortSnapshotServerside.java new file mode 100644 index 0000000..5d9c17e --- /dev/null +++ b/src/main/java/borknbeans/lightweightinventorysorting/SortSnapshotServerside.java @@ -0,0 +1,64 @@ +package borknbeans.lightweightinventorysorting; + +import com.google.gson.*; +import net.minecraft.item.ItemStack; +import net.minecraft.registry.Registries; +import net.minecraft.util.Identifier; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.lang.reflect.Type; +import java.util.Base64; +import java.util.List; +import java.util.zip.GZIPInputStream; + +public class SortSnapshotServerside { + public List inventory; + + public SortSnapshotServerside decode(String compressedData) { + try { + var decoded = Base64.getDecoder().decode(compressedData); + var bais = new ByteArrayInputStream(decoded); + var gzipIn = new GZIPInputStream(bais); + + var buffer = new byte[1024]; + var baos = new ByteArrayOutputStream(); + int len; + while ((len = gzipIn.read(buffer)) > 0) { + baos.write(buffer, 0, len); + } + gzipIn.close(); + + var json = baos.toString(); + + var gson = new GsonBuilder() + .registerTypeAdapter(ItemStack.class, new ItemStackDeserializer()) + .create(); + return gson.fromJson(json, SortSnapshotServerside.class); + } catch (IOException e) { + throw new RuntimeException("Failed to decompress inventory data", e); + } + } + + private static class ItemStackDeserializer implements JsonDeserializer { + @Override + public ItemStack deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + var obj = json.getAsJsonObject(); + + if (obj.has("empty") && obj.get("empty").getAsBoolean()) { + return ItemStack.EMPTY; + } + + var id = obj.get("id").getAsString(); + var count = obj.get("count").getAsInt(); + + // TODO: Handle item components - idk how to do this + var item = Registries.ITEM.get(Identifier.of(id)); + var stack = new ItemStack(item); + stack.setCount(count); + + return stack; + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/lightweight-inventory-sorting/lang/en_us.json b/src/main/resources/assets/lightweight-inventory-sorting/lang/en_us.json index a8e9449..a942dca 100644 --- a/src/main/resources/assets/lightweight-inventory-sorting/lang/en_us.json +++ b/src/main/resources/assets/lightweight-inventory-sorting/lang/en_us.json @@ -7,7 +7,7 @@ "category.lightweight-inventory-sorting.sort-options": "Sorting Options", "category.lightweight-inventory-sorting.sort-type": "Sorting Type", - "category.lightweight-inventory-sorting.sort-type-tooltip": "Alphanumeric (A-Z), Reverse Alphanumeric (Z-A), or by Raw IDs", + "category.lightweight-inventory-sorting.sort-type-tooltip": "Alphanumeric (A-Z), Reverse Alphanumeric (Z-A)", "category.lightweight-inventory-sorting.sort-delay": "Sorting Delay", "category.lightweight-inventory-sorting.sort-delay-tooltip": "Increase delay between sorting steps (useful for multiplayer server errors)", diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 54808bf..4d16500 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -1,7 +1,7 @@ { "schemaVersion": 1, "id": "lightweight-inventory-sorting", - "version": "1.1.3+1.21.2", + "version": "${version}", "name": "Lightweight Inventory Sorting", "description": "Lightweight client-side inventory Sorting with no extra fluff", "authors": [ @@ -12,7 +12,7 @@ "demorogabrtz" ], "contact": { - "sources": "https://github.com/ebork11/LightweightInventorySorting/tree/main" + "sources": "https://github.com/borknbeans/LightweightInventorySorting/tree/main" }, "license": "MIT License", "icon": "assets/lightweight-inventory-sorting/logo.png", @@ -36,8 +36,8 @@ } ], "depends": { - "fabricloader": ">=0.16.9", - "minecraft": "~1.21.2", + "fabricloader": ">=0.16.14", + "minecraft": "~1.21.4", "java": ">=21", "fabric-api": "*" }, From d834640b2846d2767b0fecbde8b992242daada8b Mon Sep 17 00:00:00 2001 From: Ethan Date: Tue, 3 Jun 2025 15:19:19 -0700 Subject: [PATCH 2/2] gradle version changes to fix failures --- gradle/wrapper/gradle-wrapper.properties | 2 +- .../lightweight-inventory-sorting.client.mixins.json | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a441313..e18bc25 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/src/client/resources/lightweight-inventory-sorting.client.mixins.json b/src/client/resources/lightweight-inventory-sorting.client.mixins.json index 8c5f99d..ea5459d 100644 --- a/src/client/resources/lightweight-inventory-sorting.client.mixins.json +++ b/src/client/resources/lightweight-inventory-sorting.client.mixins.json @@ -3,9 +3,9 @@ "package": "borknbeans.lightweightinventorysorting.mixin.client", "compatibilityLevel": "JAVA_21", "client": [ - "MixinInventoryScreen", - "MixinGenericContainerScreen", - "MixinShulkerBoxScreen" + "InventoryScreenMixin", + "GenericContainerScreenMixin", + "ShulkerBoxScreenMixin" ], "injectors": { "defaultRequire": 1