From 768515dfe24813f3ed0f37a2eab091adbe8a92af Mon Sep 17 00:00:00 2001 From: North-West-Wind Date: Sat, 28 Mar 2026 09:43:07 +0800 Subject: [PATCH 01/52] ignore neoforge stuff --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 84269ca..856610b 100644 --- a/.gitignore +++ b/.gitignore @@ -19,7 +19,7 @@ build # other eclipse -run +run* .vscode # Files from Forge MDK From f94f7dc1f96df5d5f7410607497db61707592f68 Mon Sep 17 00:00:00 2001 From: North-West-Wind Date: Sat, 28 Mar 2026 10:34:13 +0800 Subject: [PATCH 02/52] refactor: 1.21.11 compat --- gradle.properties | 4 +- .../northwestwind/forgeautofish/AutoFish.java | 9 ++-- .../config/gui/CheckIntervalScreen.java | 15 +++---- .../config/gui/FilterSelectionScreen.java | 38 +++++++++-------- .../config/gui/RecastDelayScreen.java | 15 +++---- .../config/gui/ReelInDelayScreen.java | 15 +++---- .../config/gui/SettingsScreen.java | 1 - .../config/gui/SuperFilterScreen.java | 23 +++++----- .../config/gui/ThrowDelayScreen.java | 15 +++---- .../handler/AutoFishHandler.java | 42 +++++++++---------- .../forgeautofish/keybind/KeyBinds.java | 12 +++--- 11 files changed, 98 insertions(+), 91 deletions(-) diff --git a/gradle.properties b/gradle.properties index be1c1c7..2eacbf9 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,6 +4,6 @@ org.gradle.jvmargs=-Xmx3G org.gradle.daemon=false org.gradle.java.home=/usr/lib/jvm/java-21-graalvm-ee -mc_version=1.21.1 -forge_version=52.0.9 +mc_version=1.21.11 +forge_version=61.1.0 build_mc_version=1.21.x \ No newline at end of file diff --git a/src/main/java/ml/northwestwind/forgeautofish/AutoFish.java b/src/main/java/ml/northwestwind/forgeautofish/AutoFish.java index 61b8a88..76b7598 100644 --- a/src/main/java/ml/northwestwind/forgeautofish/AutoFish.java +++ b/src/main/java/ml/northwestwind/forgeautofish/AutoFish.java @@ -5,6 +5,7 @@ import net.minecraft.network.chat.MutableComponent; import net.minecraft.network.chat.contents.PlainTextContents; import net.minecraft.network.chat.contents.TranslatableContents; +import net.minecraftforge.client.event.RegisterKeyMappingsEvent; import net.minecraftforge.fml.IExtensionPoint; import net.minecraftforge.fml.ModLoadingContext; import net.minecraftforge.fml.common.Mod; @@ -20,13 +21,13 @@ public class AutoFish public static final String MODID = "forgeautofish"; public static final Logger LOGGER = LogManager.getLogger(); - public AutoFish() { - ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, Config.CLIENT); + public AutoFish(FMLJavaModLoadingContext context) { + context.registerConfig(ModConfig.Type.CLIENT, Config.CLIENT); - FMLJavaModLoadingContext.get().getModEventBus().addListener(KeyBinds::register); + RegisterKeyMappingsEvent.BUS.addListener(KeyBinds::register); Config.loadConfig(FMLPaths.CONFIGDIR.get().resolve("forgeautofish-client.toml").toString()); - ModLoadingContext.get().registerExtensionPoint(IExtensionPoint.DisplayTest.class, ()->new IExtensionPoint.DisplayTest(()->"ANY", (remote, isServer)-> true)); + context.registerExtensionPoint(IExtensionPoint.DisplayTest.class, ()->new IExtensionPoint.DisplayTest(()->"ANY", (remote, isServer)-> true)); } public static MutableComponent getTranslatableComponent(String key, Object... args) { diff --git a/src/main/java/ml/northwestwind/forgeautofish/config/gui/CheckIntervalScreen.java b/src/main/java/ml/northwestwind/forgeautofish/config/gui/CheckIntervalScreen.java index 92ff6a3..05626f4 100644 --- a/src/main/java/ml/northwestwind/forgeautofish/config/gui/CheckIntervalScreen.java +++ b/src/main/java/ml/northwestwind/forgeautofish/config/gui/CheckIntervalScreen.java @@ -8,6 +8,8 @@ import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.components.EditBox; import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.input.KeyEvent; +import net.minecraft.client.input.MouseButtonEvent; import org.lwjgl.glfw.GLFW; import java.util.regex.Pattern; @@ -25,9 +27,9 @@ protected CheckIntervalScreen(Screen parent) { protected void init() { checkInterval = new EditBox(this.font, this.width / 2 - 75, this.height / 2 - 25, 150, 20, AutoFish.getTranslatableComponent("gui.setcheckinterval.checkinterval")) { @Override - public boolean mouseClicked(double mouseX, double mouseY, int button) { - if (button == GLFW.GLFW_MOUSE_BUTTON_2) this.setValue(""); - return super.mouseClicked(mouseX, mouseY, button); + public boolean mouseClicked(MouseButtonEvent ev, boolean p_430750_) { + if (ev.button() == GLFW.GLFW_MOUSE_BUTTON_2) this.setValue(""); + return super.mouseClicked(ev, p_430750_); } }; checkInterval.setValue(Long.toString(AutoFishHandler.checkInterval)); @@ -62,7 +64,6 @@ public static boolean isNumeric(String strNum) { @Override public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) { - this.renderBackground(graphics, mouseX, mouseY, partialTicks); super.render(graphics, mouseX, mouseY, partialTicks); graphics.drawCenteredString(this.font, this.title, this.width / 2, 20, -1); this.checkInterval.render(graphics, mouseX, mouseY, partialTicks); @@ -74,9 +75,9 @@ public boolean shouldCloseOnEsc() { } @Override - public boolean keyPressed(int keyCode, int scanCode, int modifiers) { - if (keyCode == GLFW.GLFW_KEY_ESCAPE) Minecraft.getInstance().setScreen(parent); - return super.keyPressed(keyCode, scanCode, modifiers); + public boolean keyPressed(KeyEvent ev) { + if (ev.key() == GLFW.GLFW_KEY_ESCAPE) Minecraft.getInstance().setScreen(parent); + return super.keyPressed(ev); } @Override diff --git a/src/main/java/ml/northwestwind/forgeautofish/config/gui/FilterSelectionScreen.java b/src/main/java/ml/northwestwind/forgeautofish/config/gui/FilterSelectionScreen.java index afa6ed0..eb90bff 100644 --- a/src/main/java/ml/northwestwind/forgeautofish/config/gui/FilterSelectionScreen.java +++ b/src/main/java/ml/northwestwind/forgeautofish/config/gui/FilterSelectionScreen.java @@ -8,7 +8,9 @@ import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.components.EditBox; import net.minecraft.client.gui.screens.Screen; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.client.input.KeyEvent; +import net.minecraft.client.input.MouseButtonEvent; +import net.minecraft.resources.Identifier; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraftforge.registries.ForgeRegistries; @@ -26,7 +28,7 @@ public class FilterSelectionScreen extends Screen { private EditBox search; private final Collection original = ForgeRegistries.ITEMS.getValues(); private Collection searching; - private final Set selected = new HashSet<>(Config.FILTER.get().stream().map(string -> ForgeRegistries.ITEMS.getValue(ResourceLocation.parse(string))).collect(Collectors.toList())); + private final Set selected = new HashSet<>(Config.FILTER.get().stream().map(string -> ForgeRegistries.ITEMS.getValue(Identifier.parse(string))).collect(Collectors.toList())); private int page, maxPage = (int) Math.ceil(original.size() / 300.0), max = 300; private boolean clickProcessed = true; private double clickX, clickY; @@ -48,9 +50,9 @@ protected void init() { searching = original; search = new EditBox(this.font, this.width / 2 - 75, 35, 150, 20, AutoFish.getTranslatableComponent("gui.superfilterscreen.search")) { @Override - public boolean mouseClicked(double mouseX, double mouseY, int button) { - if (button == GLFW.GLFW_MOUSE_BUTTON_2) this.setValue(""); - return super.mouseClicked(mouseX, mouseY, button); + public boolean mouseClicked(MouseButtonEvent ev, boolean p_430750_) { + if (ev.button() == GLFW.GLFW_MOUSE_BUTTON_2) this.setValue(""); + return super.mouseClicked(ev, p_430750_); } }; search.setResponder(s -> { @@ -59,7 +61,7 @@ public boolean mouseClicked(double mouseX, double mouseY, int button) { String[] tags = Arrays.stream(args).filter(s1 -> s1.startsWith("#")).toArray(String[]::new); String[] finalArgs = Arrays.stream(args).filter(s1 -> !s1.startsWith("@") && !s1.startsWith("#")).toArray(String[]::new);; searching = original.stream().filter(item -> { - ResourceLocation rl = ForgeRegistries.ITEMS.getKey(item); + Identifier rl = ForgeRegistries.ITEMS.getKey(item); boolean matchmod = mods.length < 1, matchtag = tags.length < 1, matcharg = finalArgs.length < 1; for (String mod : mods) { mod = mod.toLowerCase().substring(1); @@ -73,7 +75,7 @@ public boolean mouseClicked(double mouseX, double mouseY, int button) { } for (String arg : finalArgs) { arg = arg.toLowerCase(); - if (rl != null) matcharg = rl.getPath().contains(arg) || item.getDescription().getString().contains(arg); + if (rl != null) matcharg = rl.getPath().contains(arg); } return matchmod && matchtag && matcharg; }).collect(Collectors.toList()); @@ -99,16 +101,15 @@ public boolean mouseClicked(double mouseX, double mouseY, int button) { @Override public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) { - this.renderBackground(graphics, mouseX, mouseY, partialTicks); super.render(graphics, mouseX, mouseY, partialTicks); graphics.drawCenteredString(this.font, this.title, this.width / 2, 20, -1);Collection searchingCopy = Lists.newArrayList(); Collection prioritized = searching.stream().filter(item -> { - ResourceLocation rl = ForgeRegistries.ITEMS.getKey(item); + Identifier rl = ForgeRegistries.ITEMS.getKey(item); if (rl == null) return false; boolean pri = Config.PRIORITIZE.get().contains(rl.toString()); if (!pri) searchingCopy.add(item); return pri; - }).collect(Collectors.toList()); + }).toList(); Item[] items = Stream.concat(prioritized.stream(), searchingCopy.stream()).toArray(Item[]::new); if (items.length > 0 && page >= 0) { for (int i = page * max; i < Math.min((page + 1) * max, searching.size()); i++) { @@ -127,7 +128,8 @@ public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTi } if (selected.contains(item)) graphics.fillGradient(x - 2, y - 2, x + 18, y + 18, Color.GREEN.getRGB(), Color.GREEN.getRGB()); else if (isMouseInRange(mouseX, mouseY, x, y,x + 16, y + 16)) graphics.fillGradient(x - 2, y - 2, x + 18, y + 18, Color.LIGHT_GRAY.getRGB(), Color.LIGHT_GRAY.getRGB()); - if (isMouseInRange(mouseX, mouseY, x, y,x + 16, y + 16)) graphics.renderTooltip(this.font, stack, mouseX, mouseY); + //if (isMouseInRange(mouseX, mouseY, x, y,x + 16, y + 16)) graphics.item(this.font, stack, mouseX, mouseY); + graphics.renderItem(stack, x, y); } } } @@ -147,20 +149,20 @@ private int getYPos(int k, int height) { } @Override - public boolean keyPressed(int keyCode, int scanCode, int modifiers) { - if (keyCode == GLFW.GLFW_KEY_ESCAPE) { + public boolean keyPressed(KeyEvent ev) { + if (ev.key() == GLFW.GLFW_KEY_ESCAPE) { if (!search.isFocused()) Minecraft.getInstance().setScreen(parent); else search.setFocused(false); } - return super.keyPressed(keyCode, scanCode, modifiers); + return super.keyPressed(ev); } @Override - public boolean mouseClicked(double mouseX, double mouseY, int button) { - clickX = mouseX; - clickY = mouseY; + public boolean mouseClicked(MouseButtonEvent ev, boolean flag) { + clickX = ev.x(); + clickY = ev.y(); clickProcessed = false; - return super.mouseClicked(mouseX, mouseY, button); + return super.mouseClicked(ev, flag); } @Override diff --git a/src/main/java/ml/northwestwind/forgeautofish/config/gui/RecastDelayScreen.java b/src/main/java/ml/northwestwind/forgeautofish/config/gui/RecastDelayScreen.java index 755a80d..0cc62b4 100644 --- a/src/main/java/ml/northwestwind/forgeautofish/config/gui/RecastDelayScreen.java +++ b/src/main/java/ml/northwestwind/forgeautofish/config/gui/RecastDelayScreen.java @@ -8,6 +8,8 @@ import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.components.EditBox; import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.input.KeyEvent; +import net.minecraft.client.input.MouseButtonEvent; import org.lwjgl.glfw.GLFW; import java.util.regex.Pattern; @@ -25,9 +27,9 @@ protected RecastDelayScreen(Screen parent) { protected void init() { recastDelay = new EditBox(this.font, this.width / 2 - 75, this.height / 2 - 25, 150, 20, AutoFish.getTranslatableComponent("gui.setrecastdelay.recastdelay")) { @Override - public boolean mouseClicked(double mouseX, double mouseY, int button) { - if (button == GLFW.GLFW_MOUSE_BUTTON_2) this.setValue(""); - return super.mouseClicked(mouseX, mouseY, button); + public boolean mouseClicked(MouseButtonEvent ev, boolean flag) { + if (ev.button() == GLFW.GLFW_MOUSE_BUTTON_2) this.setValue(""); + return super.mouseClicked(ev, flag); } }; recastDelay.setValue(Long.toString(AutoFishHandler.recastDelay)); @@ -62,7 +64,6 @@ public static boolean isNumeric(String strNum) { @Override public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) { - this.renderBackground(graphics, mouseX, mouseY, partialTicks); super.render(graphics, mouseX, mouseY, partialTicks); graphics.drawCenteredString(this.font, this.title, this.width / 2, 20, -1); this.recastDelay.render(graphics, mouseX, mouseY, partialTicks); @@ -74,9 +75,9 @@ public boolean shouldCloseOnEsc() { } @Override - public boolean keyPressed(int keyCode, int scanCode, int modifiers) { - if (keyCode == GLFW.GLFW_KEY_ESCAPE) Minecraft.getInstance().setScreen(parent); - return super.keyPressed(keyCode, scanCode, modifiers); + public boolean keyPressed(KeyEvent ev) { + if (ev.key() == GLFW.GLFW_KEY_ESCAPE) Minecraft.getInstance().setScreen(parent); + return super.keyPressed(ev); } @Override diff --git a/src/main/java/ml/northwestwind/forgeautofish/config/gui/ReelInDelayScreen.java b/src/main/java/ml/northwestwind/forgeautofish/config/gui/ReelInDelayScreen.java index 74161b2..a67de9c 100644 --- a/src/main/java/ml/northwestwind/forgeautofish/config/gui/ReelInDelayScreen.java +++ b/src/main/java/ml/northwestwind/forgeautofish/config/gui/ReelInDelayScreen.java @@ -8,6 +8,8 @@ import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.components.EditBox; import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.input.KeyEvent; +import net.minecraft.client.input.MouseButtonEvent; import org.lwjgl.glfw.GLFW; import java.util.regex.Pattern; @@ -25,9 +27,9 @@ protected ReelInDelayScreen(Screen parent) { protected void init() { reelInDelay = new EditBox(this.font, this.width / 2 - 75, this.height / 2 - 25, 150, 20, AutoFish.getTranslatableComponent("gui.setreelindelay.reelindelay")) { @Override - public boolean mouseClicked(double mouseX, double mouseY, int button) { - if (button == GLFW.GLFW_MOUSE_BUTTON_2) this.setValue(""); - return super.mouseClicked(mouseX, mouseY, button); + public boolean mouseClicked(MouseButtonEvent ev, boolean flag) { + if (ev.button() == GLFW.GLFW_MOUSE_BUTTON_2) this.setValue(""); + return super.mouseClicked(ev, flag); } }; reelInDelay.setValue(Long.toString(AutoFishHandler.reelInDelay)); @@ -62,7 +64,6 @@ public static boolean isNumeric(String strNum) { @Override public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) { - this.renderBackground(graphics, mouseX, mouseY, partialTicks); super.render(graphics, mouseX, mouseY, partialTicks); graphics.drawCenteredString(this.font, this.title, this.width / 2, 20, -1); this.reelInDelay.render(graphics, mouseX, mouseY, partialTicks); @@ -74,9 +75,9 @@ public boolean shouldCloseOnEsc() { } @Override - public boolean keyPressed(int keyCode, int scanCode, int modifiers) { - if (keyCode == GLFW.GLFW_KEY_ESCAPE) Minecraft.getInstance().setScreen(parent); - return super.keyPressed(keyCode, scanCode, modifiers); + public boolean keyPressed(KeyEvent ev) { + if (ev.key() == GLFW.GLFW_KEY_ESCAPE) Minecraft.getInstance().setScreen(parent); + return super.keyPressed(ev); } @Override diff --git a/src/main/java/ml/northwestwind/forgeautofish/config/gui/SettingsScreen.java b/src/main/java/ml/northwestwind/forgeautofish/config/gui/SettingsScreen.java index 94184b2..21aeb72 100644 --- a/src/main/java/ml/northwestwind/forgeautofish/config/gui/SettingsScreen.java +++ b/src/main/java/ml/northwestwind/forgeautofish/config/gui/SettingsScreen.java @@ -40,7 +40,6 @@ protected void init() { @Override public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) { - this.renderBackground(graphics, mouseX, mouseY, partialTicks); super.render(graphics, mouseX, mouseY, partialTicks); graphics.drawCenteredString(this.font, this.title, this.width / 2, 20, -1); } diff --git a/src/main/java/ml/northwestwind/forgeautofish/config/gui/SuperFilterScreen.java b/src/main/java/ml/northwestwind/forgeautofish/config/gui/SuperFilterScreen.java index 0bdb201..8954757 100644 --- a/src/main/java/ml/northwestwind/forgeautofish/config/gui/SuperFilterScreen.java +++ b/src/main/java/ml/northwestwind/forgeautofish/config/gui/SuperFilterScreen.java @@ -7,7 +7,9 @@ import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.components.EditBox; import net.minecraft.client.gui.screens.Screen; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.client.input.KeyEvent; +import net.minecraft.client.input.MouseButtonEvent; +import net.minecraft.resources.Identifier; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraftforge.registries.ForgeRegistries; @@ -47,14 +49,14 @@ protected void init() { reducedHeight = this.height - 90; reducedWidth = this.width - 30; max = /* (int) Math.round(30 * (reducedWidth / 550.0 + reducedHeight / 330.0) / 2.0) */ 30; - original = Config.FILTER.get().stream().map(string -> ForgeRegistries.ITEMS.getValue(ResourceLocation.parse(string))).collect(Collectors.toList()); + original = Config.FILTER.get().stream().map(string -> ForgeRegistries.ITEMS.getValue(Identifier.parse(string))).collect(Collectors.toList()); maxPage = (int) Math.ceil(original.size() / (double) max); searching = original; search = new EditBox(this.font, this.width / 2 - 75, 35, 150, 20, AutoFish.getTranslatableComponent("gui.superfilterscreen.search")) { @Override - public boolean mouseClicked(double mouseX, double mouseY, int button) { - if (button == GLFW.GLFW_MOUSE_BUTTON_2) this.setValue(""); - return super.mouseClicked(mouseX, mouseY, button); + public boolean mouseClicked(MouseButtonEvent ev, boolean flag) { + if (ev.button() == GLFW.GLFW_MOUSE_BUTTON_2) this.setValue(""); + return super.mouseClicked(ev, flag); } }; search.setResponder(s -> { @@ -63,7 +65,7 @@ public boolean mouseClicked(double mouseX, double mouseY, int button) { String[] tags = Arrays.stream(args).filter(s1 -> s1.startsWith("#")).toArray(String[]::new); String[] finalArgs = Arrays.stream(args).filter(s1 -> !s1.startsWith("@") && !s1.startsWith("#")).toArray(String[]::new);; searching = original.stream().filter(item -> { - ResourceLocation rl = ForgeRegistries.ITEMS.getKey(item); + Identifier rl = ForgeRegistries.ITEMS.getKey(item); boolean matchmod = mods.length < 1, matchtag = tags.length < 1, matcharg = finalArgs.length < 1; for (String mod : mods) { mod = mod.toLowerCase().substring(1); @@ -77,7 +79,7 @@ public boolean mouseClicked(double mouseX, double mouseY, int button) { } for (String arg : finalArgs) { arg = arg.toLowerCase(); - if (rl != null) matcharg = rl.getPath().contains(arg) || item.getDescription().getString().contains(arg); + if (rl != null) matcharg = rl.getPath().contains(arg); } return matchmod && matchtag && matcharg; }).collect(Collectors.toList()); @@ -99,7 +101,6 @@ public boolean mouseClicked(double mouseX, double mouseY, int button) { @Override public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) { - this.renderBackground(graphics, mouseX, mouseY, partialTicks); super.render(graphics, mouseX, mouseY, partialTicks); graphics.drawCenteredString(this.font, this.title, this.width / 2, 20, -1); Item[] items = searching.toArray(new Item[0]); @@ -122,9 +123,9 @@ public boolean shouldCloseOnEsc() { } @Override - public boolean keyPressed(int keyCode, int scanCode, int modifiers) { - if (keyCode == GLFW.GLFW_KEY_ESCAPE) Minecraft.getInstance().setScreen(parent); - return super.keyPressed(keyCode, scanCode, modifiers); + public boolean keyPressed(KeyEvent ev) { + if (ev.key() == GLFW.GLFW_KEY_ESCAPE) Minecraft.getInstance().setScreen(parent); + return super.keyPressed(ev); } @Override diff --git a/src/main/java/ml/northwestwind/forgeautofish/config/gui/ThrowDelayScreen.java b/src/main/java/ml/northwestwind/forgeautofish/config/gui/ThrowDelayScreen.java index eb1d351..2e6e056 100644 --- a/src/main/java/ml/northwestwind/forgeautofish/config/gui/ThrowDelayScreen.java +++ b/src/main/java/ml/northwestwind/forgeautofish/config/gui/ThrowDelayScreen.java @@ -8,6 +8,8 @@ import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.components.EditBox; import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.input.KeyEvent; +import net.minecraft.client.input.MouseButtonEvent; import org.lwjgl.glfw.GLFW; import java.util.regex.Pattern; @@ -25,9 +27,9 @@ protected ThrowDelayScreen(Screen parent) { protected void init() { throwDelay = new EditBox(this.font, this.width / 2 - 75, this.height / 2 - 25, 150, 20, AutoFish.getTranslatableComponent("gui.setthrowdelay.throwdelay")) { @Override - public boolean mouseClicked(double mouseX, double mouseY, int button) { - if (button == GLFW.GLFW_MOUSE_BUTTON_2) this.setValue(""); - return super.mouseClicked(mouseX, mouseY, button); + public boolean mouseClicked(MouseButtonEvent ev, boolean flag) { + if (ev.button() == GLFW.GLFW_MOUSE_BUTTON_2) this.setValue(""); + return super.mouseClicked(ev, flag); } }; throwDelay.setValue(Long.toString(AutoFishHandler.throwDelay)); @@ -62,7 +64,6 @@ public static boolean isNumeric(String strNum) { @Override public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) { - this.renderBackground(graphics, mouseX, mouseY, partialTicks); super.render(graphics, mouseX, mouseY, partialTicks); graphics.drawCenteredString(this.font, this.title, this.width / 2, 20, -1); this.throwDelay.render(graphics, mouseX, mouseY, partialTicks); @@ -74,9 +75,9 @@ public boolean shouldCloseOnEsc() { } @Override - public boolean keyPressed(int keyCode, int scanCode, int modifiers) { - if (keyCode == GLFW.GLFW_KEY_ESCAPE) Minecraft.getInstance().setScreen(parent); - return super.keyPressed(keyCode, scanCode, modifiers); + public boolean keyPressed(KeyEvent ev) { + if (ev.key() == GLFW.GLFW_KEY_ESCAPE) Minecraft.getInstance().setScreen(parent); + return super.keyPressed(ev); } @Override diff --git a/src/main/java/ml/northwestwind/forgeautofish/handler/AutoFishHandler.java b/src/main/java/ml/northwestwind/forgeautofish/handler/AutoFishHandler.java index e1c2601..132f6ac 100644 --- a/src/main/java/ml/northwestwind/forgeautofish/handler/AutoFishHandler.java +++ b/src/main/java/ml/northwestwind/forgeautofish/handler/AutoFishHandler.java @@ -10,7 +10,7 @@ import net.minecraft.client.multiplayer.MultiPlayerGameMode; import net.minecraft.client.player.LocalPlayer; import net.minecraft.network.chat.Component; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.world.InteractionHand; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.FishingRodItem; @@ -20,7 +20,7 @@ import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.client.event.InputEvent; import net.minecraftforge.event.TickEvent; -import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.eventbus.api.listener.SubscribeEvent; import net.minecraftforge.fml.LogicalSide; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.registries.ForgeRegistries; @@ -34,10 +34,9 @@ public class AutoFishHandler { public static long recastDelay = Config.RECAST_DELAY.get(), reelInDelay = Config.REEL_IN_DELAY.get(), throwDelay = Config.THROW_DELAY.get(), checkInterval = Config.CHECK_INTERVAL.get(); private static final List shouldDrop = Lists.newArrayList(); private static boolean processingDrop, pendingReelIn, pendingRecast, lastTickFishing, afterDrop; - private static int dropCd; + private static int dropCd, rodSlot; private static long tick, checkTick; private static List itemsBeforeFished; - private static ItemStack rodStack; @SubscribeEvent public static void onKeyInput(InputEvent.Key e) { @@ -61,9 +60,9 @@ public static void onKeyInput(InputEvent.Key e) { } @SubscribeEvent - public static void onPlayerTick(final TickEvent.PlayerTickEvent e) { - if (e.side != LogicalSide.CLIENT || !e.phase.equals(TickEvent.Phase.START)) return; - Player player = e.player; + public static void onPlayerTick(final TickEvent.PlayerTickEvent.Pre ev) { + if (ev.side() != LogicalSide.CLIENT) return; + Player player = ev.player(); if (!player.getUUID().equals(Minecraft.getInstance().player.getUUID())) return; if (checkTick > 0) checkTick--; else { @@ -74,12 +73,12 @@ public static void onPlayerTick(final TickEvent.PlayerTickEvent e) { } } if (lastTickFishing && player.fishing == null) - itemsBeforeFished = Lists.newArrayList(player.getInventory().items); + itemsBeforeFished = Lists.newArrayList(player.getInventory().getNonEquipmentItems()); lastTickFishing = player.fishing != null; if (afterDrop) { - if (tick == 0 && rodStack != null) { - player.getInventory().setPickedItem(rodStack); - rodStack = null; + if (tick == 0 && rodSlot != -1) { + player.getInventory().setSelectedSlot(rodSlot); + rodSlot = -1; } tick++; if (tick > 2) { @@ -100,7 +99,7 @@ public static void onPlayerTick(final TickEvent.PlayerTickEvent e) { if (processingDrop) { if (dropCd > 0) dropCd--; dropItem(player); - if (shouldDrop.size() <= 0) { + if (shouldDrop.isEmpty()) { processingDrop = false; afterDrop = true; } @@ -150,12 +149,12 @@ else if (fishingRod.getMaxDamage() - fishingRod.getDamageValue() < 3 && !player. AutoFish.LOGGER.info("Fishing rod broke. Finding replacement..."); boolean found = false; for (int i = 0; i < 9; i++) { - if (i == player.getInventory().selected) continue; + if (i == player.getInventory().getSelectedSlot()) continue; ItemStack stack = player.getInventory().getItem(i); if (stack.getItem() instanceof FishingRodItem) { if (rodprotect && stack.getMaxDamage() - stack.getDamageValue() < 2) continue; AutoFish.LOGGER.info("Found fishing rod for replacement"); - player.getInventory().selected = i; + player.getInventory().setSelectedSlot(i); found = true; break; } @@ -176,9 +175,9 @@ private static void recast(Player player) { private static void checkItem(Player player) { if (itemsBeforeFished != null) { - List items = player.getInventory().items; + List items = player.getInventory().getNonEquipmentItems(); for (String name : Config.FILTER.get()) { - ResourceLocation rl = ResourceLocation.parse(name); + Identifier rl = Identifier.parse(name); Item item = ForgeRegistries.ITEMS.getValue(rl); if (item == null) continue; int newCount = items.stream().filter(stack -> stack.getItem().equals(item)).mapToInt(ItemStack::getCount).reduce(Integer::sum).orElse(0); @@ -187,25 +186,24 @@ private static void checkItem(Player player) { for (int ii = 0; ii < diff; ii++) shouldDrop.add(item); } itemsBeforeFished = null; - if (shouldDrop.size() > 0) { + if (!shouldDrop.isEmpty()) { processingDrop = true; - rodStack = player.getMainHandItem(); + rodSlot = player.getInventory().getSelectedSlot(); } } } private static void dropItem(Player player) { if (dropCd == 4 || dropCd == 2 || dropCd == 1) return; - Item item = shouldDrop.get(0); + Item item = shouldDrop.getFirst(); if (dropCd == 3) { ((LocalPlayer) player).drop(false); shouldDrop.remove(item); return; } for (int ii = 0; ii < 9; ii++) { - final ItemStack stack = player.getInventory().items.get(ii); - if (!stack.getItem().equals(item)) continue; - player.getInventory().setPickedItem(stack); + if (!player.getInventory().getItem(ii).getItem().equals(item)) continue; + player.getInventory().setSelectedSlot(ii); dropCd = 5; return; } diff --git a/src/main/java/ml/northwestwind/forgeautofish/keybind/KeyBinds.java b/src/main/java/ml/northwestwind/forgeautofish/keybind/KeyBinds.java index ae945ff..af0ede5 100644 --- a/src/main/java/ml/northwestwind/forgeautofish/keybind/KeyBinds.java +++ b/src/main/java/ml/northwestwind/forgeautofish/keybind/KeyBinds.java @@ -2,6 +2,7 @@ import ml.northwestwind.forgeautofish.AutoFish; import net.minecraft.client.KeyMapping; +import net.minecraft.resources.Identifier; import net.minecraftforge.client.event.RegisterKeyMappingsEvent; import org.lwjgl.glfw.GLFW; @@ -10,11 +11,12 @@ public class KeyBinds { public static KeyMapping autofish, rodprotect, autoreplace, settings, itemfilter; public static void register(final RegisterKeyMappingsEvent event) { - autofish = new KeyMapping(AutoFish.getTranslatableComponent("key.forgeautofish.autofish").getString(), GLFW.GLFW_KEY_MINUS, "key.categories.forgeautofish"); - rodprotect = new KeyMapping(AutoFish.getTranslatableComponent("key.forgeautofish.rodprotect").getString(), GLFW.GLFW_KEY_BACKSLASH, "key.categories.forgeautofish"); - autoreplace = new KeyMapping(AutoFish.getTranslatableComponent("key.forgeautofish.autoreplace").getString(), GLFW.GLFW_KEY_RIGHT_BRACKET, "key.categories.forgeautofish"); - settings = new KeyMapping(AutoFish.getTranslatableComponent("key.forgeautofish.settings").getString(), GLFW.GLFW_KEY_K, "key.categories.forgeautofish"); - itemfilter = new KeyMapping(AutoFish.getTranslatableComponent("key.forgeautofish.itemfilter").getString(), GLFW.GLFW_KEY_APOSTROPHE, "key.categories.forgeautofish"); + KeyMapping.Category cat = KeyMapping.Category.register(Identifier.fromNamespaceAndPath(AutoFish.MODID, "autofish")); + autofish = new KeyMapping(AutoFish.getTranslatableComponent("key.forgeautofish.autofish").getString(), GLFW.GLFW_KEY_MINUS, cat); + rodprotect = new KeyMapping(AutoFish.getTranslatableComponent("key.forgeautofish.rodprotect").getString(), GLFW.GLFW_KEY_BACKSLASH, cat); + autoreplace = new KeyMapping(AutoFish.getTranslatableComponent("key.forgeautofish.autoreplace").getString(), GLFW.GLFW_KEY_RIGHT_BRACKET, cat); + settings = new KeyMapping(AutoFish.getTranslatableComponent("key.forgeautofish.settings").getString(), GLFW.GLFW_KEY_K, cat); + itemfilter = new KeyMapping(AutoFish.getTranslatableComponent("key.forgeautofish.itemfilter").getString(), GLFW.GLFW_KEY_APOSTROPHE, cat); event.register(autofish); event.register(rodprotect); From a49ebc1843e126c20a9cce703e0c342e9ef779ac Mon Sep 17 00:00:00 2001 From: North-West-Wind Date: Sat, 28 Mar 2026 10:36:48 +0800 Subject: [PATCH 03/52] build: identifiable build version --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 2eacbf9..de3c7e9 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,4 +6,4 @@ org.gradle.java.home=/usr/lib/jvm/java-21-graalvm-ee mc_version=1.21.11 forge_version=61.1.0 -build_mc_version=1.21.x \ No newline at end of file +build_mc_version=1.21.11-forge \ No newline at end of file From 54d64f2414bd2d44e8c28cf830e8d8862afe26e2 Mon Sep 17 00:00:00 2001 From: North-West-Wind Date: Sat, 27 Jun 2026 20:14:27 +0800 Subject: [PATCH 04/52] refactor: 26.1.2 compat --- build.gradle | 95 +++++------------- gradle.properties | 21 ++-- gradle/wrapper/gradle-wrapper.jar | Bin 62076 -> 43583 bytes gradle/wrapper/gradle-wrapper.properties | 3 +- settings.gradle | 11 +- .../northwestwind/forgeautofish/AutoFish.java | 3 +- .../config/gui/CheckIntervalScreen.java | 10 +- .../config/gui/FilterSelectionScreen.java | 15 +-- .../config/gui/RecastDelayScreen.java | 10 +- .../config/gui/ReelInDelayScreen.java | 10 +- .../config/gui/SettingsScreen.java | 9 +- .../config/gui/SuperFilterScreen.java | 14 +-- .../config/gui/ThrowDelayScreen.java | 10 +- .../handler/AutoFishHandler.java | 10 +- src/main/resources/META-INF/mods.toml | 4 +- 15 files changed, 96 insertions(+), 129 deletions(-) diff --git a/build.gradle b/build.gradle index 74abb83..7c3b48f 100644 --- a/build.gradle +++ b/build.gradle @@ -1,98 +1,57 @@ plugins { - id 'eclipse' + id 'java' id 'idea' - id 'maven-publish' - id 'net.minecraftforge.gradle' version '[6.0.24,6.2)' + id 'eclipse' + id 'net.minecraftforge.gradle' version '[7.0.17,8)' } -version = '7.1.0' +version = mod_version group = 'ml.northwestwind.forgeautofish' base { - archivesName = "forgeautofish" + archivesName = "$mod_id-forge-$mc_version" } -java.toolchain.languageVersion = JavaLanguageVersion.of(21) +java.toolchain.languageVersion = JavaLanguageVersion.of(25) println('Java: ' + System.getProperty('java.version') + ' JVM: ' + System.getProperty('java.vm.version') + '(' + System.getProperty('java.vendor') + ') Arch: ' + System.getProperty('os.arch')) minecraft { - mappings channel: 'official', version: mc_version - reobf = false - copyIdeResources = true runs { configureEach { - workingDirectory project.file('run') - property 'forge.logging.markers', 'REGISTRIES' - property 'forge.logging.console.level', 'debug' - } - client { - property 'forge.enabledGameTestNamespaces', "forgeautofish" + workingDir = layout.projectDirectory.dir('run') + + systemProperty 'eventbus.api.strictRuntimeChecks', 'true' + systemProperty 'forge.enabledGameTestNamespaces', 'forgeautofish' } - server { - property 'forge.enabledGameTestNamespaces', "forgeautofish" + register('client') + + register('server') { args '--nogui' } + register('gameTestServer') + data { - workingDirectory project.file('run-data') - args '--mod', "forgeautofish", '--all', '--output', file('src/generated/resources/'), '--existing', file('src/main/resources/') + workingDir = layout.projectDirectory.dir('run-data') + + args '--mod', "forgeautofish", '--all', '--output', layout.projectDirectory.dir('src/generated/resources'), '--existing', layout.projectDirectory.dir('src/main/resources') } } } -sourceSets.main.resources { srcDir 'src/generated/resources' } - -dependencies { - minecraft "net.minecraftforge:forge:${mc_version}-${forge_version}" - implementation('net.sf.jopt-simple:jopt-simple:5.0.4') { version { strictly '5.0.4' } } -} - -tasks.named('jar', Jar).configure { - manifest { - archiveClassifier = build_mc_version - attributes([ - "Specification-Title": "forgeautofish", - "Specification-Vendor": "forgeautofish", - "Specification-Version": "1", // We are version 1 of ourselves - "Implementation-Title": project.name, - "Implementation-Version": "${version}", - "Implementation-Vendor" :"forgeautofish", - "Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ") - ]) - } +repositories { + minecraft.mavenizer(it) // In Kotlin, it = this + maven fg.forgeMaven + maven fg.minecraftLibsMaven + mavenCentral() } -publishing { - publications { - mavenJava(MavenPublication) { - artifact jar - } - } - repositories { - maven { - url "file://${project.projectDir}/mcmodsrepo" - } - } +dependencies { + implementation minecraft.dependency("net.minecraftforge:forge:${mc_version}-${forge_version}") + annotationProcessor 'net.minecraftforge:eventbus-validator:7.0.1' } tasks.withType(JavaCompile).configureEach { options.encoding = 'UTF-8' // Use the UTF-8 charset for Java compilation -} - -eclipse { - // Run everytime eclipse builds the code - //autoBuildTasks genEclipseRuns - // Run when importing the project - synchronizationTasks 'genEclipseRuns' -} - -// Merge the resources and classes into the same directory. -// This is done because java expects modules to be in a single directory. -// And if we have it in multiple we have to do performance intensive hacks like having the UnionFileSystem -// This will eventually be migrated to ForgeGradle so modders don't need to manually do it. But that is later. -sourceSets.each { - def dir = layout.buildDirectory.dir("sourcesSets/$it.name") - it.output.resourcesDir = dir - it.java.destinationDirectory = dir -} +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index de3c7e9..29874b8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,9 +1,18 @@ -# Sets default memory used for gradle commands. Can be overridden by user or command line properties. -# This is required to provide enough memory for the Minecraft decompilation process. +org.gradle.caching=true +org.gradle.parallel=true +org.gradle.configureondemand=true + +org.gradle.configuration-cache=true +org.gradle.configuration-cache.parallel=true +org.gradle.configuration-cache.problems=warn + +net.minecraftforge.gradle.merge-source-sets=true + org.gradle.jvmargs=-Xmx3G -org.gradle.daemon=false org.gradle.java.home=/usr/lib/jvm/java-21-graalvm-ee -mc_version=1.21.11 -forge_version=61.1.0 -build_mc_version=1.21.11-forge \ No newline at end of file +mc_version=26.1.2 +forge_version=64.0.10 +build_mc_version=26.1.2-forge +mod_id=forgeautofish +mod_version=7.1.0 \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index c1962a79e29d3e0ab67b14947c167a862655af9b..a4b76b9530d66f5e68d973ea569d8e19de379189 100644 GIT binary patch literal 43583 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 yu?e%!_ptzGB|8GrCfu%p?(_ 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 GZ~q4|mN%#X literal 62076 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index d951fac..8d9046d 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.1-bin.zip +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/settings.gradle b/settings.gradle index 3703f36..1cba304 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,6 +1,5 @@ -pluginManagement { - repositories { - gradlePluginPortal() - maven { url = 'https://maven.minecraftforge.net/' } - } -} \ No newline at end of file +plugins { + id 'org.gradle.toolchains.foojay-resolver-convention' version '1.0.0' +} + +rootProject.name = 'forgeautofish' diff --git a/src/main/java/ml/northwestwind/forgeautofish/AutoFish.java b/src/main/java/ml/northwestwind/forgeautofish/AutoFish.java index 76b7598..d936f8c 100644 --- a/src/main/java/ml/northwestwind/forgeautofish/AutoFish.java +++ b/src/main/java/ml/northwestwind/forgeautofish/AutoFish.java @@ -7,7 +7,6 @@ import net.minecraft.network.chat.contents.TranslatableContents; import net.minecraftforge.client.event.RegisterKeyMappingsEvent; import net.minecraftforge.fml.IExtensionPoint; -import net.minecraftforge.fml.ModLoadingContext; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.config.ModConfig; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; @@ -15,7 +14,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -@Mod("forgeautofish") +@Mod(AutoFish.MODID) public class AutoFish { public static final String MODID = "forgeautofish"; diff --git a/src/main/java/ml/northwestwind/forgeautofish/config/gui/CheckIntervalScreen.java b/src/main/java/ml/northwestwind/forgeautofish/config/gui/CheckIntervalScreen.java index 05626f4..ef35f7c 100644 --- a/src/main/java/ml/northwestwind/forgeautofish/config/gui/CheckIntervalScreen.java +++ b/src/main/java/ml/northwestwind/forgeautofish/config/gui/CheckIntervalScreen.java @@ -4,7 +4,7 @@ import ml.northwestwind.forgeautofish.config.Config; import ml.northwestwind.forgeautofish.handler.AutoFishHandler; import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.components.EditBox; import net.minecraft.client.gui.screens.Screen; @@ -63,10 +63,10 @@ public static boolean isNumeric(String strNum) { } @Override - public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) { - super.render(graphics, mouseX, mouseY, partialTicks); - graphics.drawCenteredString(this.font, this.title, this.width / 2, 20, -1); - this.checkInterval.render(graphics, mouseX, mouseY, partialTicks); + public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float partialTicks) { + super.extractRenderState(graphics, mouseX, mouseY, partialTicks); + graphics.centeredText(this.font, this.title, this.width / 2, 20, -1); + this.checkInterval.extractRenderState(graphics, mouseX, mouseY, partialTicks); } @Override diff --git a/src/main/java/ml/northwestwind/forgeautofish/config/gui/FilterSelectionScreen.java b/src/main/java/ml/northwestwind/forgeautofish/config/gui/FilterSelectionScreen.java index eb90bff..c01dfad 100644 --- a/src/main/java/ml/northwestwind/forgeautofish/config/gui/FilterSelectionScreen.java +++ b/src/main/java/ml/northwestwind/forgeautofish/config/gui/FilterSelectionScreen.java @@ -4,7 +4,7 @@ import ml.northwestwind.forgeautofish.AutoFish; import ml.northwestwind.forgeautofish.config.Config; import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.components.EditBox; import net.minecraft.client.gui.screens.Screen; @@ -100,9 +100,10 @@ public boolean mouseClicked(MouseButtonEvent ev, boolean p_430750_) { } @Override - public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) { - super.render(graphics, mouseX, mouseY, partialTicks); - graphics.drawCenteredString(this.font, this.title, this.width / 2, 20, -1);Collection searchingCopy = Lists.newArrayList(); + public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float partialTicks) { + super.extractRenderState(graphics, mouseX, mouseY, partialTicks); + graphics.centeredText(this.font, this.title, this.width / 2, 20, -1); + Collection searchingCopy = Lists.newArrayList(); Collection prioritized = searching.stream().filter(item -> { Identifier rl = ForgeRegistries.ITEMS.getKey(item); if (rl == null) return false; @@ -120,7 +121,7 @@ public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTi int y = getYPos(k, reducedHeight); ItemStack stack = new ItemStack(item); if (!stack.isEmpty()) { - graphics.renderItem(stack, x, y); + graphics.item(stack, x, y); if (!clickProcessed && isMouseInRange(clickX, clickY, x, y, x+16, y+16)) { if (selected.contains(item)) selected.remove(item); else selected.add(item); @@ -129,11 +130,11 @@ public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTi if (selected.contains(item)) graphics.fillGradient(x - 2, y - 2, x + 18, y + 18, Color.GREEN.getRGB(), Color.GREEN.getRGB()); else if (isMouseInRange(mouseX, mouseY, x, y,x + 16, y + 16)) graphics.fillGradient(x - 2, y - 2, x + 18, y + 18, Color.LIGHT_GRAY.getRGB(), Color.LIGHT_GRAY.getRGB()); //if (isMouseInRange(mouseX, mouseY, x, y,x + 16, y + 16)) graphics.item(this.font, stack, mouseX, mouseY); - graphics.renderItem(stack, x, y); + graphics.item(stack, x, y); } } } - search.render(graphics, mouseX, mouseY, partialTicks); + search.extractRenderState(graphics, mouseX, mouseY, partialTicks); } private boolean isMouseInRange(double mouseX, double mouseY, int x1, int y1, int x2, int y2) { diff --git a/src/main/java/ml/northwestwind/forgeautofish/config/gui/RecastDelayScreen.java b/src/main/java/ml/northwestwind/forgeautofish/config/gui/RecastDelayScreen.java index 0cc62b4..f0892c1 100644 --- a/src/main/java/ml/northwestwind/forgeautofish/config/gui/RecastDelayScreen.java +++ b/src/main/java/ml/northwestwind/forgeautofish/config/gui/RecastDelayScreen.java @@ -4,7 +4,7 @@ import ml.northwestwind.forgeautofish.config.Config; import ml.northwestwind.forgeautofish.handler.AutoFishHandler; import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.components.EditBox; import net.minecraft.client.gui.screens.Screen; @@ -63,10 +63,10 @@ public static boolean isNumeric(String strNum) { } @Override - public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) { - super.render(graphics, mouseX, mouseY, partialTicks); - graphics.drawCenteredString(this.font, this.title, this.width / 2, 20, -1); - this.recastDelay.render(graphics, mouseX, mouseY, partialTicks); + public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float partialTicks) { + super.extractRenderState(graphics, mouseX, mouseY, partialTicks); + graphics.centeredText(this.font, this.title, this.width / 2, 20, -1); + this.recastDelay.extractRenderState(graphics, mouseX, mouseY, partialTicks); } @Override diff --git a/src/main/java/ml/northwestwind/forgeautofish/config/gui/ReelInDelayScreen.java b/src/main/java/ml/northwestwind/forgeautofish/config/gui/ReelInDelayScreen.java index a67de9c..e192197 100644 --- a/src/main/java/ml/northwestwind/forgeautofish/config/gui/ReelInDelayScreen.java +++ b/src/main/java/ml/northwestwind/forgeautofish/config/gui/ReelInDelayScreen.java @@ -4,7 +4,7 @@ import ml.northwestwind.forgeautofish.config.Config; import ml.northwestwind.forgeautofish.handler.AutoFishHandler; import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.components.EditBox; import net.minecraft.client.gui.screens.Screen; @@ -63,10 +63,10 @@ public static boolean isNumeric(String strNum) { } @Override - public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) { - super.render(graphics, mouseX, mouseY, partialTicks); - graphics.drawCenteredString(this.font, this.title, this.width / 2, 20, -1); - this.reelInDelay.render(graphics, mouseX, mouseY, partialTicks); + public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float partialTicks) { + super.extractRenderState(graphics, mouseX, mouseY, partialTicks); + graphics.centeredText(this.font, this.title, this.width / 2, 20, -1); + this.reelInDelay.extractRenderState(graphics, mouseX, mouseY, partialTicks); } @Override diff --git a/src/main/java/ml/northwestwind/forgeautofish/config/gui/SettingsScreen.java b/src/main/java/ml/northwestwind/forgeautofish/config/gui/SettingsScreen.java index 21aeb72..8555ab0 100644 --- a/src/main/java/ml/northwestwind/forgeautofish/config/gui/SettingsScreen.java +++ b/src/main/java/ml/northwestwind/forgeautofish/config/gui/SettingsScreen.java @@ -2,9 +2,8 @@ import ml.northwestwind.forgeautofish.AutoFish; import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.components.Button; -import net.minecraft.client.gui.components.StringWidget; import net.minecraft.client.gui.screens.Screen; public class SettingsScreen extends Screen { @@ -39,8 +38,8 @@ protected void init() { } @Override - public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) { - super.render(graphics, mouseX, mouseY, partialTicks); - graphics.drawCenteredString(this.font, this.title, this.width / 2, 20, -1); + public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float partialTicks) { + super.extractRenderState(graphics, mouseX, mouseY, partialTicks); + graphics.centeredText(this.font, this.title, this.width / 2, 20, -1); } } diff --git a/src/main/java/ml/northwestwind/forgeautofish/config/gui/SuperFilterScreen.java b/src/main/java/ml/northwestwind/forgeautofish/config/gui/SuperFilterScreen.java index 8954757..0a88380 100644 --- a/src/main/java/ml/northwestwind/forgeautofish/config/gui/SuperFilterScreen.java +++ b/src/main/java/ml/northwestwind/forgeautofish/config/gui/SuperFilterScreen.java @@ -3,7 +3,7 @@ import ml.northwestwind.forgeautofish.AutoFish; import ml.northwestwind.forgeautofish.config.Config; import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.components.EditBox; import net.minecraft.client.gui.screens.Screen; @@ -100,9 +100,9 @@ public boolean mouseClicked(MouseButtonEvent ev, boolean flag) { } @Override - public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) { - super.render(graphics, mouseX, mouseY, partialTicks); - graphics.drawCenteredString(this.font, this.title, this.width / 2, 20, -1); + public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float partialTicks) { + super.extractRenderState(graphics, mouseX, mouseY, partialTicks); + graphics.centeredText(this.font, this.title, this.width / 2, 20, -1); Item[] items = searching.toArray(new Item[0]); for (int i = page * max; i < Math.min((page + 1) * max, searching.size()); i++) { Item item = items[i]; @@ -110,11 +110,11 @@ public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTi int k = (i % max) % (max / 3); ItemStack stack = ItemStack.EMPTY; if (item != null) stack = new ItemStack(item); - if (!stack.isEmpty()) graphics.renderItem(stack, (reducedWidth * h / 3) + 15, (reducedHeight * k / (max / 3)) + 90); - graphics.drawString(this.font, stack.getDisplayName().getString(), ((reducedWidth * h / 3) + 45), ((reducedHeight * k / (max / 3)) + 95), Color.WHITE.getRGB()); + if (!stack.isEmpty()) graphics.item(stack, (reducedWidth * h / 3) + 15, (reducedHeight * k / (max / 3)) + 90); + graphics.text(this.font, stack.getDisplayName().getString(), ((reducedWidth * h / 3) + 45), ((reducedHeight * k / (max / 3)) + 95), Color.WHITE.getRGB()); //this.font.draw(graphics, stack.getDisplayName().getString(), (float) ((reducedWidth * h / 3) + 45), (float) ((reducedHeight * k / (max / 3)) + 95), Color.WHITE.getRGB()); } - search.render(graphics, mouseX, mouseY, partialTicks); + search.extractRenderState(graphics, mouseX, mouseY, partialTicks); } @Override diff --git a/src/main/java/ml/northwestwind/forgeautofish/config/gui/ThrowDelayScreen.java b/src/main/java/ml/northwestwind/forgeautofish/config/gui/ThrowDelayScreen.java index 2e6e056..9909094 100644 --- a/src/main/java/ml/northwestwind/forgeautofish/config/gui/ThrowDelayScreen.java +++ b/src/main/java/ml/northwestwind/forgeautofish/config/gui/ThrowDelayScreen.java @@ -4,7 +4,7 @@ import ml.northwestwind.forgeautofish.config.Config; import ml.northwestwind.forgeautofish.handler.AutoFishHandler; import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.components.EditBox; import net.minecraft.client.gui.screens.Screen; @@ -63,10 +63,10 @@ public static boolean isNumeric(String strNum) { } @Override - public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) { - super.render(graphics, mouseX, mouseY, partialTicks); - graphics.drawCenteredString(this.font, this.title, this.width / 2, 20, -1); - this.throwDelay.render(graphics, mouseX, mouseY, partialTicks); + public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float partialTicks) { + super.extractRenderState(graphics, mouseX, mouseY, partialTicks); + graphics.centeredText(this.font, this.title, this.width / 2, 20, -1); + this.throwDelay.extractRenderState(graphics, mouseX, mouseY, partialTicks); } @Override diff --git a/src/main/java/ml/northwestwind/forgeautofish/handler/AutoFishHandler.java b/src/main/java/ml/northwestwind/forgeautofish/handler/AutoFishHandler.java index 132f6ac..9299885 100644 --- a/src/main/java/ml/northwestwind/forgeautofish/handler/AutoFishHandler.java +++ b/src/main/java/ml/northwestwind/forgeautofish/handler/AutoFishHandler.java @@ -44,17 +44,17 @@ public static void onKeyInput(InputEvent.Key e) { LocalPlayer player = minecraft.player; if (KeyBinds.autofish.consumeClick()) { Config.setAutoFish(!autofish); - if (player != null) player.displayClientMessage(getText("forgeautofish", autofish), true); + if (player != null) player.sendOverlayMessage(getText("forgeautofish", autofish)); } else if (KeyBinds.rodprotect.consumeClick()) { Config.setRodProtect(!rodprotect); - if (player != null) player.displayClientMessage(getText("rodprotect", rodprotect), true); + if (player != null) player.sendOverlayMessage(getText("rodprotect", rodprotect)); } else if (KeyBinds.autoreplace.consumeClick()) { Config.setAutoReplace(!autoreplace); - if (player != null) player.displayClientMessage(getText("autoreplace", autoreplace), true); + if (player != null) player.sendOverlayMessage(getText("autoreplace", autoreplace)); } else if (KeyBinds.itemfilter.consumeClick()) { Config.enableFilter(!itemfilter); if (player != null) - player.displayClientMessage(getText("itemfilter", itemfilter), true); + player.sendOverlayMessage(getText("itemfilter", itemfilter)); } else if (KeyBinds.settings.consumeClick()) minecraft.setScreen(new SettingsScreen()); } @@ -142,7 +142,7 @@ else if (fishingRod.getMaxDamage() - fishingRod.getDamageValue() < 3 && !player. if (autoreplace) needReplace = true; else { autofish = false; - player.displayClientMessage(getText("forgeautofish", autofish), true); + player.sendOverlayMessage(getText("forgeautofish", autofish)); return; } if (needReplace) { diff --git a/src/main/resources/META-INF/mods.toml b/src/main/resources/META-INF/mods.toml index cb7f4d9..d6d9df9 100644 --- a/src/main/resources/META-INF/mods.toml +++ b/src/main/resources/META-INF/mods.toml @@ -23,13 +23,13 @@ Note that this is my first mod, so there might be bugs. [[dependencies.forgeautofish]] modId="forge" mandatory=true - versionRange="[51,)" + versionRange="[64,)" ordering="NONE" side="BOTH" [[dependencies.forgeautofish]] modId="minecraft" mandatory=true - versionRange="[1.21,1.22)" + versionRange="[26.1.2,26.2)" ordering="NONE" side="BOTH" From 1686c5accf1399af129d93940adc62c9a05e35df Mon Sep 17 00:00:00 2001 From: North-West-Wind Date: Sat, 27 Jun 2026 20:30:03 +0800 Subject: [PATCH 05/52] refactor: update loaderVersion --- src/main/resources/META-INF/mods.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/META-INF/mods.toml b/src/main/resources/META-INF/mods.toml index d6d9df9..02120eb 100644 --- a/src/main/resources/META-INF/mods.toml +++ b/src/main/resources/META-INF/mods.toml @@ -1,5 +1,5 @@ modLoader="javafml" -loaderVersion="[51,)" +loaderVersion="[64,)" issueTrackerURL="https://github.com/North-West-Wind/forge-autofish/issues" license="GPL v3" From 06d5c8951d0eef8d7adc3d838ac49561ddabb2d2 Mon Sep 17 00:00:00 2001 From: North-West-Wind Date: Sun, 28 Jun 2026 22:12:35 +0800 Subject: [PATCH 06/52] refactor: 26.2 compat --- gradle.properties | 6 +++--- .../forgeautofish/config/gui/CheckIntervalScreen.java | 4 ++-- .../config/gui/FilterSelectionScreen.java | 6 +++--- .../forgeautofish/config/gui/RecastDelayScreen.java | 4 ++-- .../forgeautofish/config/gui/ReelInDelayScreen.java | 4 ++-- .../forgeautofish/config/gui/SettingsScreen.java | 10 +++++----- .../forgeautofish/config/gui/SuperFilterScreen.java | 6 +++--- .../forgeautofish/config/gui/ThrowDelayScreen.java | 4 ++-- .../forgeautofish/handler/AutoFishHandler.java | 2 +- src/main/resources/META-INF/mods.toml | 6 +++--- 10 files changed, 26 insertions(+), 26 deletions(-) diff --git a/gradle.properties b/gradle.properties index 29874b8..244a1e4 100644 --- a/gradle.properties +++ b/gradle.properties @@ -11,8 +11,8 @@ net.minecraftforge.gradle.merge-source-sets=true org.gradle.jvmargs=-Xmx3G org.gradle.java.home=/usr/lib/jvm/java-21-graalvm-ee -mc_version=26.1.2 -forge_version=64.0.10 -build_mc_version=26.1.2-forge +mc_version=26.2 +forge_version=65.0.1 +build_mc_version=26.2-forge mod_id=forgeautofish mod_version=7.1.0 \ No newline at end of file diff --git a/src/main/java/ml/northwestwind/forgeautofish/config/gui/CheckIntervalScreen.java b/src/main/java/ml/northwestwind/forgeautofish/config/gui/CheckIntervalScreen.java index ef35f7c..f25e77f 100644 --- a/src/main/java/ml/northwestwind/forgeautofish/config/gui/CheckIntervalScreen.java +++ b/src/main/java/ml/northwestwind/forgeautofish/config/gui/CheckIntervalScreen.java @@ -41,7 +41,7 @@ public boolean mouseClicked(MouseButtonEvent ev, boolean p_430750_) { if (delay < Config.CHECK_INTERVAL_RANGE[1] || delay > Config.CHECK_INTERVAL_RANGE[2]) checkInterval.setValue(Long.toString(AutoFishHandler.checkInterval)); else { Config.setCheckInterval(delay); - Minecraft.getInstance().setScreen(parent); + Minecraft.getInstance().setScreenAndShow(parent); } } }).pos(this.width / 2 - 75, this.height / 2).size(150, 20).build(); @@ -76,7 +76,7 @@ public boolean shouldCloseOnEsc() { @Override public boolean keyPressed(KeyEvent ev) { - if (ev.key() == GLFW.GLFW_KEY_ESCAPE) Minecraft.getInstance().setScreen(parent); + if (ev.key() == GLFW.GLFW_KEY_ESCAPE) Minecraft.getInstance().setScreenAndShow(parent); return super.keyPressed(ev); } diff --git a/src/main/java/ml/northwestwind/forgeautofish/config/gui/FilterSelectionScreen.java b/src/main/java/ml/northwestwind/forgeautofish/config/gui/FilterSelectionScreen.java index c01dfad..b7d8cee 100644 --- a/src/main/java/ml/northwestwind/forgeautofish/config/gui/FilterSelectionScreen.java +++ b/src/main/java/ml/northwestwind/forgeautofish/config/gui/FilterSelectionScreen.java @@ -86,10 +86,10 @@ public boolean mouseClicked(MouseButtonEvent ev, boolean p_430750_) { Button add = new Button.Builder(AutoFish.getTranslatableComponent("gui.filterselection.save"), button -> { List items = selected.stream().map(item -> Objects.requireNonNullElse(ForgeRegistries.ITEMS.getKey(item), item).toString()).collect(Collectors.toList()); Config.setFILTER(items); - Minecraft.getInstance().setScreen(parent); + Minecraft.getInstance().setScreenAndShow(parent); }).pos(this.width / 2 - 75, 60).size(72, 20).build(); addRenderableWidget(add); - Button done = new Button.Builder(AutoFish.getTranslatableComponent("gui.filterselection.cancel"), button -> Minecraft.getInstance().setScreen(parent)).pos(this.width / 2 + 3, 60).size(72, 20).build(); + Button done = new Button.Builder(AutoFish.getTranslatableComponent("gui.filterselection.cancel"), button -> Minecraft.getInstance().setScreenAndShow(parent)).pos(this.width / 2 + 3, 60).size(72, 20).build(); addRenderableWidget(done); previous = new Button.Builder(AutoFish.getLiteralComponent("<"), button -> { if (page > 0) page--; }).pos(this.width / 2 - 100, 60).size(20, 20).build(); previous.visible = false; @@ -152,7 +152,7 @@ private int getYPos(int k, int height) { @Override public boolean keyPressed(KeyEvent ev) { if (ev.key() == GLFW.GLFW_KEY_ESCAPE) { - if (!search.isFocused()) Minecraft.getInstance().setScreen(parent); + if (!search.isFocused()) Minecraft.getInstance().setScreenAndShow(parent); else search.setFocused(false); } return super.keyPressed(ev); diff --git a/src/main/java/ml/northwestwind/forgeautofish/config/gui/RecastDelayScreen.java b/src/main/java/ml/northwestwind/forgeautofish/config/gui/RecastDelayScreen.java index f0892c1..43a7ccf 100644 --- a/src/main/java/ml/northwestwind/forgeautofish/config/gui/RecastDelayScreen.java +++ b/src/main/java/ml/northwestwind/forgeautofish/config/gui/RecastDelayScreen.java @@ -41,7 +41,7 @@ public boolean mouseClicked(MouseButtonEvent ev, boolean flag) { if (delay < Config.RECAST_DELAY_RANGE[1] || delay > Config.RECAST_DELAY_RANGE[2]) recastDelay.setValue(Long.toString(AutoFishHandler.recastDelay)); else { Config.setRecastDelay(delay); - Minecraft.getInstance().setScreen(parent); + Minecraft.getInstance().setScreenAndShow(parent); } } }).pos(this.width / 2 - 75, this.height / 2).size(150, 20).build(); @@ -76,7 +76,7 @@ public boolean shouldCloseOnEsc() { @Override public boolean keyPressed(KeyEvent ev) { - if (ev.key() == GLFW.GLFW_KEY_ESCAPE) Minecraft.getInstance().setScreen(parent); + if (ev.key() == GLFW.GLFW_KEY_ESCAPE) Minecraft.getInstance().setScreenAndShow(parent); return super.keyPressed(ev); } diff --git a/src/main/java/ml/northwestwind/forgeautofish/config/gui/ReelInDelayScreen.java b/src/main/java/ml/northwestwind/forgeautofish/config/gui/ReelInDelayScreen.java index e192197..7aca35f 100644 --- a/src/main/java/ml/northwestwind/forgeautofish/config/gui/ReelInDelayScreen.java +++ b/src/main/java/ml/northwestwind/forgeautofish/config/gui/ReelInDelayScreen.java @@ -41,7 +41,7 @@ public boolean mouseClicked(MouseButtonEvent ev, boolean flag) { if (delay < Config.REEL_IN_DELAY_RANGE[1] || delay > Config.REEL_IN_DELAY_RANGE[2]) reelInDelay.setValue(Long.toString(AutoFishHandler.reelInDelay)); else { Config.setReelInDelay(delay); - Minecraft.getInstance().setScreen(parent); + Minecraft.getInstance().setScreenAndShow(parent); } } }).pos(this.width / 2 - 75, this.height / 2).size(150, 20).build(); @@ -76,7 +76,7 @@ public boolean shouldCloseOnEsc() { @Override public boolean keyPressed(KeyEvent ev) { - if (ev.key() == GLFW.GLFW_KEY_ESCAPE) Minecraft.getInstance().setScreen(parent); + if (ev.key() == GLFW.GLFW_KEY_ESCAPE) Minecraft.getInstance().setScreenAndShow(parent); return super.keyPressed(ev); } diff --git a/src/main/java/ml/northwestwind/forgeautofish/config/gui/SettingsScreen.java b/src/main/java/ml/northwestwind/forgeautofish/config/gui/SettingsScreen.java index 8555ab0..a21ed94 100644 --- a/src/main/java/ml/northwestwind/forgeautofish/config/gui/SettingsScreen.java +++ b/src/main/java/ml/northwestwind/forgeautofish/config/gui/SettingsScreen.java @@ -21,11 +21,11 @@ public boolean isPauseScreen() { @Override protected void init() { Button.Builder[] builders = { - new Button.Builder(AutoFish.getTranslatableComponent("gui.forgeautofish.recastdelay"), button -> Minecraft.getInstance().setScreen(new RecastDelayScreen(this))), - new Button.Builder(AutoFish.getTranslatableComponent("gui.forgeautofish.reelindelay"), button -> Minecraft.getInstance().setScreen(new ReelInDelayScreen(this))), - new Button.Builder(AutoFish.getTranslatableComponent("gui.forgeautofish.throwdelay"), button -> Minecraft.getInstance().setScreen(new ThrowDelayScreen(this))), - new Button.Builder(AutoFish.getTranslatableComponent("gui.forgeautofish.checkinterval"), button -> Minecraft.getInstance().setScreen(new CheckIntervalScreen(this))), - new Button.Builder(AutoFish.getTranslatableComponent("gui.forgeautofish.filter"), button -> Minecraft.getInstance().setScreen(new SuperFilterScreen(this))) + new Button.Builder(AutoFish.getTranslatableComponent("gui.forgeautofish.recastdelay"), button -> Minecraft.getInstance().setScreenAndShow(new RecastDelayScreen(this))), + new Button.Builder(AutoFish.getTranslatableComponent("gui.forgeautofish.reelindelay"), button -> Minecraft.getInstance().setScreenAndShow(new ReelInDelayScreen(this))), + new Button.Builder(AutoFish.getTranslatableComponent("gui.forgeautofish.throwdelay"), button -> Minecraft.getInstance().setScreenAndShow(new ThrowDelayScreen(this))), + new Button.Builder(AutoFish.getTranslatableComponent("gui.forgeautofish.checkinterval"), button -> Minecraft.getInstance().setScreenAndShow(new CheckIntervalScreen(this))), + new Button.Builder(AutoFish.getTranslatableComponent("gui.forgeautofish.filter"), button -> Minecraft.getInstance().setScreenAndShow(new SuperFilterScreen(this))) }; for (int ii = 0; ii < builders.length; ii++) { diff --git a/src/main/java/ml/northwestwind/forgeautofish/config/gui/SuperFilterScreen.java b/src/main/java/ml/northwestwind/forgeautofish/config/gui/SuperFilterScreen.java index 0a88380..4beeb64 100644 --- a/src/main/java/ml/northwestwind/forgeautofish/config/gui/SuperFilterScreen.java +++ b/src/main/java/ml/northwestwind/forgeautofish/config/gui/SuperFilterScreen.java @@ -87,9 +87,9 @@ public boolean mouseClicked(MouseButtonEvent ev, boolean flag) { if (page > maxPage - 1) page = maxPage - 1; }); addRenderableWidget(search); - Button add = new Button.Builder(AutoFish.getTranslatableComponent("gui.superfilterscreen.openfilter"), button -> Minecraft.getInstance().setScreen(new FilterSelectionScreen(this))).pos(this.width / 2 - 75, 60).size(72, 20).build(); + Button add = new Button.Builder(AutoFish.getTranslatableComponent("gui.superfilterscreen.openfilter"), button -> Minecraft.getInstance().setScreenAndShow(new FilterSelectionScreen(this))).pos(this.width / 2 - 75, 60).size(72, 20).build(); addRenderableWidget(add); - Button done = new Button.Builder(AutoFish.getTranslatableComponent("gui.superfilterscreen.done"), button -> Minecraft.getInstance().setScreen(parent)).pos(this.width / 2 + 3, 60).size(72, 20).build(); + Button done = new Button.Builder(AutoFish.getTranslatableComponent("gui.superfilterscreen.done"), button -> Minecraft.getInstance().setScreenAndShow(parent)).pos(this.width / 2 + 3, 60).size(72, 20).build(); addRenderableWidget(done); previous = new Button.Builder(AutoFish.getLiteralComponent("<"), button -> { if (page > 0) page--; }).pos(this.width / 2 - 100, 60).size(20, 20).build(); previous.visible = false; @@ -124,7 +124,7 @@ public boolean shouldCloseOnEsc() { @Override public boolean keyPressed(KeyEvent ev) { - if (ev.key() == GLFW.GLFW_KEY_ESCAPE) Minecraft.getInstance().setScreen(parent); + if (ev.key() == GLFW.GLFW_KEY_ESCAPE) Minecraft.getInstance().setScreenAndShow(parent); return super.keyPressed(ev); } diff --git a/src/main/java/ml/northwestwind/forgeautofish/config/gui/ThrowDelayScreen.java b/src/main/java/ml/northwestwind/forgeautofish/config/gui/ThrowDelayScreen.java index 9909094..913c432 100644 --- a/src/main/java/ml/northwestwind/forgeautofish/config/gui/ThrowDelayScreen.java +++ b/src/main/java/ml/northwestwind/forgeautofish/config/gui/ThrowDelayScreen.java @@ -41,7 +41,7 @@ public boolean mouseClicked(MouseButtonEvent ev, boolean flag) { if (delay < Config.THROW_DELAY_RANGE[1] || delay > Config.THROW_DELAY_RANGE[2]) throwDelay.setValue(Long.toString(AutoFishHandler.throwDelay)); else { Config.setThrowDelay(delay); - Minecraft.getInstance().setScreen(parent); + Minecraft.getInstance().setScreenAndShow(parent); } } }).pos(this.width / 2 - 75, this.height / 2).size(150, 20).build(); @@ -76,7 +76,7 @@ public boolean shouldCloseOnEsc() { @Override public boolean keyPressed(KeyEvent ev) { - if (ev.key() == GLFW.GLFW_KEY_ESCAPE) Minecraft.getInstance().setScreen(parent); + if (ev.key() == GLFW.GLFW_KEY_ESCAPE) Minecraft.getInstance().setScreenAndShow(parent); return super.keyPressed(ev); } diff --git a/src/main/java/ml/northwestwind/forgeautofish/handler/AutoFishHandler.java b/src/main/java/ml/northwestwind/forgeautofish/handler/AutoFishHandler.java index 9299885..c859bdd 100644 --- a/src/main/java/ml/northwestwind/forgeautofish/handler/AutoFishHandler.java +++ b/src/main/java/ml/northwestwind/forgeautofish/handler/AutoFishHandler.java @@ -56,7 +56,7 @@ public static void onKeyInput(InputEvent.Key e) { if (player != null) player.sendOverlayMessage(getText("itemfilter", itemfilter)); } else if (KeyBinds.settings.consumeClick()) - minecraft.setScreen(new SettingsScreen()); + minecraft.setScreenAndShow(new SettingsScreen()); } @SubscribeEvent diff --git a/src/main/resources/META-INF/mods.toml b/src/main/resources/META-INF/mods.toml index 02120eb..afa2774 100644 --- a/src/main/resources/META-INF/mods.toml +++ b/src/main/resources/META-INF/mods.toml @@ -1,5 +1,5 @@ modLoader="javafml" -loaderVersion="[64,)" +loaderVersion="[65,)" issueTrackerURL="https://github.com/North-West-Wind/forge-autofish/issues" license="GPL v3" @@ -23,13 +23,13 @@ Note that this is my first mod, so there might be bugs. [[dependencies.forgeautofish]] modId="forge" mandatory=true - versionRange="[64,)" + versionRange="[65,)" ordering="NONE" side="BOTH" [[dependencies.forgeautofish]] modId="minecraft" mandatory=true - versionRange="[26.1.2,26.2)" + versionRange="[26.2,26.3)" ordering="NONE" side="BOTH" From 450c9618d1379d87b1bee90dcee17127023dda9c Mon Sep 17 00:00:00 2001 From: North-West-Wind Date: Mon, 29 Jun 2026 11:34:28 +0800 Subject: [PATCH 07/52] refactor: multiloader --- .gitattributes | 21 +- .gitignore | 10 +- LICENSE | 795 +++--------------- README.md | 80 +- build-logic/build.gradle | 3 + .../src/main/groovy/multiloader-common.gradle | 85 ++ .../src/main/groovy/multiloader-loader.gradle | 45 + build.gradle | 61 +- common/build.gradle | 59 ++ .../java/in/northwestw/autofish/AutoFish.java | 26 + .../in/northwestw/autofish/config/Config.java | 169 ++++ .../config/gui/CheckIntervalScreen.java | 14 +- .../config/gui/FilterSelectionScreen.java | 62 +- .../config/gui/RecastDelayScreen.java | 14 +- .../config/gui/ReelInDelayScreen.java | 14 +- .../autofish}/config/gui/SettingsScreen.java | 18 +- .../config/gui/SuperFilterScreen.java | 51 +- .../config/gui/ThrowDelayScreen.java | 14 +- .../autofish}/handler/AutoFishHandler.java | 111 ++- .../autofish}/keybind/KeyBinds.java | 15 +- .../assets/forgeautofish/lang/en_us.json | 100 +-- .../assets/forgeautofish/lang/zh_tw.json | 26 +- .../src/main/resources/autofish.png | Bin .../src}/main/resources/pack.mcmeta | 0 fabric/build.gradle | 49 ++ .../northwestw/autofish/AutoFishFabric.java | 24 + fabric/src/main/resources/fabric.mod.json | 32 + forge/build.gradle | 81 ++ .../in/northwestw/autofish/AutoFishForge.java | 40 + forge/src/main/resources/META-INF/mods.toml | 27 + gradle.properties | 44 +- gradle/wrapper/gradle-wrapper.jar | Bin 43583 -> 48462 bytes gradle/wrapper/gradle-wrapper.properties | 5 +- gradlew | 58 +- gradlew.bat | 171 ++-- neoforge/build.gradle | 61 ++ .../northwestw/autofish/AutoFishNeoForge.java | 44 + .../resources/META-INF/neoforge.mods.toml | 32 + settings.gradle | 48 +- .../northwestwind/forgeautofish/AutoFish.java | 39 - .../forgeautofish/config/Config.java | 112 --- src/main/resources/META-INF/mods.toml | 35 - 42 files changed, 1368 insertions(+), 1327 deletions(-) create mode 100644 build-logic/build.gradle create mode 100644 build-logic/src/main/groovy/multiloader-common.gradle create mode 100644 build-logic/src/main/groovy/multiloader-loader.gradle create mode 100644 common/build.gradle create mode 100644 common/src/main/java/in/northwestw/autofish/AutoFish.java create mode 100644 common/src/main/java/in/northwestw/autofish/config/Config.java rename {src/main/java/ml/northwestwind/forgeautofish => common/src/main/java/in/northwestw/autofish}/config/gui/CheckIntervalScreen.java (88%) rename {src/main/java/ml/northwestwind/forgeautofish => common/src/main/java/in/northwestw/autofish}/config/gui/FilterSelectionScreen.java (74%) rename {src/main/java/ml/northwestwind/forgeautofish => common/src/main/java/in/northwestw/autofish}/config/gui/RecastDelayScreen.java (88%) rename {src/main/java/ml/northwestwind/forgeautofish => common/src/main/java/in/northwestw/autofish}/config/gui/ReelInDelayScreen.java (88%) rename {src/main/java/ml/northwestwind/forgeautofish => common/src/main/java/in/northwestw/autofish}/config/gui/SettingsScreen.java (62%) rename {src/main/java/ml/northwestwind/forgeautofish => common/src/main/java/in/northwestw/autofish}/config/gui/SuperFilterScreen.java (73%) rename {src/main/java/ml/northwestwind/forgeautofish => common/src/main/java/in/northwestw/autofish}/config/gui/ThrowDelayScreen.java (88%) rename {src/main/java/ml/northwestwind/forgeautofish => common/src/main/java/in/northwestw/autofish}/handler/AutoFishHandler.java (67%) rename {src/main/java/ml/northwestwind/forgeautofish => common/src/main/java/in/northwestw/autofish}/keybind/KeyBinds.java (68%) rename {src => common/src}/main/resources/assets/forgeautofish/lang/en_us.json (60%) rename {src => common/src}/main/resources/assets/forgeautofish/lang/zh_tw.json (58%) rename src/main/resources/forgeautofish.png => common/src/main/resources/autofish.png (100%) rename {src => common/src}/main/resources/pack.mcmeta (100%) create mode 100644 fabric/build.gradle create mode 100644 fabric/src/main/java/in/northwestw/autofish/AutoFishFabric.java create mode 100644 fabric/src/main/resources/fabric.mod.json create mode 100644 forge/build.gradle create mode 100644 forge/src/main/java/in/northwestw/autofish/AutoFishForge.java create mode 100644 forge/src/main/resources/META-INF/mods.toml create mode 100644 neoforge/build.gradle create mode 100644 neoforge/src/main/java/in/northwestw/autofish/AutoFishNeoForge.java create mode 100644 neoforge/src/main/resources/META-INF/neoforge.mods.toml delete mode 100644 src/main/java/ml/northwestwind/forgeautofish/AutoFish.java delete mode 100644 src/main/java/ml/northwestwind/forgeautofish/config/Config.java delete mode 100644 src/main/resources/META-INF/mods.toml diff --git a/.gitattributes b/.gitattributes index f811f6a..4c3684e 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,5 +1,16 @@ -# Disable autocrlf on generated files, they always generate with LF -# Add any extra files or paths here to make git stop saying they -# are changed when only line endings change. -src/generated/**/.cache/cache text eol=lf -src/generated/**/*.json text eol=lf +* text eol=lf +*.bat text eol=crlf +*.patch text eol=lf +*.java text eol=lf +*.gradle text eol=crlf +*.png binary +*.gif binary +*.exe binary +*.dll binary +*.jar binary +*.lzma binary +*.zip binary +*.pyd binary +*.cfg text eol=lf +*.jks binary +*.ogg binary \ No newline at end of file diff --git a/.gitignore b/.gitignore index 856610b..461017f 100644 --- a/.gitignore +++ b/.gitignore @@ -11,7 +11,8 @@ out *.ipr *.iws *.iml -.idea +.idea/* +!.idea/scopes # gradle build @@ -19,8 +20,5 @@ build # other eclipse -run* -.vscode - -# Files from Forge MDK -forge*changelog.txt +run +runs diff --git a/LICENSE b/LICENSE index f288702..0e259d4 100644 --- a/LICENSE +++ b/LICENSE @@ -1,674 +1,121 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. diff --git a/README.md b/README.md index c2b5ad6..c88b432 100644 --- a/README.md +++ b/README.md @@ -1,48 +1,32 @@ -# AutoFish for Forge -Finally! An AFK fishing mod for Forge users! - -## Download -1. Go to the CurseForge page [here](https://www.curseforge.com/minecraft/mc-mods/autofish-for-forge) -2. Find the version of the mod you want -3. Find the Minecraft version of the mod you want -4. Download -5. Enjoy! - -Thanks for using the mod! - -### Currently supported versions -1.21.x -(Multiple versions were too much for me. I'm sorry.) - -### Unsupported, but we have their files -1.20.x -1.19.x -1.18.x -1.17.x -1.16.x -1.15.x -1.14.4 -1.13.2 -1.12.2 -1.11.2 -1.10.2 -1.9.4 -1.8.9 - -## What does it do? -This mod allows you to AFK fish (as long as the server allows AFK) anywhere. Can I use it in my singleplayer world? Yes! Can I use it on servers? Yes! The mod is completely client-side! You just need a Forge client on your computer, put this mod into the "mods" folder and you finished the setup! How easy it is! - -Note: Putting the mod into the "mods" folder of a server will NOT do anything. -There is also NO Fabric version of this mod, as there are other fishing mods for Fabric already. - -## Why did I make this? -To answer that, we need to talk about ~~parallel universe~~ the 1.16 update of Minecraft. If you read the changelogs, there is 1 particular part that nerfed the entire AFK fishing farm. Basically, it still allows players to fish with it, but you will not get any treasure (e.g. Enchanted Books, Saddles, etc.). On the other hand, you can still get fish from it. Since the farm is nerfed, players started to create other designs of the fishing farm. However, those are not as good as the old ones. That's what causes me to make this mod, which I think quite a lot of players needed it. - -After I made the very first version of the mod, why not make it for more versions of Minecraft? And that's the reason I made the mod for more versions. I know some other Forge fishing mods exist in older versions, but they are not accurate. - -## How does it work? -Programmers have probably looked at the source code already, but allow me to explain that for non-coders. - -When the mod is enabled, it will look for the bobber of the player. You may think that I look for the state of the bobber but no. The state of the bobber is not public, and there is no public methods that returns the state, so it is impossible to listen for change of state. - -However, I found a way simplier method to know if is catch a fish...\*drumroll\* Motion. Since, the bobber is an entity, we can track its motion. As we all know, the bobber sinks into the water when it catches a fish. By tracking the vertical motion of the bobber, we can know when it catches a fish. It is simple as that! +# MultiLoader Template + +This project provides a Gradle project template that can compile Minecraft mods for multiple modloaders using a common project for the sources. This project does not require any third party libraries or dependencies. If you have any questions or want to discuss the project, please join our [Discord](https://discord.myceliummod.network). + +## Getting Started + +### IntelliJ IDEA +This guide will show how to import the MultiLoader Template into IntelliJ IDEA. The setup process is roughly equivalent to setting up the modloaders independently and should be very familiar to anyone who has worked with their MDKs. + +1. Clone or download this repository to your computer. +2. Configure the project by setting the properties in the `gradle.properties` file. You will also need to change the `rootProject.name` property in `settings.gradle`, this should match the folder name of your project, or else IDEA may complain. +3. Open the template's root folder as a new project in IDEA. This is the folder that contains this README.md file and the gradlew executable. +4. If your default JVM/JDK is not Java 25 you will encounter an error when opening the project. This error is fixed by going to `File > Settings > Build, Execution, Deployment > Build Tools > Gradle > Gradle JVM` and changing the value to a valid Java 25 JVM. You will also need to set the Project SDK to Java 25. This can be done by going to `File > Project Structure > Project SDK`. Once both have been set open the Gradle tab in IDEA and click the refresh button to reload the project. +5. Open your Run/Debug Configurations. Under the `Application` category there should now be options to run Fabric and NeoForge projects. Select one of the client options and try to run it. +6. Assuming you were able to run the game in step 5 your workspace should now be set up. + +### Eclipse +While it is possible to use this template in Eclipse it is not recommended. During the development of this template multiple critical bugs and quirks related to Eclipse were found at nearly every level of the required build tools. While we continue to work with these tools to report and resolve issues support for projects like these are not there yet. For now Eclipse is considered unsupported by this project. The development cycle for build tools is notoriously slow so there are no ETAs available. + +## Development Guide +When using this template the majority of your mod should be developed in the `common` project. The `common` project is compiled against the vanilla game and is used to hold code that is shared between the different loader-specific versions of your mod. The `common` project has no knowledge or access to ModLoader specific code, apis, or concepts. Code that requires something from a specific loader must be done through the project that is specific to that loader, such as the `fabric` or `neoforge` projects. + +Loader specific projects such as the `fabric` and `neoforge` project are used to load the `common` project into the game. These projects also define code that is specific to that loader. Loader specific projects can access all the code in the `common` project. It is important to remember that the `common` project can not access code from loader specific projects. + +## Removing Platforms and Loaders +While this template has support for many modloaders, new loaders may appear in the future, and existing loaders may become less relevant. + +Removing loader specific projects is as easy as deleting the folder, and removing the `include("projectname")` line from the `settings.gradle` file. +For example if you wanted to remove support for `forge` you would follow the following steps: + +1. Delete the subproject folder. For example, delete `MultiLoader-Template/forge`. +2. Remove the project from `settings.gradle`. For example, remove `include("forge")`. diff --git a/build-logic/build.gradle b/build-logic/build.gradle new file mode 100644 index 0000000..6784052 --- /dev/null +++ b/build-logic/build.gradle @@ -0,0 +1,3 @@ +plugins { + id 'groovy-gradle-plugin' +} diff --git a/build-logic/src/main/groovy/multiloader-common.gradle b/build-logic/src/main/groovy/multiloader-common.gradle new file mode 100644 index 0000000..f9a55cf --- /dev/null +++ b/build-logic/src/main/groovy/multiloader-common.gradle @@ -0,0 +1,85 @@ +plugins { + id 'java-library' + id 'maven-publish' +} + +base { + archivesName = "${mod_id}-${project.name}-${minecraft_version}" +} + +java { + toolchain.languageVersion = JavaLanguageVersion.of(java_version) + withSourcesJar() + withJavadocJar() +} + +tasks.withType(Jar).configureEach { + from(rootProject.file('LICENSE')) { + rename { "${it}_${mod_name}" } + } +} + +jar { + + manifest { + attributes([ + 'Specification-Title' : mod_name, + 'Specification-Vendor' : mod_author, + 'Specification-Version' : project.jar.archiveVersion, + 'Implementation-Title' : project.name, + 'Implementation-Version': project.jar.archiveVersion, + 'Implementation-Vendor' : mod_author, + 'Built-On-Minecraft' : minecraft_version + ]) + } +} + +processResources { + var expandProps = [ + 'version' : version, + 'group' : project.group, //Else we target the task's group. + 'minecraft_version' : minecraft_version, + 'minecraft_version_range' : minecraft_version_range, + 'fabric_version' : fabric_version, + 'fabric_loader_version' : fabric_loader_version, + 'mod_name' : mod_name, + 'mod_author' : mod_author, + 'mod_id' : mod_id, + 'license' : license, + 'description' : project.description, + 'forge_version' : forge_version, + 'forge_loader_version_range' : forge_loader_version_range, + 'neoforge_version' : neoforge_version, + 'neoforge_loader_version_range': neoforge_loader_version_range, + 'credits' : credits, + 'java_version' : java_version + ] + + var jsonExpandProps = expandProps.collectEntries { + key, value -> [(key): value instanceof String ? value.replace('\n', '\\\\n') : value] + } + + filesMatching(['META-INF/mods.toml', 'META-INF/neoforge.mods.toml']) { + expand expandProps + } + + filesMatching(['pack.mcmeta', 'fabric.mod.json', '*.mixins.json']) { + expand jsonExpandProps + } + + inputs.properties(expandProps) +} + +publishing { + publications { + register('mavenJava', MavenPublication) { + artifactId base.archivesName.get() + from components.java + } + } + repositories { + maven { + url System.getenv('local_maven_url') + } + } +} diff --git a/build-logic/src/main/groovy/multiloader-loader.gradle b/build-logic/src/main/groovy/multiloader-loader.gradle new file mode 100644 index 0000000..1adfaee --- /dev/null +++ b/build-logic/src/main/groovy/multiloader-loader.gradle @@ -0,0 +1,45 @@ +plugins { + id 'multiloader-common' +} + +configurations { + commonJava { + canBeResolved = true + } + commonResources { + canBeResolved = true + } +} + +dependencies { + compileOnly(project(':common')) { + def loaderAttribute = Attribute.of('io.github.mcgradleconventions.loader', String) + attributes { + attribute(loaderAttribute, 'common') + } + } + commonJava(project(path: ':common', configuration: 'commonJava')) + commonResources(project(path: ':common', configuration: 'commonResources')) +} + +tasks.named('compileJava', JavaCompile) { + dependsOn(configurations.commonJava) + source(configurations.commonJava) +} + +processResources { + dependsOn(configurations.commonResources) + from(configurations.commonResources) +} + +tasks.named('javadoc', Javadoc).configure { + dependsOn(configurations.commonJava) + source(configurations.commonJava) +} + +tasks.named('sourcesJar', Jar) { + dependsOn(configurations.commonJava) + from(configurations.commonJava) + dependsOn(configurations.commonResources) + from(configurations.commonResources) +} diff --git a/build.gradle b/build.gradle index 7c3b48f..f23072f 100644 --- a/build.gradle +++ b/build.gradle @@ -1,57 +1,6 @@ -plugins { - id 'java' - id 'idea' - id 'eclipse' - id 'net.minecraftforge.gradle' version '[7.0.17,8)' -} - -version = mod_version -group = 'ml.northwestwind.forgeautofish' - -base { - archivesName = "$mod_id-forge-$mc_version" -} - -java.toolchain.languageVersion = JavaLanguageVersion.of(25) - -println('Java: ' + System.getProperty('java.version') + ' JVM: ' + System.getProperty('java.vm.version') + '(' + System.getProperty('java.vendor') + ') Arch: ' + System.getProperty('os.arch')) -minecraft { - runs { - configureEach { - workingDir = layout.projectDirectory.dir('run') - - systemProperty 'eventbus.api.strictRuntimeChecks', 'true' - systemProperty 'forge.enabledGameTestNamespaces', 'forgeautofish' - } - - register('client') - - register('server') { - args '--nogui' - } - - register('gameTestServer') - - data { - workingDir = layout.projectDirectory.dir('run-data') - - args '--mod', "forgeautofish", '--all', '--output', layout.projectDirectory.dir('src/generated/resources'), '--existing', layout.projectDirectory.dir('src/main/resources') - } - } -} - -repositories { - minecraft.mavenizer(it) // In Kotlin, it = this - maven fg.forgeMaven - maven fg.minecraftLibsMaven - mavenCentral() -} - -dependencies { - implementation minecraft.dependency("net.minecraftforge:forge:${mc_version}-${forge_version}") - annotationProcessor 'net.minecraftforge:eventbus-validator:7.0.1' -} - -tasks.withType(JavaCompile).configureEach { - options.encoding = 'UTF-8' // Use the UTF-8 charset for Java compilation +plugins { + // see https://maven.fabricmc.net/fabric-loom/fabric-loom.gradle.plugin/maven-metadata.xml for new versions + id 'net.fabricmc.fabric-loom' version '1.16.3' apply false + // see https://projects.neoforged.net/neoforged/moddevgradle for new versions + id 'net.neoforged.moddev' version '2.0.141' apply false } \ No newline at end of file diff --git a/common/build.gradle b/common/build.gradle new file mode 100644 index 0000000..e702da1 --- /dev/null +++ b/common/build.gradle @@ -0,0 +1,59 @@ +plugins { + id 'multiloader-common' + id 'net.neoforged.moddev' +} + +neoForge { + neoFormVersion = neo_form_version + // Automatically enable AccessTransformers if the file exists + def at = file('src/main/resources/META-INF/accesstransformer.cfg') + if (at.exists()) { + accessTransformers.from(at.absolutePath) + } +} + +dependencies { + // Fabric and NeoForge both bundle Fabric Mixin, so it is safe to use it in common + // If you need to update, check what version they are using to see what is compatible + // https://github.com/neoforged/NeoForge/blob/26.2.x/gradle.properties#L37 + // https://github.com/FabricMC/fabric-loader/blob/master/gradle.properties#L12 + compileOnly('net.fabricmc:sponge-mixin:0.17.3+mixin.0.8.7') + // Fabric and NeoForge both bundle MixinExtras, so it is safe to use it in common + compileOnly(annotationProcessor('io.github.llamalad7:mixinextras-common:0.5.3')) + +} + +configurations { + commonJava { + canBeResolved = false + canBeConsumed = true + } + commonResources { + canBeResolved = false + canBeConsumed = true + } +} + +artifacts { + commonJava sourceSets.main.java.sourceDirectories.singleFile + commonResources sourceSets.main.resources.sourceDirectories.singleFile +} + +// Implement mcgradleconventions loader attribute +def loaderAttribute = Attribute.of('io.github.mcgradleconventions.loader', String) +['apiElements', 'runtimeElements', 'sourcesElements', 'javadocElements'].each { variant -> + configurations.named(variant) { + attributes { + attribute(loaderAttribute, 'common') + } + } +} +sourceSets.configureEach { + [it.compileClasspathConfigurationName, it.runtimeClasspathConfigurationName].each { variant-> + configurations.named(variant) { + attributes { + attribute(loaderAttribute, 'common') + } + } + } +} diff --git a/common/src/main/java/in/northwestw/autofish/AutoFish.java b/common/src/main/java/in/northwestw/autofish/AutoFish.java new file mode 100644 index 0000000..6336c65 --- /dev/null +++ b/common/src/main/java/in/northwestw/autofish/AutoFish.java @@ -0,0 +1,26 @@ +package in.northwestw.autofish; + +import in.northwestw.autofish.config.Config; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.network.chat.contents.PlainTextContents; +import net.minecraft.network.chat.contents.TranslatableContents; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class AutoFish +{ + public static final String MOD_ID = "autofish"; + public static final Logger LOGGER = LogManager.getLogger(); + + static { + Config.load(); + } + + public static MutableComponent getTranslatableComponent(String key, Object... args) { + return MutableComponent.create(new TranslatableContents(key, null, args)); + } + + public static MutableComponent getLiteralComponent(String str) { + return MutableComponent.create(new PlainTextContents.LiteralContents(str)); + } +} diff --git a/common/src/main/java/in/northwestw/autofish/config/Config.java b/common/src/main/java/in/northwestw/autofish/config/Config.java new file mode 100644 index 0000000..e6c7074 --- /dev/null +++ b/common/src/main/java/in/northwestw/autofish/config/Config.java @@ -0,0 +1,169 @@ +package in.northwestw.autofish.config; + +import com.google.common.collect.Lists; +import com.google.gson.*; +import in.northwestw.autofish.AutoFish; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.List; + +public class Config { + private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); + + public static final long[] RECAST_DELAY_RANGE = { 20L, 1L, 600L }; + public static final long[] REEL_IN_DELAY_RANGE = { 0L, 0L, 600L }; + public static final long[] THROW_DELAY_RANGE = { 10L, 5L, 600L }; + public static final long[] CHECK_INTERVAL_RANGE = { 200L, 20L, 72000L }; + + public static long recastDelay = 20, reelInDelay = 0, throwDelay = 10, checkInterval = 200; + public static boolean autoFish = true, rodProtect = true, autoReplace = true, allFilters = true; + public static List filter = Lists.newArrayList(), prioritize = Lists.newArrayList(); + + public static void save() { + try { + File file = new File("config/" + AutoFish.MOD_ID + ".json"); + JsonObject json = new JsonObject(); + json.addProperty("recast_delay", recastDelay); + json.addProperty("reel_in_delay", reelInDelay); + json.addProperty("throw_delay", throwDelay); + json.addProperty("check_interval", checkInterval); + json.addProperty("auto_fish", autoFish); + json.addProperty("rod_protect", rodProtect); + json.addProperty("auto_replace", autoReplace); + json.addProperty("all_filters", allFilters); + + JsonArray array = new JsonArray(); + filter.forEach(array::add); + json.add("filter", array); + + if (file.exists() || file.createNewFile()) { + PrintWriter writer = new PrintWriter(file); + writer.println(GSON.toJson(json)); + writer.close(); + } + } catch (IOException e) { + AutoFish.LOGGER.error(e); + } + } + public static void load() { + try { + File file = new File("config/" + AutoFish.MOD_ID + ".json"); + if (!file.exists()) { + save(); + } else { + JsonObject json = GSON.fromJson(new FileReader(file), JsonObject.class); + if (json.has("recast_delay")) + recastDelay = json.get("recast_delay").getAsLong(); + if (json.has("reel_in_delay")) + reelInDelay = json.get("reel_in_delay").getAsLong(); + if (json.has("throw_delay")) + throwDelay = json.get("throw_delay").getAsLong(); + if (json.has("check_interval")) + checkInterval = json.get("check_interval").getAsLong(); + if (json.has("auto_fish")) + autoFish = json.get("auto_fish").getAsBoolean(); + if (json.has("rod_protect")) + rodProtect = json.get("rod_protect").getAsBoolean(); + if (json.has("auto_replace")) + autoReplace = json.get("auto_replace").getAsBoolean(); + if (json.has("all_filters")) + allFilters = json.get("all_filters").getAsBoolean(); + if (json.has("filter")) + filter = json.getAsJsonArray("filter").asList().stream().map(JsonElement::getAsString).toList(); + + // validate + if (recastDelay < 1 || recastDelay > 600) { + AutoFish.LOGGER.warn("recast_delay must be in range [1, 600]. Defaults to 20"); + recastDelay = 20; + } + if (reelInDelay < 0 || reelInDelay > 600) { + AutoFish.LOGGER.warn("reel_in_delay must be in range [0, 600]. Defaults to 0"); + reelInDelay = 0; + } + if (throwDelay < 5 || throwDelay > 600) { + AutoFish.LOGGER.warn("throw_delay must be in range [5, 600]. Defaults to 10"); + throwDelay = 10; + } + if (checkInterval < 20 || checkInterval > 72000) { + AutoFish.LOGGER.warn("check_interval must be in range [20, 72000]. Defaults to 200"); + checkInterval = 200; + } + } + } catch (IOException e) { + AutoFish.LOGGER.error(e); + } + } + + public static void setRecastDelay(long recastDelay) { + if (recastDelay < 1 || recastDelay > 600) { + AutoFish.LOGGER.warn("max_circuit_size must be in range [1, 600]. Defaults to 20"); + recastDelay = 20; + } + Config.recastDelay = recastDelay; + Config.save(); + AutoFish.LOGGER.debug("Set Recast Delay: " + recastDelay); + } + + public static void setReelInDelay(long reelInDelay) { + if (reelInDelay < 0 || reelInDelay > 600) { + AutoFish.LOGGER.warn("reel_in_delay must be in range [0, 600]. Defaults to 0"); + reelInDelay = 0; + } + Config.reelInDelay = reelInDelay; + Config.save(); + AutoFish.LOGGER.debug("Set Reel In Delay: " + reelInDelay); + } + + public static void setThrowDelay(long throwDelay) { + if (throwDelay < 5 || throwDelay > 600) { + AutoFish.LOGGER.warn("throw_delay must be in range [5, 600]. Defaults to 10"); + throwDelay = 10; + } + Config.throwDelay = throwDelay; + Config.save(); + AutoFish.LOGGER.debug("Set Throw Delay: " + throwDelay); + } + + public static void setCheckInterval(long checkInterval) { + if (checkInterval < 20 || checkInterval > 72000) { + AutoFish.LOGGER.warn("check_interval must be in range [20, 72000]. Defaults to 200"); + checkInterval = 200; + } + Config.checkInterval = checkInterval; + Config.save(); + AutoFish.LOGGER.debug("Set Check Interval: " + checkInterval); + } + + public static void setAutoFish(boolean autoFish) { + Config.autoFish = autoFish; + Config.save(); + AutoFish.LOGGER.info("Toggle AutoFish: " + autoFish); + } + + public static void setRodProtect(boolean rodProtect) { + Config.rodProtect = rodProtect; + Config.save(); + AutoFish.LOGGER.info("Toggle Rod Protect: " + rodProtect); + } + + public static void setAutoReplace(boolean autoReplace) { + Config.autoReplace = autoReplace; + Config.save(); + AutoFish.LOGGER.info("Toggle Auto Replace: " + autoReplace); + } + + public static void enableFilter(boolean filter) { + Config.allFilters = filter; + Config.save(); + AutoFish.LOGGER.info("Toggle Filter: " + filter); + } + + public static void setFilter(List list) { + Config.filter = list; + Config.save(); + AutoFish.LOGGER.info("Received new Filter"); + } +} diff --git a/src/main/java/ml/northwestwind/forgeautofish/config/gui/CheckIntervalScreen.java b/common/src/main/java/in/northwestw/autofish/config/gui/CheckIntervalScreen.java similarity index 88% rename from src/main/java/ml/northwestwind/forgeautofish/config/gui/CheckIntervalScreen.java rename to common/src/main/java/in/northwestw/autofish/config/gui/CheckIntervalScreen.java index f25e77f..3e11c82 100644 --- a/src/main/java/ml/northwestwind/forgeautofish/config/gui/CheckIntervalScreen.java +++ b/common/src/main/java/in/northwestw/autofish/config/gui/CheckIntervalScreen.java @@ -1,8 +1,8 @@ -package ml.northwestwind.forgeautofish.config.gui; +package in.northwestw.autofish.config.gui; -import ml.northwestwind.forgeautofish.AutoFish; -import ml.northwestwind.forgeautofish.config.Config; -import ml.northwestwind.forgeautofish.handler.AutoFishHandler; +import in.northwestw.autofish.AutoFish; +import in.northwestw.autofish.config.Config; +import in.northwestw.autofish.handler.AutoFishHandler; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.components.Button; @@ -32,13 +32,13 @@ public boolean mouseClicked(MouseButtonEvent ev, boolean p_430750_) { return super.mouseClicked(ev, p_430750_); } }; - checkInterval.setValue(Long.toString(AutoFishHandler.checkInterval)); + checkInterval.setValue(Long.toString(Config.checkInterval)); addRenderableWidget(checkInterval); Button save = new Button.Builder(AutoFish.getTranslatableComponent("gui.setcheckinterval.save"), button -> { - if (!isNumeric(checkInterval.getValue())) checkInterval.setValue(Long.toString(AutoFishHandler.checkInterval)); + if (!isNumeric(checkInterval.getValue())) checkInterval.setValue(Long.toString(Config.checkInterval)); else { long delay = Long.parseLong(checkInterval.getValue()); - if (delay < Config.CHECK_INTERVAL_RANGE[1] || delay > Config.CHECK_INTERVAL_RANGE[2]) checkInterval.setValue(Long.toString(AutoFishHandler.checkInterval)); + if (delay < Config.CHECK_INTERVAL_RANGE[1] || delay > Config.CHECK_INTERVAL_RANGE[2]) checkInterval.setValue(Long.toString(Config.checkInterval)); else { Config.setCheckInterval(delay); Minecraft.getInstance().setScreenAndShow(parent); diff --git a/src/main/java/ml/northwestwind/forgeautofish/config/gui/FilterSelectionScreen.java b/common/src/main/java/in/northwestw/autofish/config/gui/FilterSelectionScreen.java similarity index 74% rename from src/main/java/ml/northwestwind/forgeautofish/config/gui/FilterSelectionScreen.java rename to common/src/main/java/in/northwestw/autofish/config/gui/FilterSelectionScreen.java index b7d8cee..2922895 100644 --- a/src/main/java/ml/northwestwind/forgeautofish/config/gui/FilterSelectionScreen.java +++ b/common/src/main/java/in/northwestw/autofish/config/gui/FilterSelectionScreen.java @@ -1,8 +1,8 @@ -package ml.northwestwind.forgeautofish.config.gui; +package in.northwestw.autofish.config.gui; import com.google.common.collect.Lists; -import ml.northwestwind.forgeautofish.AutoFish; -import ml.northwestwind.forgeautofish.config.Config; +import in.northwestw.autofish.AutoFish; +import in.northwestw.autofish.config.Config; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.components.Button; @@ -10,11 +10,12 @@ import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.input.KeyEvent; import net.minecraft.client.input.MouseButtonEvent; +import net.minecraft.core.HolderSet; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.resources.Identifier; +import net.minecraft.resources.ResourceKey; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; -import net.minecraftforge.registries.ForgeRegistries; -import net.minecraftforge.registries.tags.IReverseTag; import org.lwjgl.glfw.GLFW; import java.awt.*; @@ -26,9 +27,9 @@ public class FilterSelectionScreen extends Screen { private final Screen parent; private EditBox search; - private final Collection original = ForgeRegistries.ITEMS.getValues(); + private final Collection original = BuiltInRegistries.ITEM.stream().toList(); private Collection searching; - private final Set selected = new HashSet<>(Config.FILTER.get().stream().map(string -> ForgeRegistries.ITEMS.getValue(Identifier.parse(string))).collect(Collectors.toList())); + private final Set selected = new HashSet<>(Config.filter.stream().map(string -> BuiltInRegistries.ITEM.getOptional(Identifier.parse(string))).filter(Optional::isPresent).map(Optional::get).collect(Collectors.toList())); private int page, maxPage = (int) Math.ceil(original.size() / 300.0), max = 300; private boolean clickProcessed = true; private double clickX, clickY; @@ -57,26 +58,24 @@ public boolean mouseClicked(MouseButtonEvent ev, boolean p_430750_) { }; search.setResponder(s -> { String[] args = s.split("/ +/"); - String[] mods = Arrays.stream(args).filter(s1 -> s1.startsWith("@")).toArray(String[]::new); - String[] tags = Arrays.stream(args).filter(s1 -> s1.startsWith("#")).toArray(String[]::new); - String[] finalArgs = Arrays.stream(args).filter(s1 -> !s1.startsWith("@") && !s1.startsWith("#")).toArray(String[]::new);; + List mods = Lists.newArrayList(), tags = Lists.newArrayList(), paths = Lists.newArrayList(); + for (String arg : args) { + if (arg.startsWith("@")) mods.add(arg.toLowerCase().substring(1)); + else if (arg.startsWith("#")) tags.add(arg.toLowerCase().substring(1)); + else paths.add(arg.toLowerCase()); + } + List> itemTags = BuiltInRegistries.ITEM.getTags().filter(tag -> tags.stream().anyMatch(t -> tag.key().location().getPath().contains(t))).toList(); searching = original.stream().filter(item -> { - Identifier rl = ForgeRegistries.ITEMS.getKey(item); - boolean matchmod = mods.length < 1, matchtag = tags.length < 1, matcharg = finalArgs.length < 1; - for (String mod : mods) { - mod = mod.toLowerCase().substring(1); - if (rl != null) matchmod = rl.getNamespace().toLowerCase().contains(mod); - } - Optional> reverseTagsOptional = ForgeRegistries.ITEMS.tags().getReverseTag(item); - if (reverseTagsOptional.isPresent()) - for (String tag : tags) { - String finalTag = tag.toLowerCase().substring(1); - matchtag = reverseTagsOptional.get().getTagKeys().anyMatch(tagKey -> tagKey.location().getPath().contains(finalTag)); - } - for (String arg : finalArgs) { - arg = arg.toLowerCase(); - if (rl != null) matcharg = rl.getPath().contains(arg); - } + Optional> opt = BuiltInRegistries.ITEM.getResourceKey(item); + if (opt.isEmpty()) return false; + Identifier rl = opt.get().identifier(); + boolean matchmod = mods.isEmpty(), matchtag = tags.isEmpty(), matcharg = false; + for (String mod : mods) + matchmod = matchmod || rl.getNamespace().toLowerCase().contains(mod); + for (HolderSet.Named itemTag : itemTags) + matchtag = matchtag || itemTag.stream().anyMatch(tagItem -> tagItem.value() == item); + for (String arg : paths) + matcharg = matcharg || rl.getPath().contains(arg); return matchmod && matchtag && matcharg; }).collect(Collectors.toList()); maxPage = (int) Math.ceil(searching.size() / (double) max); @@ -84,8 +83,8 @@ public boolean mouseClicked(MouseButtonEvent ev, boolean p_430750_) { }); addRenderableWidget(search); Button add = new Button.Builder(AutoFish.getTranslatableComponent("gui.filterselection.save"), button -> { - List items = selected.stream().map(item -> Objects.requireNonNullElse(ForgeRegistries.ITEMS.getKey(item), item).toString()).collect(Collectors.toList()); - Config.setFILTER(items); + List items = selected.stream().map(item -> BuiltInRegistries.ITEM.getKey(item).toString()).collect(Collectors.toList()); + Config.setFilter(items); Minecraft.getInstance().setScreenAndShow(parent); }).pos(this.width / 2 - 75, 60).size(72, 20).build(); addRenderableWidget(add); @@ -105,9 +104,10 @@ public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mo graphics.centeredText(this.font, this.title, this.width / 2, 20, -1); Collection searchingCopy = Lists.newArrayList(); Collection prioritized = searching.stream().filter(item -> { - Identifier rl = ForgeRegistries.ITEMS.getKey(item); - if (rl == null) return false; - boolean pri = Config.PRIORITIZE.get().contains(rl.toString()); + Optional> opt = BuiltInRegistries.ITEM.getResourceKey(item); + if (opt.isEmpty()) return false; + Identifier rl = opt.get().identifier(); + boolean pri = Config.prioritize.contains(rl.toString()); if (!pri) searchingCopy.add(item); return pri; }).toList(); diff --git a/src/main/java/ml/northwestwind/forgeautofish/config/gui/RecastDelayScreen.java b/common/src/main/java/in/northwestw/autofish/config/gui/RecastDelayScreen.java similarity index 88% rename from src/main/java/ml/northwestwind/forgeautofish/config/gui/RecastDelayScreen.java rename to common/src/main/java/in/northwestw/autofish/config/gui/RecastDelayScreen.java index 43a7ccf..bf52499 100644 --- a/src/main/java/ml/northwestwind/forgeautofish/config/gui/RecastDelayScreen.java +++ b/common/src/main/java/in/northwestw/autofish/config/gui/RecastDelayScreen.java @@ -1,8 +1,8 @@ -package ml.northwestwind.forgeautofish.config.gui; +package in.northwestw.autofish.config.gui; -import ml.northwestwind.forgeautofish.AutoFish; -import ml.northwestwind.forgeautofish.config.Config; -import ml.northwestwind.forgeautofish.handler.AutoFishHandler; +import in.northwestw.autofish.AutoFish; +import in.northwestw.autofish.config.Config; +import in.northwestw.autofish.handler.AutoFishHandler; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.components.Button; @@ -32,13 +32,13 @@ public boolean mouseClicked(MouseButtonEvent ev, boolean flag) { return super.mouseClicked(ev, flag); } }; - recastDelay.setValue(Long.toString(AutoFishHandler.recastDelay)); + recastDelay.setValue(Long.toString(Config.recastDelay)); addRenderableWidget(recastDelay); Button save = new Button.Builder(AutoFish.getTranslatableComponent("gui.setrecastdelay.save"), button -> { - if (!isNumeric(recastDelay.getValue())) recastDelay.setValue(Long.toString(AutoFishHandler.recastDelay)); + if (!isNumeric(recastDelay.getValue())) recastDelay.setValue(Long.toString(Config.recastDelay)); else { long delay = Long.parseLong(recastDelay.getValue()); - if (delay < Config.RECAST_DELAY_RANGE[1] || delay > Config.RECAST_DELAY_RANGE[2]) recastDelay.setValue(Long.toString(AutoFishHandler.recastDelay)); + if (delay < Config.RECAST_DELAY_RANGE[1] || delay > Config.RECAST_DELAY_RANGE[2]) recastDelay.setValue(Long.toString(Config.recastDelay)); else { Config.setRecastDelay(delay); Minecraft.getInstance().setScreenAndShow(parent); diff --git a/src/main/java/ml/northwestwind/forgeautofish/config/gui/ReelInDelayScreen.java b/common/src/main/java/in/northwestw/autofish/config/gui/ReelInDelayScreen.java similarity index 88% rename from src/main/java/ml/northwestwind/forgeautofish/config/gui/ReelInDelayScreen.java rename to common/src/main/java/in/northwestw/autofish/config/gui/ReelInDelayScreen.java index 7aca35f..c188053 100644 --- a/src/main/java/ml/northwestwind/forgeautofish/config/gui/ReelInDelayScreen.java +++ b/common/src/main/java/in/northwestw/autofish/config/gui/ReelInDelayScreen.java @@ -1,8 +1,8 @@ -package ml.northwestwind.forgeautofish.config.gui; +package in.northwestw.autofish.config.gui; -import ml.northwestwind.forgeautofish.AutoFish; -import ml.northwestwind.forgeautofish.config.Config; -import ml.northwestwind.forgeautofish.handler.AutoFishHandler; +import in.northwestw.autofish.AutoFish; +import in.northwestw.autofish.config.Config; +import in.northwestw.autofish.handler.AutoFishHandler; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.components.Button; @@ -32,13 +32,13 @@ public boolean mouseClicked(MouseButtonEvent ev, boolean flag) { return super.mouseClicked(ev, flag); } }; - reelInDelay.setValue(Long.toString(AutoFishHandler.reelInDelay)); + reelInDelay.setValue(Long.toString(Config.reelInDelay)); addRenderableWidget(reelInDelay); Button save = new Button.Builder(AutoFish.getTranslatableComponent("gui.setreelindelay.save"), button -> { - if (!isNumeric(reelInDelay.getValue())) reelInDelay.setValue(Long.toString(AutoFishHandler.recastDelay)); + if (!isNumeric(reelInDelay.getValue())) reelInDelay.setValue(Long.toString(Config.recastDelay)); else { long delay = Long.parseLong(reelInDelay.getValue()); - if (delay < Config.REEL_IN_DELAY_RANGE[1] || delay > Config.REEL_IN_DELAY_RANGE[2]) reelInDelay.setValue(Long.toString(AutoFishHandler.reelInDelay)); + if (delay < Config.REEL_IN_DELAY_RANGE[1] || delay > Config.REEL_IN_DELAY_RANGE[2]) reelInDelay.setValue(Long.toString(Config.reelInDelay)); else { Config.setReelInDelay(delay); Minecraft.getInstance().setScreenAndShow(parent); diff --git a/src/main/java/ml/northwestwind/forgeautofish/config/gui/SettingsScreen.java b/common/src/main/java/in/northwestw/autofish/config/gui/SettingsScreen.java similarity index 62% rename from src/main/java/ml/northwestwind/forgeautofish/config/gui/SettingsScreen.java rename to common/src/main/java/in/northwestw/autofish/config/gui/SettingsScreen.java index a21ed94..13c47c7 100644 --- a/src/main/java/ml/northwestwind/forgeautofish/config/gui/SettingsScreen.java +++ b/common/src/main/java/in/northwestw/autofish/config/gui/SettingsScreen.java @@ -1,6 +1,6 @@ -package ml.northwestwind.forgeautofish.config.gui; +package in.northwestw.autofish.config.gui; -import ml.northwestwind.forgeautofish.AutoFish; +import in.northwestw.autofish.AutoFish; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.components.Button; @@ -10,7 +10,7 @@ public class SettingsScreen extends Screen { private static final int WIDTH = 150, HEIGHT = 20, MARGIN = 5; public SettingsScreen() { - super(AutoFish.getTranslatableComponent("gui.forgeautofish")); + super(AutoFish.getTranslatableComponent("gui.autofish")); } @Override @@ -21,11 +21,11 @@ public boolean isPauseScreen() { @Override protected void init() { Button.Builder[] builders = { - new Button.Builder(AutoFish.getTranslatableComponent("gui.forgeautofish.recastdelay"), button -> Minecraft.getInstance().setScreenAndShow(new RecastDelayScreen(this))), - new Button.Builder(AutoFish.getTranslatableComponent("gui.forgeautofish.reelindelay"), button -> Minecraft.getInstance().setScreenAndShow(new ReelInDelayScreen(this))), - new Button.Builder(AutoFish.getTranslatableComponent("gui.forgeautofish.throwdelay"), button -> Minecraft.getInstance().setScreenAndShow(new ThrowDelayScreen(this))), - new Button.Builder(AutoFish.getTranslatableComponent("gui.forgeautofish.checkinterval"), button -> Minecraft.getInstance().setScreenAndShow(new CheckIntervalScreen(this))), - new Button.Builder(AutoFish.getTranslatableComponent("gui.forgeautofish.filter"), button -> Minecraft.getInstance().setScreenAndShow(new SuperFilterScreen(this))) + new Button.Builder(AutoFish.getTranslatableComponent("gui.autofish.recastdelay"), button -> Minecraft.getInstance().setScreenAndShow(new RecastDelayScreen(this))), + new Button.Builder(AutoFish.getTranslatableComponent("gui.autofish.reelindelay"), button -> Minecraft.getInstance().setScreenAndShow(new ReelInDelayScreen(this))), + new Button.Builder(AutoFish.getTranslatableComponent("gui.autofish.throwdelay"), button -> Minecraft.getInstance().setScreenAndShow(new ThrowDelayScreen(this))), + new Button.Builder(AutoFish.getTranslatableComponent("gui.autofish.checkinterval"), button -> Minecraft.getInstance().setScreenAndShow(new CheckIntervalScreen(this))), + new Button.Builder(AutoFish.getTranslatableComponent("gui.autofish.filter"), button -> Minecraft.getInstance().setScreenAndShow(new SuperFilterScreen(this))) }; for (int ii = 0; ii < builders.length; ii++) { @@ -33,7 +33,7 @@ protected void init() { addRenderableWidget(button); } - Button done = new Button.Builder(AutoFish.getTranslatableComponent("gui.forgeautofish.done"), button -> onClose()).pos(this.width / 2 - 75, this.height - 25).size(150, 20).build(); + Button done = new Button.Builder(AutoFish.getTranslatableComponent("gui.autofish.done"), button -> onClose()).pos(this.width / 2 - 75, this.height - 25).size(150, 20).build(); addRenderableWidget(done); } diff --git a/src/main/java/ml/northwestwind/forgeautofish/config/gui/SuperFilterScreen.java b/common/src/main/java/in/northwestw/autofish/config/gui/SuperFilterScreen.java similarity index 73% rename from src/main/java/ml/northwestwind/forgeautofish/config/gui/SuperFilterScreen.java rename to common/src/main/java/in/northwestw/autofish/config/gui/SuperFilterScreen.java index 4beeb64..570f199 100644 --- a/src/main/java/ml/northwestwind/forgeautofish/config/gui/SuperFilterScreen.java +++ b/common/src/main/java/in/northwestw/autofish/config/gui/SuperFilterScreen.java @@ -1,7 +1,8 @@ -package ml.northwestwind.forgeautofish.config.gui; +package in.northwestw.autofish.config.gui; -import ml.northwestwind.forgeautofish.AutoFish; -import ml.northwestwind.forgeautofish.config.Config; +import com.google.common.collect.Lists; +import in.northwestw.autofish.AutoFish; +import in.northwestw.autofish.config.Config; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.components.Button; @@ -9,16 +10,18 @@ import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.input.KeyEvent; import net.minecraft.client.input.MouseButtonEvent; +import net.minecraft.core.HolderSet; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.resources.Identifier; +import net.minecraft.resources.ResourceKey; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; -import net.minecraftforge.registries.ForgeRegistries; -import net.minecraftforge.registries.tags.IReverseTag; import org.lwjgl.glfw.GLFW; import java.awt.*; import java.util.Arrays; import java.util.Collection; +import java.util.List; import java.util.Optional; import java.util.stream.Collectors; @@ -49,7 +52,7 @@ protected void init() { reducedHeight = this.height - 90; reducedWidth = this.width - 30; max = /* (int) Math.round(30 * (reducedWidth / 550.0 + reducedHeight / 330.0) / 2.0) */ 30; - original = Config.FILTER.get().stream().map(string -> ForgeRegistries.ITEMS.getValue(Identifier.parse(string))).collect(Collectors.toList()); + original = Config.filter.stream().map(string -> BuiltInRegistries.ITEM.getOptional(Identifier.parse(string))).filter(Optional::isPresent).map(Optional::get).collect(Collectors.toList()); maxPage = (int) Math.ceil(original.size() / (double) max); searching = original; search = new EditBox(this.font, this.width / 2 - 75, 35, 150, 20, AutoFish.getTranslatableComponent("gui.superfilterscreen.search")) { @@ -61,26 +64,24 @@ public boolean mouseClicked(MouseButtonEvent ev, boolean flag) { }; search.setResponder(s -> { String[] args = s.split("/ +/"); - String[] mods = Arrays.stream(args).filter(s1 -> s1.startsWith("@")).toArray(String[]::new); - String[] tags = Arrays.stream(args).filter(s1 -> s1.startsWith("#")).toArray(String[]::new); - String[] finalArgs = Arrays.stream(args).filter(s1 -> !s1.startsWith("@") && !s1.startsWith("#")).toArray(String[]::new);; + List mods = Lists.newArrayList(), tags = Lists.newArrayList(), paths = Lists.newArrayList(); + for (String arg : args) { + if (arg.startsWith("@")) mods.add(arg.toLowerCase().substring(1)); + else if (arg.startsWith("#")) tags.add(arg.toLowerCase().substring(1)); + else paths.add(arg.toLowerCase()); + } + List> itemTags = BuiltInRegistries.ITEM.getTags().filter(tag -> tags.stream().anyMatch(t -> tag.key().location().getPath().contains(t))).toList(); searching = original.stream().filter(item -> { - Identifier rl = ForgeRegistries.ITEMS.getKey(item); - boolean matchmod = mods.length < 1, matchtag = tags.length < 1, matcharg = finalArgs.length < 1; - for (String mod : mods) { - mod = mod.toLowerCase().substring(1); - if (rl != null) matchmod = rl.getNamespace().toLowerCase().contains(mod); - } - Optional> reverseTagsOptional = ForgeRegistries.ITEMS.tags().getReverseTag(item); - if (reverseTagsOptional.isPresent()) - for (String tag : tags) { - String finalTag = tag.toLowerCase().substring(1); - matchtag = reverseTagsOptional.get().getTagKeys().anyMatch(tagKey -> tagKey.location().getPath().contains(finalTag)); - } - for (String arg : finalArgs) { - arg = arg.toLowerCase(); - if (rl != null) matcharg = rl.getPath().contains(arg); - } + Optional> opt = BuiltInRegistries.ITEM.getResourceKey(item); + if (opt.isEmpty()) return false; + Identifier rl = opt.get().identifier(); + boolean matchmod = mods.isEmpty(), matchtag = tags.isEmpty(), matcharg = false; + for (String mod : mods) + matchmod = matchmod || rl.getNamespace().toLowerCase().contains(mod); + for (HolderSet.Named itemTag : itemTags) + matchtag = matchtag || itemTag.stream().anyMatch(tagItem -> tagItem.value() == item); + for (String arg : paths) + matcharg = matcharg || rl.getPath().contains(arg); return matchmod && matchtag && matcharg; }).collect(Collectors.toList()); maxPage = (int) Math.ceil(original.size() / (double) max); diff --git a/src/main/java/ml/northwestwind/forgeautofish/config/gui/ThrowDelayScreen.java b/common/src/main/java/in/northwestw/autofish/config/gui/ThrowDelayScreen.java similarity index 88% rename from src/main/java/ml/northwestwind/forgeautofish/config/gui/ThrowDelayScreen.java rename to common/src/main/java/in/northwestw/autofish/config/gui/ThrowDelayScreen.java index 913c432..ae1a355 100644 --- a/src/main/java/ml/northwestwind/forgeautofish/config/gui/ThrowDelayScreen.java +++ b/common/src/main/java/in/northwestw/autofish/config/gui/ThrowDelayScreen.java @@ -1,8 +1,8 @@ -package ml.northwestwind.forgeautofish.config.gui; +package in.northwestw.autofish.config.gui; -import ml.northwestwind.forgeautofish.AutoFish; -import ml.northwestwind.forgeautofish.config.Config; -import ml.northwestwind.forgeautofish.handler.AutoFishHandler; +import in.northwestw.autofish.AutoFish; +import in.northwestw.autofish.config.Config; +import in.northwestw.autofish.handler.AutoFishHandler; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.components.Button; @@ -32,13 +32,13 @@ public boolean mouseClicked(MouseButtonEvent ev, boolean flag) { return super.mouseClicked(ev, flag); } }; - throwDelay.setValue(Long.toString(AutoFishHandler.throwDelay)); + throwDelay.setValue(Long.toString(Config.throwDelay)); addRenderableWidget(throwDelay); Button save = new Button.Builder(AutoFish.getTranslatableComponent("gui.setthrowdelay.save"), button -> { - if (!isNumeric(throwDelay.getValue())) throwDelay.setValue(Long.toString(AutoFishHandler.throwDelay)); + if (!isNumeric(throwDelay.getValue())) throwDelay.setValue(Long.toString(Config.throwDelay)); else { long delay = Long.parseLong(throwDelay.getValue()); - if (delay < Config.THROW_DELAY_RANGE[1] || delay > Config.THROW_DELAY_RANGE[2]) throwDelay.setValue(Long.toString(AutoFishHandler.throwDelay)); + if (delay < Config.THROW_DELAY_RANGE[1] || delay > Config.THROW_DELAY_RANGE[2]) throwDelay.setValue(Long.toString(Config.throwDelay)); else { Config.setThrowDelay(delay); Minecraft.getInstance().setScreenAndShow(parent); diff --git a/src/main/java/ml/northwestwind/forgeautofish/handler/AutoFishHandler.java b/common/src/main/java/in/northwestw/autofish/handler/AutoFishHandler.java similarity index 67% rename from src/main/java/ml/northwestwind/forgeautofish/handler/AutoFishHandler.java rename to common/src/main/java/in/northwestw/autofish/handler/AutoFishHandler.java index c859bdd..ff61b04 100644 --- a/src/main/java/ml/northwestwind/forgeautofish/handler/AutoFishHandler.java +++ b/common/src/main/java/in/northwestw/autofish/handler/AutoFishHandler.java @@ -1,14 +1,17 @@ -package ml.northwestwind.forgeautofish.handler; +package in.northwestw.autofish.handler; import com.google.common.collect.Lists; -import ml.northwestwind.forgeautofish.AutoFish; -import ml.northwestwind.forgeautofish.config.Config; -import ml.northwestwind.forgeautofish.config.gui.SettingsScreen; -import ml.northwestwind.forgeautofish.keybind.KeyBinds; +import com.google.common.collect.Maps; +import in.northwestw.autofish.AutoFish; +import in.northwestw.autofish.config.Config; +import in.northwestw.autofish.config.gui.SettingsScreen; +import in.northwestw.autofish.keybind.KeyBinds; import net.minecraft.ChatFormatting; import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.MultiPlayerGameMode; import net.minecraft.client.player.LocalPlayer; +import net.minecraft.core.Holder; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.network.chat.Component; import net.minecraft.resources.Identifier; import net.minecraft.world.InteractionHand; @@ -17,67 +20,52 @@ import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.phys.Vec3; -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.client.event.InputEvent; -import net.minecraftforge.event.TickEvent; -import net.minecraftforge.eventbus.api.listener.SubscribeEvent; -import net.minecraftforge.fml.LogicalSide; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.registries.ForgeRegistries; -import javax.annotation.Nullable; import java.util.List; +import java.util.Map; +import java.util.Optional; -@Mod.EventBusSubscriber(modid = AutoFish.MODID, value = Dist.CLIENT) public class AutoFishHandler { - public static boolean autofish = Config.AUTO_FISH.get(), rodprotect = Config.ROD_PROTECT.get(), autoreplace = Config.AUTO_REPLACE.get(), itemfilter = Config.ALL_FILTERS.get(); - public static long recastDelay = Config.RECAST_DELAY.get(), reelInDelay = Config.REEL_IN_DELAY.get(), throwDelay = Config.THROW_DELAY.get(), checkInterval = Config.CHECK_INTERVAL.get(); private static final List shouldDrop = Lists.newArrayList(); private static boolean processingDrop, pendingReelIn, pendingRecast, lastTickFishing, afterDrop; private static int dropCd, rodSlot; private static long tick, checkTick; - private static List itemsBeforeFished; + private static final Map itemsBeforeFished = Maps.newHashMap(); - @SubscribeEvent - public static void onKeyInput(InputEvent.Key e) { + public static void onKeyInput() { Minecraft minecraft = Minecraft.getInstance(); LocalPlayer player = minecraft.player; if (KeyBinds.autofish.consumeClick()) { - Config.setAutoFish(!autofish); - if (player != null) player.sendOverlayMessage(getText("forgeautofish", autofish)); + Config.setAutoFish(!Config.autoFish); + if (player != null) player.sendOverlayMessage(getText("forgeautofish", Config.autoFish)); } else if (KeyBinds.rodprotect.consumeClick()) { - Config.setRodProtect(!rodprotect); - if (player != null) player.sendOverlayMessage(getText("rodprotect", rodprotect)); + Config.setRodProtect(!Config.rodProtect); + if (player != null) player.sendOverlayMessage(getText("rodprotect", Config.rodProtect)); } else if (KeyBinds.autoreplace.consumeClick()) { - Config.setAutoReplace(!autoreplace); - if (player != null) player.sendOverlayMessage(getText("autoreplace", autoreplace)); + Config.setAutoReplace(!Config.autoReplace); + if (player != null) player.sendOverlayMessage(getText("autoreplace", Config.autoReplace)); } else if (KeyBinds.itemfilter.consumeClick()) { - Config.enableFilter(!itemfilter); - if (player != null) - player.sendOverlayMessage(getText("itemfilter", itemfilter)); + Config.enableFilter(!Config.allFilters); + if (player != null) player.sendOverlayMessage(getText("itemfilter", Config.allFilters)); } else if (KeyBinds.settings.consumeClick()) minecraft.setScreenAndShow(new SettingsScreen()); } - @SubscribeEvent - public static void onPlayerTick(final TickEvent.PlayerTickEvent.Pre ev) { - if (ev.side() != LogicalSide.CLIENT) return; - Player player = ev.player(); + public static void onPlayerTick(final Player player) { + if (Minecraft.getInstance().player == null) return; if (!player.getUUID().equals(Minecraft.getInstance().player.getUUID())) return; if (checkTick > 0) checkTick--; else { - checkTick = checkInterval; + checkTick = Config.checkInterval; if (!pendingRecast) { if (player.fishing == null) recast(player); else if (player.fishing.getDeltaMovement().lengthSqr() == 0) pendingReelIn = true; } } - if (lastTickFishing && player.fishing == null) - itemsBeforeFished = Lists.newArrayList(player.getInventory().getNonEquipmentItems()); - lastTickFishing = player.fishing != null; if (afterDrop) { if (tick == 0 && rodSlot != -1) { player.getInventory().setSelectedSlot(rodSlot); + AutoFish.LOGGER.info("Swapped to hotbar slot {} for rod", rodSlot); rodSlot = -1; } tick++; @@ -89,7 +77,7 @@ public static void onPlayerTick(final TickEvent.PlayerTickEvent.Pre ev) { } if (pendingReelIn) { tick++; - if (tick >= reelInDelay) { + if (tick >= Config.reelInDelay) { reelIn(player); tick = 0; pendingReelIn = false; @@ -107,7 +95,7 @@ public static void onPlayerTick(final TickEvent.PlayerTickEvent.Pre ev) { } if (pendingRecast) { tick++; - if (tick >= recastDelay) { + if (tick >= Config.recastDelay) { checkItem(player); if (processingDrop) { tick = 0; @@ -119,7 +107,7 @@ public static void onPlayerTick(final TickEvent.PlayerTickEvent.Pre ev) { } return; } - if (!autofish || player.fishing == null) return; + if (!Config.autoFish || player.fishing == null) return; Vec3 vector = player.fishing.getDeltaMovement(); double x = vector.x(); double y = vector.y(); @@ -129,20 +117,24 @@ public static void onPlayerTick(final TickEvent.PlayerTickEvent.Pre ev) { } private static void reelIn(Player player) { - if (!autofish) return; + if (!Config.autoFish) return; InteractionHand hand = findHandOfRod(player); if (hand == null) return; + player.getInventory().getNonEquipmentItems().forEach(stack -> { + Identifier rl = BuiltInRegistries.ITEM.getKey(stack.getItem()); + itemsBeforeFished.put(rl, itemsBeforeFished.getOrDefault(rl, 0) + stack.count()); + }); click(player, hand, Minecraft.getInstance().gameMode); ItemStack fishingRod = player.getItemInHand(hand); boolean needReplace = false; if (fishingRod.getMaxDamage() - fishingRod.getDamageValue() < 2) - if (autoreplace) needReplace = true; + if (Config.autoReplace) needReplace = true; else return; - else if (fishingRod.getMaxDamage() - fishingRod.getDamageValue() < 3 && !player.isCreative() && rodprotect) - if (autoreplace) needReplace = true; + else if (fishingRod.getMaxDamage() - fishingRod.getDamageValue() < 3 && !player.isCreative() && Config.rodProtect) + if (Config.autoReplace) needReplace = true; else { - autofish = false; - player.sendOverlayMessage(getText("forgeautofish", autofish)); + Config.autoFish = false; + player.sendOverlayMessage(getText("forgeautofish", Config.autoFish)); return; } if (needReplace) { @@ -152,7 +144,7 @@ else if (fishingRod.getMaxDamage() - fishingRod.getDamageValue() < 3 && !player. if (i == player.getInventory().getSelectedSlot()) continue; ItemStack stack = player.getInventory().getItem(i); if (stack.getItem() instanceof FishingRodItem) { - if (rodprotect && stack.getMaxDamage() - stack.getDamageValue() < 2) continue; + if (Config.rodProtect && stack.getMaxDamage() - stack.getDamageValue() < 2) continue; AutoFish.LOGGER.info("Found fishing rod for replacement"); player.getInventory().setSelectedSlot(i); found = true; @@ -165,7 +157,7 @@ else if (fishingRod.getMaxDamage() - fishingRod.getDamageValue() < 3 && !player. } private static void recast(Player player) { - if (!autofish) return; + if (!Config.autoFish) return; InteractionHand hand = findHandOfRod(player); if (hand == null) return; ItemStack fishingRod = player.getItemInHand(hand); @@ -174,18 +166,19 @@ private static void recast(Player player) { } private static void checkItem(Player player) { - if (itemsBeforeFished != null) { + if (!itemsBeforeFished.isEmpty()) { List items = player.getInventory().getNonEquipmentItems(); - for (String name : Config.FILTER.get()) { + for (String name : Config.filter) { Identifier rl = Identifier.parse(name); - Item item = ForgeRegistries.ITEMS.getValue(rl); - if (item == null) continue; - int newCount = items.stream().filter(stack -> stack.getItem().equals(item)).mapToInt(ItemStack::getCount).reduce(Integer::sum).orElse(0); - int oldCount = itemsBeforeFished.stream().filter(stack -> stack.getItem().equals(item)).mapToInt(ItemStack::getCount).reduce(Integer::sum).orElse(0); + Optional opt = BuiltInRegistries.ITEM.getOptional(rl); + if (opt.isEmpty()) continue; + Item item = opt.get(); + int newCount = items.stream().filter(stack -> stack.getItem().toString().equals(rl.toString())).mapToInt(ItemStack::getCount).reduce(Integer::sum).orElse(0); + int oldCount = itemsBeforeFished.getOrDefault(rl, 0); int diff = newCount - oldCount; for (int ii = 0; ii < diff; ii++) shouldDrop.add(item); } - itemsBeforeFished = null; + itemsBeforeFished.clear(); if (!shouldDrop.isEmpty()) { processingDrop = true; rodSlot = player.getInventory().getSelectedSlot(); @@ -194,9 +187,9 @@ private static void checkItem(Player player) { } private static void dropItem(Player player) { - if (dropCd == 4 || dropCd == 2 || dropCd == 1) return; + if (dropCd != 10 && dropCd != 0) return; Item item = shouldDrop.getFirst(); - if (dropCd == 3) { + if (dropCd == 10) { ((LocalPlayer) player).drop(false); shouldDrop.remove(item); return; @@ -204,19 +197,19 @@ private static void dropItem(Player player) { for (int ii = 0; ii < 9; ii++) { if (!player.getInventory().getItem(ii).getItem().equals(item)) continue; player.getInventory().setSelectedSlot(ii); - dropCd = 5; + AutoFish.LOGGER.info("Swapped to hotbar slot {} for item", ii); + dropCd = 20; return; } // if item cannot be found in hotbar, just ignore it shouldDrop.remove(item); } - private static void click(Player player, InteractionHand hand, @Nullable MultiPlayerGameMode controller) { + private static void click(Player player, InteractionHand hand, MultiPlayerGameMode controller) { if (controller == null) return; controller.useItem(player, hand); } - @Nullable private static InteractionHand findHandOfRod(Player player) { if (player.getMainHandItem().getItem() instanceof FishingRodItem) return InteractionHand.MAIN_HAND; else if (player.getOffhandItem().getItem() instanceof FishingRodItem) return InteractionHand.OFF_HAND; diff --git a/src/main/java/ml/northwestwind/forgeautofish/keybind/KeyBinds.java b/common/src/main/java/in/northwestw/autofish/keybind/KeyBinds.java similarity index 68% rename from src/main/java/ml/northwestwind/forgeautofish/keybind/KeyBinds.java rename to common/src/main/java/in/northwestw/autofish/keybind/KeyBinds.java index af0ede5..3be0934 100644 --- a/src/main/java/ml/northwestwind/forgeautofish/keybind/KeyBinds.java +++ b/common/src/main/java/in/northwestw/autofish/keybind/KeyBinds.java @@ -1,27 +1,20 @@ -package ml.northwestwind.forgeautofish.keybind; +package in.northwestw.autofish.keybind; -import ml.northwestwind.forgeautofish.AutoFish; +import in.northwestw.autofish.AutoFish; import net.minecraft.client.KeyMapping; import net.minecraft.resources.Identifier; -import net.minecraftforge.client.event.RegisterKeyMappingsEvent; import org.lwjgl.glfw.GLFW; public class KeyBinds { public static KeyMapping autofish, rodprotect, autoreplace, settings, itemfilter; - public static void register(final RegisterKeyMappingsEvent event) { - KeyMapping.Category cat = KeyMapping.Category.register(Identifier.fromNamespaceAndPath(AutoFish.MODID, "autofish")); + static { + KeyMapping.Category cat = KeyMapping.Category.register(Identifier.fromNamespaceAndPath(AutoFish.MOD_ID, "autofish")); autofish = new KeyMapping(AutoFish.getTranslatableComponent("key.forgeautofish.autofish").getString(), GLFW.GLFW_KEY_MINUS, cat); rodprotect = new KeyMapping(AutoFish.getTranslatableComponent("key.forgeautofish.rodprotect").getString(), GLFW.GLFW_KEY_BACKSLASH, cat); autoreplace = new KeyMapping(AutoFish.getTranslatableComponent("key.forgeautofish.autoreplace").getString(), GLFW.GLFW_KEY_RIGHT_BRACKET, cat); settings = new KeyMapping(AutoFish.getTranslatableComponent("key.forgeautofish.settings").getString(), GLFW.GLFW_KEY_K, cat); itemfilter = new KeyMapping(AutoFish.getTranslatableComponent("key.forgeautofish.itemfilter").getString(), GLFW.GLFW_KEY_APOSTROPHE, cat); - - event.register(autofish); - event.register(rodprotect); - event.register(autoreplace); - event.register(settings); - event.register(itemfilter); } } diff --git a/src/main/resources/assets/forgeautofish/lang/en_us.json b/common/src/main/resources/assets/forgeautofish/lang/en_us.json similarity index 60% rename from src/main/resources/assets/forgeautofish/lang/en_us.json rename to common/src/main/resources/assets/forgeautofish/lang/en_us.json index 22dcf47..de66ed5 100644 --- a/src/main/resources/assets/forgeautofish/lang/en_us.json +++ b/common/src/main/resources/assets/forgeautofish/lang/en_us.json @@ -1,51 +1,51 @@ -{ - "key.forgeautofish.autofish": "Toggle AutoFish", - "key.forgeautofish.rodprotect": "Toggle Fishing Rod Protection", - "key.forgeautofish.autoreplace": "Toggle Auto Replace", - "key.forgeautofish.settings": "Open Settings", - "key.forgeautofish.itemfilter": "Toggle Item Filter", - "key.categories.forgeautofish": "AutoFish for Forge", - - "toggle.forgeautofish": "%s AutoFish", - "toggle.rodprotect": "%s Fishing Rod Protection", - "toggle.autoreplace": "%s Auto Replace", - "toggle.itemfilter": "%s Item Filter", - - "warning.autoreplace": "Auto Replace Coming Soon", - - "gui.forgeautofish": "AutoFish Configuration", - "gui.forgeautofish.reelindelay": "Reel-In Delay", - "gui.forgeautofish.recastdelay": "Recast Delay", - "gui.forgeautofish.throwdelay": "Throw Delay", - "gui.forgeautofish.checkinterval": "Check Interval", - "gui.forgeautofish.filter": "Item Filter", - "gui.forgeautofish.done": "Done", - - "gui.setreelindelay": "Set Reel-In Delay", - "gui.setreelindelay.reelindelay": "Reel-In Delay", - "gui.setreelindelay.save": "Save Reel-In Delay", - - "gui.setrecastdelay": "Set Recast Delay", - "gui.setrecastdelay.recastdelay": "Recast Delay", - "gui.setrecastdelay.save": "Save Recast Delay", - - "gui.setthrowdelay": "Set Throw Delay", - "gui.setthrowdelay.throwdelay": "Throw Delay", - "gui.setthrowdelay.save": "Save Throw Delay", - - "gui.setcheckinterval": "Set Check Interval", - "gui.setcheckinterval.checkinterval": "Check Interval", - "gui.setcheckinterval.save": "Save Check Interval", - - "gui.superfilterscreen": "Super Item Filter", - "gui.superfilterscreen.openfilter": "Config", - "gui.superfilterscreen.search": "Search", - "gui.superfilterscreen.done": "Done", - - "gui.filterselection": "Item Filter Configuration", - "gui.filterselection.save": "Save", - "gui.filterselection.cancel": "Cancel", - - "toggle.enable.true": "Enabled", - "toggle.enable.false": "Disabled" +{ + "key.autofish.autofish": "Toggle AutoFish", + "key.autofish.rodprotect": "Toggle Fishing Rod Protection", + "key.autofish.autoreplace": "Toggle Auto Replace", + "key.autofish.settings": "Open Settings", + "key.autofish.itemfilter": "Toggle Item Filter", + "key.categories.autofish": "AutoFish for Forge", + + "toggle.autofish": "%s AutoFish", + "toggle.rodprotect": "%s Fishing Rod Protection", + "toggle.autoreplace": "%s Auto Replace", + "toggle.itemfilter": "%s Item Filter", + + "warning.autoreplace": "Auto Replace Coming Soon", + + "gui.autofish": "AutoFish Configuration", + "gui.autofish.reelindelay": "Reel-In Delay", + "gui.autofish.recastdelay": "Recast Delay", + "gui.autofish.throwdelay": "Throw Delay", + "gui.autofish.checkinterval": "Check Interval", + "gui.autofish.filter": "Item Filter", + "gui.autofish.done": "Done", + + "gui.setreelindelay": "Set Reel-In Delay", + "gui.setreelindelay.reelindelay": "Reel-In Delay", + "gui.setreelindelay.save": "Save Reel-In Delay", + + "gui.setrecastdelay": "Set Recast Delay", + "gui.setrecastdelay.recastdelay": "Recast Delay", + "gui.setrecastdelay.save": "Save Recast Delay", + + "gui.setthrowdelay": "Set Throw Delay", + "gui.setthrowdelay.throwdelay": "Throw Delay", + "gui.setthrowdelay.save": "Save Throw Delay", + + "gui.setcheckinterval": "Set Check Interval", + "gui.setcheckinterval.checkinterval": "Check Interval", + "gui.setcheckinterval.save": "Save Check Interval", + + "gui.superfilterscreen": "Super Item Filter", + "gui.superfilterscreen.openfilter": "Config", + "gui.superfilterscreen.search": "Search", + "gui.superfilterscreen.done": "Done", + + "gui.filterselection": "Item Filter Configuration", + "gui.filterselection.save": "Save", + "gui.filterselection.cancel": "Cancel", + + "toggle.enable.true": "Enabled", + "toggle.enable.false": "Disabled" } \ No newline at end of file diff --git a/src/main/resources/assets/forgeautofish/lang/zh_tw.json b/common/src/main/resources/assets/forgeautofish/lang/zh_tw.json similarity index 58% rename from src/main/resources/assets/forgeautofish/lang/zh_tw.json rename to common/src/main/resources/assets/forgeautofish/lang/zh_tw.json index 6630290..4bd31f5 100644 --- a/src/main/resources/assets/forgeautofish/lang/zh_tw.json +++ b/common/src/main/resources/assets/forgeautofish/lang/zh_tw.json @@ -1,23 +1,23 @@ { - "key.forgeautofish.autofish": "切換 自動釣魚", - "key.forgeautofish.rodprotect": "切換 釣竿保護", - "key.forgeautofish.autoreplace": "切換 自動取代", - "key.forgeautofish.settings": "開啟設定", - "key.forgeautofish.itemfilter": "切換 物品過濾", - "key.categories.forgeautofish": "自動釣魚", - - "toggle.forgeautofish": "%s 自動釣魚", + "key.autofish.autofish": "切換 自動釣魚", + "key.autofish.rodprotect": "切換 釣竿保護", + "key.autofish.autoreplace": "切換 自動取代", + "key.autofish.settings": "開啟設定", + "key.autofish.itemfilter": "切換 物品過濾", + "key.categories.autofish": "自動釣魚", + + "toggle.autofish": "%s 自動釣魚", "toggle.rodprotect": "%s 釣竿保護", "toggle.autoreplace": "%s 自動取代", "toggle.itemfilter": "%s 物品過濾", "warning.autoreplace": "自動過濾 即將來臨", - "gui.forgeautofish": "自動釣魚設定", - "gui.forgeautofish.reelindelay": "收竿延遲", - "gui.forgeautofish.recastdelay": "投竿延遲", - "gui.forgeautofish.filter": "物品過濾", - "gui.forgeautofish.done": "完成", + "gui.autofish": "自動釣魚設定", + "gui.autofish.reelindelay": "收竿延遲", + "gui.autofish.recastdelay": "投竿延遲", + "gui.autofish.filter": "物品過濾", + "gui.autofish.done": "完成", "gui.setrecastdelay": "投竿延遲設定", "gui.setrecastdelay.recastdelay": "投竿延遲", diff --git a/src/main/resources/forgeautofish.png b/common/src/main/resources/autofish.png similarity index 100% rename from src/main/resources/forgeautofish.png rename to common/src/main/resources/autofish.png diff --git a/src/main/resources/pack.mcmeta b/common/src/main/resources/pack.mcmeta similarity index 100% rename from src/main/resources/pack.mcmeta rename to common/src/main/resources/pack.mcmeta diff --git a/fabric/build.gradle b/fabric/build.gradle new file mode 100644 index 0000000..8c3ffb5 --- /dev/null +++ b/fabric/build.gradle @@ -0,0 +1,49 @@ +plugins { + id 'multiloader-loader' + id 'net.fabricmc.fabric-loom' +} +dependencies { + minecraft("com.mojang:minecraft:${minecraft_version}") + implementation("net.fabricmc:fabric-loader:${fabric_loader_version}") + implementation("net.fabricmc.fabric-api:fabric-api:${fabric_version}") +} + +loom { + def aw = project(':common').file("src/main/resources/${mod_id}.accesswidener") + if (aw.exists()) { + accessWidenerPath.set(aw) + } + runs { + client { + client() + setConfigName('Fabric Client') + ideConfigGenerated(true) + runDir('runs/client') + } + server { + server() + setConfigName('Fabric Server') + ideConfigGenerated(true) + runDir('runs/server') + } + } +} + +// Implement mcgradleconventions loader attribute +def loaderAttribute = Attribute.of('io.github.mcgradleconventions.loader', String) +['apiElements', 'runtimeElements', 'sourcesElements', 'javadocElements', 'includeInternal', 'modCompileClasspath'].each { variant -> + configurations.named(variant) { + attributes { + attribute(loaderAttribute, 'fabric') + } + } +} +sourceSets.configureEach { + [it.compileClasspathConfigurationName, it.runtimeClasspathConfigurationName].each { variant-> + configurations.named(variant) { + attributes { + attribute(loaderAttribute, 'fabric') + } + } + } +} diff --git a/fabric/src/main/java/in/northwestw/autofish/AutoFishFabric.java b/fabric/src/main/java/in/northwestw/autofish/AutoFishFabric.java new file mode 100644 index 0000000..0f2bb8f --- /dev/null +++ b/fabric/src/main/java/in/northwestw/autofish/AutoFishFabric.java @@ -0,0 +1,24 @@ +package in.northwestw.autofish; + +import in.northwestw.autofish.handler.AutoFishHandler; +import in.northwestw.autofish.keybind.KeyBinds; +import net.fabricmc.api.ModInitializer; +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; +import net.fabricmc.fabric.api.client.keymapping.v1.KeyMappingHelper; + +public class AutoFishFabric implements ModInitializer { + + @Override + public void onInitialize() { + KeyMappingHelper.registerKeyMapping(KeyBinds.autofish); + KeyMappingHelper.registerKeyMapping(KeyBinds.rodprotect); + KeyMappingHelper.registerKeyMapping(KeyBinds.autoreplace); + KeyMappingHelper.registerKeyMapping(KeyBinds.settings); + KeyMappingHelper.registerKeyMapping(KeyBinds.itemfilter); + + ClientTickEvents.END_CLIENT_TICK.register(_ -> AutoFishHandler.onKeyInput()); + ClientTickEvents.START_CLIENT_TICK.register(client -> { + AutoFishHandler.onPlayerTick(client.player); + }); + } +} diff --git a/fabric/src/main/resources/fabric.mod.json b/fabric/src/main/resources/fabric.mod.json new file mode 100644 index 0000000..d20efe4 --- /dev/null +++ b/fabric/src/main/resources/fabric.mod.json @@ -0,0 +1,32 @@ +{ + "schemaVersion": 1, + "id": "${mod_id}", + "version": "${version}", + "name": "${mod_name}", + "description": "${description}", + "authors": [ + "${mod_author}" + ], + "contact": { + "homepage": "https://fabricmc.net/", + "sources": "https://github.com/FabricMC/fabric-example-mod" + }, + "license": "${license}", + "icon": "${mod_id}.png", + "environment": "*", + "entrypoints": { + "main": [ + "in.northwestw.autofish.AutoFishFabric" + ] + }, + "depends": { + "fabricloader": ">=${fabric_loader_version}", + "fabric-api": "*", + "minecraft": "~${minecraft_version}", + "java": ">=${java_version}" + }, + "suggests": { + "another-mod": "*" + } +} + \ No newline at end of file diff --git a/forge/build.gradle b/forge/build.gradle new file mode 100644 index 0000000..47fbfff --- /dev/null +++ b/forge/build.gradle @@ -0,0 +1,81 @@ +plugins { + id 'multiloader-loader' + id 'net.minecraftforge.gradle' version '[7.0.17,8)' + id 'idea' +} +base { + archivesName = "${mod_id}-forge-${minecraft_version}" +} + +minecraft { + mappings channel: 'official', version: minecraft_version + + // Automatically enable forge AccessTransformers if the file exists + // This location is hardcoded in Forge and can not be changed. + // https://github.com/MinecraftForge/MinecraftForge/blob/be1698bb1554f9c8fa2f58e32b9ab70bc4385e60/fmlloader/src/main/java/net/minecraftforge/fml/loading/moddiscovery/ModFile.java#L123 + // Forge still uses SRG names during compile time, so we cannot use the common AT's + def at = file('src/main/resources/META-INF/accesstransformer.cfg') + if (at.exists()) { + accessTransformer = at + } + + runs { + configureEach { + workingDir = layout.projectDirectory.dir('run') + + systemProperty 'eventbus.api.strictRuntimeChecks', 'true' + systemProperty 'forge.enabledGameTestNamespaces', mod_id + } + + register('client') + + register('server') { + args '--nogui' + } + + register('gameTestServer') + + register('data') { + workingDir = layout.projectDirectory.dir('run-data') + + args '--mod', mod_id, '--all', '--output', layout.projectDirectory.dir('src/generated/resources'), '--existing', layout.projectDirectory.dir('src/main/resources') + } + } +} + +sourceSets.main.resources.srcDir 'src/generated/resources' + +repositories { + minecraft.mavenizer(it) // In Kotlin, it = this + maven fg.forgeMaven + maven fg.minecraftLibsMaven +} + +dependencies { + implementation minecraft.dependency("net.minecraftforge:forge:${minecraft_version}-${forge_version}") +} + +sourceSets.each { + def dir = layout.buildDirectory.dir("sourcesSets/$it.name") + it.output.resourcesDir = dir + it.java.destinationDirectory = dir +} + +// Implement mcgradleconventions loader attribute +def loaderAttribute = Attribute.of('io.github.mcgradleconventions.loader', String) +['apiElements', 'runtimeElements', 'sourcesElements', 'javadocElements'].each { variant -> + configurations.named("$variant") { + attributes { + attribute(loaderAttribute, 'forge') + } + } +} +sourceSets.configureEach { + [it.compileClasspathConfigurationName, it.runtimeClasspathConfigurationName].each { variant-> + configurations.named("$variant") { + attributes { + attribute(loaderAttribute, 'forge') + } + } + } +} diff --git a/forge/src/main/java/in/northwestw/autofish/AutoFishForge.java b/forge/src/main/java/in/northwestw/autofish/AutoFishForge.java new file mode 100644 index 0000000..b22a670 --- /dev/null +++ b/forge/src/main/java/in/northwestw/autofish/AutoFishForge.java @@ -0,0 +1,40 @@ +package in.northwestw.autofish; + +import in.northwestw.autofish.handler.AutoFishHandler; +import in.northwestw.autofish.keybind.KeyBinds; +import net.minecraftforge.client.event.InputEvent; +import net.minecraftforge.client.event.RegisterKeyMappingsEvent; +import net.minecraftforge.event.TickEvent; +import net.minecraftforge.eventbus.api.listener.SubscribeEvent; +import net.minecraftforge.fml.LogicalSide; +import net.minecraftforge.fml.common.Mod; + +@Mod(AutoFish.MOD_ID) +public class AutoFishForge { + + public AutoFishForge() { + } + + @Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD) + public static class ModEvents { + @SubscribeEvent + public static void registerKeyMappings(RegisterKeyMappingsEvent event) { + event.register(KeyBinds.autofish); + event.register(KeyBinds.rodprotect); + event.register(KeyBinds.autoreplace); + event.register(KeyBinds.settings); + event.register(KeyBinds.itemfilter); + } + + @SubscribeEvent + public static void inputKey(InputEvent.Key event) { + AutoFishHandler.onKeyInput(); + } + + @SubscribeEvent + public static void playerTickPre(TickEvent.PlayerTickEvent.Pre event) { + if (event.side() != LogicalSide.CLIENT) return; + AutoFishHandler.onPlayerTick(event.player()); + } + } +} \ No newline at end of file diff --git a/forge/src/main/resources/META-INF/mods.toml b/forge/src/main/resources/META-INF/mods.toml new file mode 100644 index 0000000..eed0909 --- /dev/null +++ b/forge/src/main/resources/META-INF/mods.toml @@ -0,0 +1,27 @@ +modLoader = "javafml" #mandatory +loaderVersion = "${forge_loader_version_range}" #mandatory This is typically bumped every Minecraft version by Forge. See https://files.minecraftforge.net/ for a list of versions. +license = "${license}" # Review your options at https://choosealicense.com/. +#issueTrackerURL="https://change.me.to.your.issue.tracker.example.invalid/" #optional +#clientSideOnly=true #optional +[[mods]] #mandatory +modId = "${mod_id}" #mandatory +version = "${version}" #mandatory +displayName = "${mod_name}" #mandatory +#updateJSONURL="https://change.me.example.invalid/updates.json" #optional (see https://mcforge.readthedocs.io/en/latest/gettingstarted/autoupdate/) +#displayURL="https://change.me.to.your.mods.homepage.example.invalid/" #optional (displayed in the mod UI) +logoFile = "${mod_id}.png" #optional +credits = "${credits}" #optional +authors = "${mod_author}" #optional +description = '''${description}''' #mandatory (Supports multiline text) +[[dependencies.${mod_id}]] #optional +modId = "forge" #mandatory +mandatory = true #mandatory +versionRange = "[${forge_version},)" #mandatory +ordering = "NONE" # The order that this dependency should load in relation to your mod, required to be either 'BEFORE' or 'AFTER' if the dependency is not mandatory +side = "BOTH" # Side this dependency is applied on - 'BOTH', 'CLIENT' or 'SERVER' +[[dependencies.${mod_id}]] +modId = "minecraft" +mandatory = true +versionRange = "${minecraft_version_range}" +ordering = "NONE" +side = "BOTH" \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 244a1e4..acf137b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,18 +1,36 @@ -org.gradle.caching=true -org.gradle.parallel=true -org.gradle.configureondemand=true +# Important Notes: +# Every field you add must be added to buildSrc/src/main/groovy/multiloader-common.gradle expandProps map. -org.gradle.configuration-cache=true -org.gradle.configuration-cache.parallel=true -org.gradle.configuration-cache.problems=warn +# Project +version=8.0.0 +group=in.northwestw.in +java_version=25 -net.minecraftforge.gradle.merge-source-sets=true +# Common +minecraft_version=26.2 +mod_name=AutoFish for Everyone +mod_author=NorthWestWind +mod_id=autofish +license=GPLv3 +credits= +description=I like playing survival, but fishing is a boring activity...\nTherefore, I made this mod!\nNow you can AFK Fish like no one else!\n\nNote that this is my first mod, so there might be bugs. +minecraft_version_range=[26.2, 26.3) +## This is the version of minecraft that the 'common' project uses, you can find a list of all versions here +## https://projects.neoforged.net/neoforged/neoform +neo_form_version=26.2-1 -org.gradle.jvmargs=-Xmx3G -org.gradle.java.home=/usr/lib/jvm/java-21-graalvm-ee +# Fabric, see https://fabricmc.net/develop/ for new versions +fabric_version=0.152.1+26.2 +fabric_loader_version=0.19.3 -mc_version=26.2 +# Forge, see https://files.minecraftforge.net/net/minecraftforge/forge/ for new versions forge_version=65.0.1 -build_mc_version=26.2-forge -mod_id=forgeautofish -mod_version=7.1.0 \ No newline at end of file +forge_loader_version_range=[65,) + +# NeoForge, see https://projects.neoforged.net/neoforged/neoforge for new versions +neoforge_version=26.2.0.1-beta +neoforge_loader_version_range=[4,) + +# Gradle +org.gradle.jvmargs=-Xmx3G +org.gradle.daemon=false diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index a4b76b9530d66f5e68d973ea569d8e19de379189..b1b8ef56b44f16b14dc800fa8103a6d89abb526f 100644 GIT binary patch delta 40229 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 delta 35349 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 /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -114,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -133,22 +132,29 @@ location of your Java installation." fi else JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -165,7 +171,6 @@ fi # For Cygwin or MSYS, switch paths to Windows format before running java if "$cygwin" || "$msys" ; then APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) JAVACMD=$( cygpath --unix "$JAVACMD" ) @@ -193,18 +198,27 @@ if "$cygwin" || "$msys" ; then done fi -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. diff --git a/gradlew.bat b/gradlew.bat index ac1b06f..24c62d5 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,89 +1,82 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables, and ensure extensions are enabled +setlocal EnableExtensions + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +"%COMSPEC%" /c exit 1 + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +"%COMSPEC%" /c exit 1 + +:execute +@rem Setup the command line + + + +@rem Execute Gradle +@rem endlocal doesn't take effect until after the line is parsed and variables are expanded +@rem which allows us to clear the local environment before executing the java command +endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel + +:exitWithErrorLevel +@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts +"%COMSPEC%" /c exit %ERRORLEVEL% diff --git a/neoforge/build.gradle b/neoforge/build.gradle new file mode 100644 index 0000000..055d5fd --- /dev/null +++ b/neoforge/build.gradle @@ -0,0 +1,61 @@ +plugins { + id 'multiloader-loader' + id 'net.neoforged.moddev' +} + +neoForge { + version = neoforge_version + // Automatically enable neoforge AccessTransformers if the file exists + def at = project(':common').file('src/main/resources/META-INF/accesstransformer.cfg') + if (at.exists()) { + accessTransformers.from(at.absolutePath) + } + runs { + configureEach { + systemProperty('neoforge.enabledGameTestNamespaces', mod_id) + ideName = "NeoForge ${it.name.capitalize()} (${project.path})" // Unify the run config names with fabric + } + client { + client() + gameDirectory = this.mkdir(this.file('runs/client')) + } + data { + clientData() + gameDirectory = this.mkdir(this.file('runs/data')) + // DataGen can be run by - "./gradlew :neoforge:runData" in Terminal. + // Specify the modid for data generation, where to output the resulting resource, and where to look for existing resources. + programArguments.addAll '--mod', project.mod_id, '--all', '--output', file('src/generated/resources/').getAbsolutePath(), '--existing', file('src/main/resources/').getAbsolutePath() + } + server { + server() + this.file('runs/server').createParentDirectories() + gameDirectory = this.mkdir(this.file('runs/server')) + } + } + mods { + "${mod_id}" { + sourceSet sourceSets.main + } + } +} + +sourceSets.main.resources { srcDir 'src/generated/resources' } + +// Implement mcgradleconventions loader attribute +def loaderAttribute = Attribute.of('io.github.mcgradleconventions.loader', String) +['apiElements', 'runtimeElements', 'sourcesElements', 'javadocElements'].each { variant -> + configurations.named(variant) { + attributes { + attribute(loaderAttribute, 'neoforge') + } + } +} +sourceSets.configureEach { + [it.compileClasspathConfigurationName, it.runtimeClasspathConfigurationName, it.getTaskName(null, 'jarJar')].each { variant -> + configurations.named(variant) { + attributes { + attribute(loaderAttribute, 'neoforge') + } + } + } +} diff --git a/neoforge/src/main/java/in/northwestw/autofish/AutoFishNeoForge.java b/neoforge/src/main/java/in/northwestw/autofish/AutoFishNeoForge.java new file mode 100644 index 0000000..a3f59d7 --- /dev/null +++ b/neoforge/src/main/java/in/northwestw/autofish/AutoFishNeoForge.java @@ -0,0 +1,44 @@ +package in.northwestw.autofish; + +import in.northwestw.autofish.handler.AutoFishHandler; +import in.northwestw.autofish.keybind.KeyBinds; +import net.minecraft.client.player.LocalPlayer; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.bus.api.IEventBus; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.LogicalSide; +import net.neoforged.fml.common.EventBusSubscriber; +import net.neoforged.fml.common.Mod; +import net.neoforged.neoforge.client.event.InputEvent; +import net.neoforged.neoforge.client.event.RegisterKeyMappingsEvent; +import net.neoforged.neoforge.event.tick.PlayerTickEvent; + +@Mod(AutoFish.MOD_ID) +public class AutoFishNeoForge { + + public AutoFishNeoForge(IEventBus eventBus) { + } + + @EventBusSubscriber + public static class ModEvents { + @SubscribeEvent + public static void registerKeyMappings(RegisterKeyMappingsEvent event) { + event.register(KeyBinds.autofish); + event.register(KeyBinds.rodprotect); + event.register(KeyBinds.autoreplace); + event.register(KeyBinds.settings); + event.register(KeyBinds.itemfilter); + } + + @SubscribeEvent + public static void inputKey(InputEvent.Key event) { + AutoFishHandler.onKeyInput(); + } + + @SubscribeEvent + public static void playerTickPre(PlayerTickEvent.Pre event) { + if (!(event.getEntity() instanceof LocalPlayer)) return; + AutoFishHandler.onPlayerTick(event.getEntity()); + } + } +} \ No newline at end of file diff --git a/neoforge/src/main/resources/META-INF/neoforge.mods.toml b/neoforge/src/main/resources/META-INF/neoforge.mods.toml new file mode 100644 index 0000000..fb9fd48 --- /dev/null +++ b/neoforge/src/main/resources/META-INF/neoforge.mods.toml @@ -0,0 +1,32 @@ +modLoader = "javafml" #mandatory +loaderVersion = "${neoforge_loader_version_range}" #mandatory +license = "${license}" # Review your options at https://choosealicense.com/. +#issueTrackerURL="https://change.me.to.your.issue.tracker.example.invalid/" #optional +[[mods]] #mandatory +modId = "${mod_id}" #mandatory +version = "${version}" #mandatory +displayName = "${mod_name}" #mandatory +#updateJSONURL="https://change.me.example.invalid/updates.json" #optional (see https://docs.neoforged.net/docs/misc/updatechecker/) +#displayURL="https://change.me.to.your.mods.homepage.example.invalid/" #optional (displayed in the mod UI) +logoFile="${mod_id}.png" #optional +credits="${credits}" #optional +authors = "${mod_author}" #optional +description = '''${description}''' #mandatory (Supports multiline text) +[[dependencies.${mod_id}]] #optional +modId = "neoforge" #mandatory +type="required" #mandatory (Can be one of "required", "optional", "incompatible" or "discouraged") +versionRange = "[${neoforge_version},)" #mandatory +ordering = "NONE" # The order that this dependency should load in relation to your mod, required to be either 'BEFORE' or 'AFTER' if the dependency is not mandatory +side = "BOTH" # Side this dependency is applied on - 'BOTH', 'CLIENT' or 'SERVER' +[[dependencies.${mod_id}]] +modId = "minecraft" +type="required" #mandatory (Can be one of "required", "optional", "incompatible" or "discouraged") +versionRange = "${minecraft_version_range}" +ordering = "NONE" +side = "BOTH" + +# Features are specific properties of the game environment, that you may want to declare you require. This example declares +# that your mod requires GL version 3.2 or higher. Other features will be added. They are side aware so declaring this won't +# stop your mod loading on the server for example. +#[features.${mod_id}] +#openGLVersion="[3.2,)" diff --git a/settings.gradle b/settings.gradle index 1cba304..c3ea7ca 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,5 +1,51 @@ +pluginManagement { + repositories { + gradlePluginPortal() + mavenCentral() + exclusiveContent { + forRepository { + maven { + name = 'Fabric' + url = uri('https://maven.fabricmc.net') + } + } + filter { + includeGroupAndSubgroups('net.fabricmc') + } + } + exclusiveContent { + forRepository { + maven { + name = 'Sponge' + url = uri('https://repo.spongepowered.org/repository/maven-public') + } + } + filter { + includeGroupAndSubgroups("org.spongepowered") + } + } + exclusiveContent { + forRepository { + maven { + name = 'Forge' + url = uri('https://maven.minecraftforge.net') + } + } + filter { + includeGroupAndSubgroups('net.minecraftforge') + } + } + } +} + plugins { id 'org.gradle.toolchains.foojay-resolver-convention' version '1.0.0' } -rootProject.name = 'forgeautofish' +// This should match the folder name of the project, or else IDEA may complain (see https://youtrack.jetbrains.com/issue/IDEA-317606) +rootProject.name = 'forge-autofish' +includeBuild('build-logic') +include('common') +include('fabric') +include('neoforge') +include('forge') diff --git a/src/main/java/ml/northwestwind/forgeautofish/AutoFish.java b/src/main/java/ml/northwestwind/forgeautofish/AutoFish.java deleted file mode 100644 index d936f8c..0000000 --- a/src/main/java/ml/northwestwind/forgeautofish/AutoFish.java +++ /dev/null @@ -1,39 +0,0 @@ -package ml.northwestwind.forgeautofish; - -import ml.northwestwind.forgeautofish.config.Config; -import ml.northwestwind.forgeautofish.keybind.KeyBinds; -import net.minecraft.network.chat.MutableComponent; -import net.minecraft.network.chat.contents.PlainTextContents; -import net.minecraft.network.chat.contents.TranslatableContents; -import net.minecraftforge.client.event.RegisterKeyMappingsEvent; -import net.minecraftforge.fml.IExtensionPoint; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.config.ModConfig; -import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; -import net.minecraftforge.fml.loading.FMLPaths; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -@Mod(AutoFish.MODID) -public class AutoFish -{ - public static final String MODID = "forgeautofish"; - public static final Logger LOGGER = LogManager.getLogger(); - - public AutoFish(FMLJavaModLoadingContext context) { - context.registerConfig(ModConfig.Type.CLIENT, Config.CLIENT); - - RegisterKeyMappingsEvent.BUS.addListener(KeyBinds::register); - - Config.loadConfig(FMLPaths.CONFIGDIR.get().resolve("forgeautofish-client.toml").toString()); - context.registerExtensionPoint(IExtensionPoint.DisplayTest.class, ()->new IExtensionPoint.DisplayTest(()->"ANY", (remote, isServer)-> true)); - } - - public static MutableComponent getTranslatableComponent(String key, Object... args) { - return MutableComponent.create(new TranslatableContents(key, null, args)); - } - - public static MutableComponent getLiteralComponent(String str) { - return MutableComponent.create(new PlainTextContents.LiteralContents(str)); - } -} diff --git a/src/main/java/ml/northwestwind/forgeautofish/config/Config.java b/src/main/java/ml/northwestwind/forgeautofish/config/Config.java deleted file mode 100644 index 1e21cd9..0000000 --- a/src/main/java/ml/northwestwind/forgeautofish/config/Config.java +++ /dev/null @@ -1,112 +0,0 @@ -package ml.northwestwind.forgeautofish.config; - -import com.electronwill.nightconfig.core.file.CommentedFileConfig; -import com.electronwill.nightconfig.core.io.WritingMode; -import com.google.common.collect.Lists; -import ml.northwestwind.forgeautofish.AutoFish; -import ml.northwestwind.forgeautofish.handler.AutoFishHandler; -import net.minecraftforge.common.ForgeConfigSpec; - -import java.io.File; -import java.util.List; - -public class Config { - - public static final long[] RECAST_DELAY_RANGE = { 20L, 1L, 600L }; - public static final long[] REEL_IN_DELAY_RANGE = { 0L, 0L, 600L }; - public static final long[] THROW_DELAY_RANGE = { 10L, 5L, 600L }; - public static final long[] CHECK_INTERVAL_RANGE = { 200L, 20L, 72000L }; - - private static final ForgeConfigSpec.Builder CLIENT_BUILDER = new ForgeConfigSpec.Builder(); - public static final ForgeConfigSpec CLIENT; - - public static ForgeConfigSpec.LongValue RECAST_DELAY, REEL_IN_DELAY, THROW_DELAY, CHECK_INTERVAL; - public static ForgeConfigSpec.BooleanValue AUTO_FISH, ROD_PROTECT, AUTO_REPLACE, ALL_FILTERS; - public static ForgeConfigSpec.ConfigValue> FILTER, PRIORITIZE; - - static { - init(); - CLIENT = CLIENT_BUILDER.build(); - } - - public static void loadConfig(String path) { - final CommentedFileConfig file = CommentedFileConfig.builder(new File(path)).sync().autosave().writingMode(WritingMode.REPLACE).build(); - file.load(); - CLIENT.setConfig(file); - } - - public static void init() { - RECAST_DELAY = CLIENT_BUILDER.comment("Sets the delay before casting the fishing rod again (in ticks).", "Minimum is 1 tick to allow Auto Replace to take effect.").defineInRange("forgeautofish.recastdelay", RECAST_DELAY_RANGE[0], RECAST_DELAY_RANGE[1], RECAST_DELAY_RANGE[2]); - REEL_IN_DELAY = CLIENT_BUILDER.comment("Sets the delay before reeling in the fishing rod after catching a fish (in ticks).").defineInRange("forgeautofish.reelindelay", REEL_IN_DELAY_RANGE[0], REEL_IN_DELAY_RANGE[1], REEL_IN_DELAY_RANGE[2]); - THROW_DELAY = CLIENT_BUILDER.comment("Sets the delay between each item throw in filtering (in ticks).").defineInRange("forgeautofish.throwdelay", THROW_DELAY_RANGE[0], THROW_DELAY_RANGE[1], THROW_DELAY_RANGE[2]); - CHECK_INTERVAL = CLIENT_BUILDER.comment("Sets the interval for checking if the rod is thrown or stuck (in ticks).", "If not, throw/recast it.").defineInRange("forgeautofish.checkinterval", CHECK_INTERVAL_RANGE[0], CHECK_INTERVAL_RANGE[1], CHECK_INTERVAL_RANGE[2]); - AUTO_FISH = CLIENT_BUILDER.comment("Sets the default status of the Auto Fish feature").define("forgeautofish.autofish", true); - ROD_PROTECT = CLIENT_BUILDER.comment("Sets whether should the mod be turned off when the fishing rod is about to break.").define("forgeautofish.rodprotect", true); - AUTO_REPLACE = CLIENT_BUILDER.comment("Does nothing currently").define("forgeautofish.autoreplace", true); - ALL_FILTERS = CLIENT_BUILDER.comment("Toggles the entire item filter").define("forgeautofish.filter.all", true); - FILTER = CLIENT_BUILDER.comment("Sets item filter").define("forgeautofish.filter.items", Lists.newArrayList("minecraft:rotten_flesh")); - PRIORITIZE = CLIENT_BUILDER.comment("Puts these items to top of filter.").define("forgeautofish.filter.prioritize", Lists.newArrayList("minecraft:cod", "minecraft:salmon", "minecraft:tropical_fish", "minecraft:pufferfish", "minecraft:bow", "minecraft:enchanted_book", "minecraft:fishing_rod", "minecraft:name_tag", "minecraft:nautilus_shell", "minecraft:saddle", "minecraft:lily_pad", "minecraft:bowl", "minecraft:leather", "minecraft:leather_boots", "minecraft:rotten_flesh", "minecraft:stick", "minecraft:string", "minecraft:water_bottle", "minecraft:bone", "minecraft:ink_sac", "minecraft:tripwire_hook", "minecraft:bamboo", "minecraft:cocoa_beans")); - } - - public static void setRecastDelay(long recastDelay) { - AutoFishHandler.recastDelay = recastDelay; - Config.RECAST_DELAY.set(recastDelay); - Config.RECAST_DELAY.save(); - AutoFish.LOGGER.info("Set Recast Delay: " + recastDelay); - } - - public static void setAutoFish(boolean autoFish) { - AutoFishHandler.autofish = autoFish; - Config.AUTO_FISH.set(autoFish); - Config.AUTO_FISH.save(); - AutoFish.LOGGER.info("Toggle AutoFish: " + autoFish); - } - - public static void setRodProtect(boolean rodProtect) { - AutoFishHandler.rodprotect = rodProtect; - Config.ROD_PROTECT.set(rodProtect); - Config.ROD_PROTECT.save(); - AutoFish.LOGGER.info("Toggle Rod Protect: " + rodProtect); - } - - public static void setAutoReplace(boolean autoReplace) { - AutoFishHandler.autoreplace = autoReplace; - Config.AUTO_REPLACE.set(autoReplace); - Config.AUTO_REPLACE.save(); - AutoFish.LOGGER.info("Toggle Auto Replace: " + autoReplace); - } - - public static void enableFilter(boolean filter) { - AutoFishHandler.itemfilter = filter; - ALL_FILTERS.set(filter); - ALL_FILTERS.save(); - AutoFish.LOGGER.info("Toggle Filter: " + filter); - } - - public static void setFILTER(List list) { - Config.FILTER.set(list); - Config.FILTER.save(); - AutoFish.LOGGER.info("Received new Filter"); - } - - public static void setReelInDelay(long reelInDelay) { - AutoFishHandler.reelInDelay = reelInDelay; - Config.REEL_IN_DELAY.set(reelInDelay); - Config.REEL_IN_DELAY.save(); - AutoFish.LOGGER.info("Set Reel In Delay: " + reelInDelay); - } - - public static void setThrowDelay(long throwDelay) { - AutoFishHandler.throwDelay = throwDelay; - Config.THROW_DELAY.set(throwDelay); - Config.THROW_DELAY.save(); - AutoFish.LOGGER.info("Set Throw Delay: " + throwDelay); - } - - public static void setCheckInterval(long checkInterval) { - AutoFishHandler.checkInterval = checkInterval; - Config.CHECK_INTERVAL.set(checkInterval); - Config.CHECK_INTERVAL.save(); - AutoFish.LOGGER.info("Set Check Interval: " + checkInterval); - } -} diff --git a/src/main/resources/META-INF/mods.toml b/src/main/resources/META-INF/mods.toml deleted file mode 100644 index afa2774..0000000 --- a/src/main/resources/META-INF/mods.toml +++ /dev/null @@ -1,35 +0,0 @@ -modLoader="javafml" -loaderVersion="[65,)" -issueTrackerURL="https://github.com/North-West-Wind/forge-autofish/issues" -license="GPL v3" - -[[mods]] -modId="forgeautofish" -version="7.1.0" -displayName="AutoFish for Forge" -updateJSONURL="https://raw.githubusercontent.com/North-West-Wind/forge-autofish/master/update.json" -displayURL="https://github.com/North-West-Wind/forge-autofish/" -logoFile="forgeautofish.png" -credits="Thank you for using this mod" -authors="NorthWestWind" -description=''' -I like playing survival, but fishing is a boring activity... -Therefore, I made this mod! -Now you can AFK Fish like no one else! - -Note that this is my first mod, so there might be bugs. -''' - -[[dependencies.forgeautofish]] - modId="forge" - mandatory=true - versionRange="[65,)" - ordering="NONE" - side="BOTH" - -[[dependencies.forgeautofish]] - modId="minecraft" - mandatory=true - versionRange="[26.2,26.3)" - ordering="NONE" - side="BOTH" From 408d4ba63aaaa6a640f9523edd9917835c104469 Mon Sep 17 00:00:00 2001 From: North-West-Wind Date: Mon, 29 Jun 2026 14:32:01 +0800 Subject: [PATCH 08/52] refactor: stonecutter --- .gitignore | 3 + build-logic/build.gradle | 3 - .../src/main/groovy/multiloader-loader.gradle | 45 ------------ build.gradle | 6 -- common/build.gradle | 59 ---------------- fabric/build.gradle | 36 +++------- forge/build.gradle | 65 ++++++------------ gradle.properties | 3 + .../shared.gradle | 33 ++++++--- neoforge/build.gradle | 51 +++++--------- settings.gradle | 40 +++++++++-- .../northwestw/autofish/AutoFishFabric.java | 0 .../fabric}/resources/fabric.mod.json | 0 .../in/northwestw/autofish/AutoFishForge.java | 0 .../forge}/resources/META-INF/mods.toml | 0 .../java/in/northwestw/autofish/AutoFish.java | 0 .../in/northwestw/autofish/config/Config.java | 0 .../config/gui/CheckIntervalScreen.java | 0 .../config/gui/FilterSelectionScreen.java | 0 .../config/gui/RecastDelayScreen.java | 0 .../config/gui/ReelInDelayScreen.java | 0 .../autofish/config/gui/SettingsScreen.java | 0 .../config/gui/SuperFilterScreen.java | 0 .../autofish/config/gui/ThrowDelayScreen.java | 0 .../autofish/handler/AutoFishHandler.java | 4 +- .../northwestw/autofish/keybind/KeyBinds.java | 0 .../assets/forgeautofish/lang/en_us.json | 0 .../assets/forgeautofish/lang/zh_tw.json | 0 .../src => src}/main/resources/autofish.png | Bin .../src => src}/main/resources/pack.mcmeta | 0 .../northwestw/autofish/AutoFishNeoForge.java | 0 .../resources/META-INF/neoforge.mods.toml | 0 stonecutter.gradle | 4 ++ 33 files changed, 119 insertions(+), 233 deletions(-) delete mode 100644 build-logic/build.gradle delete mode 100644 build-logic/src/main/groovy/multiloader-loader.gradle delete mode 100644 common/build.gradle rename build-logic/src/main/groovy/multiloader-common.gradle => gradle/shared.gradle (73%) rename {fabric/src/main => src/fabric}/java/in/northwestw/autofish/AutoFishFabric.java (100%) rename {fabric/src/main => src/fabric}/resources/fabric.mod.json (100%) rename {forge/src/main => src/forge}/java/in/northwestw/autofish/AutoFishForge.java (100%) rename {forge/src/main => src/forge}/resources/META-INF/mods.toml (100%) rename {common/src => src}/main/java/in/northwestw/autofish/AutoFish.java (100%) rename {common/src => src}/main/java/in/northwestw/autofish/config/Config.java (100%) rename {common/src => src}/main/java/in/northwestw/autofish/config/gui/CheckIntervalScreen.java (100%) rename {common/src => src}/main/java/in/northwestw/autofish/config/gui/FilterSelectionScreen.java (100%) rename {common/src => src}/main/java/in/northwestw/autofish/config/gui/RecastDelayScreen.java (100%) rename {common/src => src}/main/java/in/northwestw/autofish/config/gui/ReelInDelayScreen.java (100%) rename {common/src => src}/main/java/in/northwestw/autofish/config/gui/SettingsScreen.java (100%) rename {common/src => src}/main/java/in/northwestw/autofish/config/gui/SuperFilterScreen.java (100%) rename {common/src => src}/main/java/in/northwestw/autofish/config/gui/ThrowDelayScreen.java (100%) rename {common/src => src}/main/java/in/northwestw/autofish/handler/AutoFishHandler.java (97%) rename {common/src => src}/main/java/in/northwestw/autofish/keybind/KeyBinds.java (100%) rename {common/src => src}/main/resources/assets/forgeautofish/lang/en_us.json (100%) rename {common/src => src}/main/resources/assets/forgeautofish/lang/zh_tw.json (100%) rename {common/src => src}/main/resources/autofish.png (100%) rename {common/src => src}/main/resources/pack.mcmeta (100%) rename {neoforge/src/main => src/neoforge}/java/in/northwestw/autofish/AutoFishNeoForge.java (100%) rename {neoforge/src/main => src/neoforge}/resources/META-INF/neoforge.mods.toml (100%) create mode 100644 stonecutter.gradle diff --git a/.gitignore b/.gitignore index 461017f..3d14050 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,6 @@ build eclipse run runs + +# stonecutter version build dirs +*/versions/*/build/ diff --git a/build-logic/build.gradle b/build-logic/build.gradle deleted file mode 100644 index 6784052..0000000 --- a/build-logic/build.gradle +++ /dev/null @@ -1,3 +0,0 @@ -plugins { - id 'groovy-gradle-plugin' -} diff --git a/build-logic/src/main/groovy/multiloader-loader.gradle b/build-logic/src/main/groovy/multiloader-loader.gradle deleted file mode 100644 index 1adfaee..0000000 --- a/build-logic/src/main/groovy/multiloader-loader.gradle +++ /dev/null @@ -1,45 +0,0 @@ -plugins { - id 'multiloader-common' -} - -configurations { - commonJava { - canBeResolved = true - } - commonResources { - canBeResolved = true - } -} - -dependencies { - compileOnly(project(':common')) { - def loaderAttribute = Attribute.of('io.github.mcgradleconventions.loader', String) - attributes { - attribute(loaderAttribute, 'common') - } - } - commonJava(project(path: ':common', configuration: 'commonJava')) - commonResources(project(path: ':common', configuration: 'commonResources')) -} - -tasks.named('compileJava', JavaCompile) { - dependsOn(configurations.commonJava) - source(configurations.commonJava) -} - -processResources { - dependsOn(configurations.commonResources) - from(configurations.commonResources) -} - -tasks.named('javadoc', Javadoc).configure { - dependsOn(configurations.commonJava) - source(configurations.commonJava) -} - -tasks.named('sourcesJar', Jar) { - dependsOn(configurations.commonJava) - from(configurations.commonJava) - dependsOn(configurations.commonResources) - from(configurations.commonResources) -} diff --git a/build.gradle b/build.gradle index f23072f..e69de29 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +0,0 @@ -plugins { - // see https://maven.fabricmc.net/fabric-loom/fabric-loom.gradle.plugin/maven-metadata.xml for new versions - id 'net.fabricmc.fabric-loom' version '1.16.3' apply false - // see https://projects.neoforged.net/neoforged/moddevgradle for new versions - id 'net.neoforged.moddev' version '2.0.141' apply false -} \ No newline at end of file diff --git a/common/build.gradle b/common/build.gradle deleted file mode 100644 index e702da1..0000000 --- a/common/build.gradle +++ /dev/null @@ -1,59 +0,0 @@ -plugins { - id 'multiloader-common' - id 'net.neoforged.moddev' -} - -neoForge { - neoFormVersion = neo_form_version - // Automatically enable AccessTransformers if the file exists - def at = file('src/main/resources/META-INF/accesstransformer.cfg') - if (at.exists()) { - accessTransformers.from(at.absolutePath) - } -} - -dependencies { - // Fabric and NeoForge both bundle Fabric Mixin, so it is safe to use it in common - // If you need to update, check what version they are using to see what is compatible - // https://github.com/neoforged/NeoForge/blob/26.2.x/gradle.properties#L37 - // https://github.com/FabricMC/fabric-loader/blob/master/gradle.properties#L12 - compileOnly('net.fabricmc:sponge-mixin:0.17.3+mixin.0.8.7') - // Fabric and NeoForge both bundle MixinExtras, so it is safe to use it in common - compileOnly(annotationProcessor('io.github.llamalad7:mixinextras-common:0.5.3')) - -} - -configurations { - commonJava { - canBeResolved = false - canBeConsumed = true - } - commonResources { - canBeResolved = false - canBeConsumed = true - } -} - -artifacts { - commonJava sourceSets.main.java.sourceDirectories.singleFile - commonResources sourceSets.main.resources.sourceDirectories.singleFile -} - -// Implement mcgradleconventions loader attribute -def loaderAttribute = Attribute.of('io.github.mcgradleconventions.loader', String) -['apiElements', 'runtimeElements', 'sourcesElements', 'javadocElements'].each { variant -> - configurations.named(variant) { - attributes { - attribute(loaderAttribute, 'common') - } - } -} -sourceSets.configureEach { - [it.compileClasspathConfigurationName, it.runtimeClasspathConfigurationName].each { variant-> - configurations.named(variant) { - attributes { - attribute(loaderAttribute, 'common') - } - } - } -} diff --git a/fabric/build.gradle b/fabric/build.gradle index 8c3ffb5..65c36b5 100644 --- a/fabric/build.gradle +++ b/fabric/build.gradle @@ -1,15 +1,16 @@ plugins { - id 'multiloader-loader' - id 'net.fabricmc.fabric-loom' -} -dependencies { - minecraft("com.mojang:minecraft:${minecraft_version}") - implementation("net.fabricmc:fabric-loader:${fabric_loader_version}") - implementation("net.fabricmc.fabric-api:fabric-api:${fabric_version}") + id 'java-library' + id 'maven-publish' + id 'net.fabricmc.fabric-loom' version '1.16.3' } +ext.loaderName = "fabric" +ext.mcVersionName = "26.2" + +apply from: rootProject.file('gradle/shared.gradle') + loom { - def aw = project(':common').file("src/main/resources/${mod_id}.accesswidener") + def aw = rootProject.file("src/${loaderName}/resources/${mod_id}.accesswidener") if (aw.exists()) { accessWidenerPath.set(aw) } @@ -28,22 +29,3 @@ loom { } } } - -// Implement mcgradleconventions loader attribute -def loaderAttribute = Attribute.of('io.github.mcgradleconventions.loader', String) -['apiElements', 'runtimeElements', 'sourcesElements', 'javadocElements', 'includeInternal', 'modCompileClasspath'].each { variant -> - configurations.named(variant) { - attributes { - attribute(loaderAttribute, 'fabric') - } - } -} -sourceSets.configureEach { - [it.compileClasspathConfigurationName, it.runtimeClasspathConfigurationName].each { variant-> - configurations.named(variant) { - attributes { - attribute(loaderAttribute, 'fabric') - } - } - } -} diff --git a/forge/build.gradle b/forge/build.gradle index 47fbfff..fb67ab6 100644 --- a/forge/build.gradle +++ b/forge/build.gradle @@ -1,81 +1,58 @@ plugins { - id 'multiloader-loader' + id 'java-library' + id 'maven-publish' id 'net.minecraftforge.gradle' version '[7.0.17,8)' id 'idea' } -base { - archivesName = "${mod_id}-forge-${minecraft_version}" -} + +ext.loaderName = "forge" +ext.mcVersionName = "26.2" + +apply from: rootProject.file('gradle/shared.gradle') minecraft { mappings channel: 'official', version: minecraft_version - // Automatically enable forge AccessTransformers if the file exists - // This location is hardcoded in Forge and can not be changed. - // https://github.com/MinecraftForge/MinecraftForge/blob/be1698bb1554f9c8fa2f58e32b9ab70bc4385e60/fmlloader/src/main/java/net/minecraftforge/fml/loading/moddiscovery/ModFile.java#L123 - // Forge still uses SRG names during compile time, so we cannot use the common AT's - def at = file('src/main/resources/META-INF/accesstransformer.cfg') + def at = rootProject.file("src/${loaderName}/resources/META-INF/accesstransformer.cfg") if (at.exists()) { accessTransformer = at } runs { configureEach { - workingDir = layout.projectDirectory.dir('run') - systemProperty 'eventbus.api.strictRuntimeChecks', 'true' systemProperty 'forge.enabledGameTestNamespaces', mod_id } - register('client') + register('client') { + workingDir = rootProject.file('runs/client') + } register('server') { + workingDir = rootProject.file('runs/server') args '--nogui' } - register('gameTestServer') + register('gameTestServer') { + workingDir = rootProject.file('runs/gameTestServer') + } register('data') { - workingDir = layout.projectDirectory.dir('run-data') + workingDir = rootProject.file('runs/data') - args '--mod', mod_id, '--all', '--output', layout.projectDirectory.dir('src/generated/resources'), '--existing', layout.projectDirectory.dir('src/main/resources') + args '--mod', mod_id, '--all', '--output', rootProject.file("src/${loaderName}/generated/resources"), '--existing', rootProject.file("src/${loaderName}/resources") } } } -sourceSets.main.resources.srcDir 'src/generated/resources' - repositories { - minecraft.mavenizer(it) // In Kotlin, it = this + minecraft.mavenizer(it) maven fg.forgeMaven maven fg.minecraftLibsMaven } -dependencies { - implementation minecraft.dependency("net.minecraftforge:forge:${minecraft_version}-${forge_version}") -} +sourceSets.main.resources.srcDir rootProject.file("src/${loaderName}/generated/resources") -sourceSets.each { - def dir = layout.buildDirectory.dir("sourcesSets/$it.name") - it.output.resourcesDir = dir - it.java.destinationDirectory = dir -} - -// Implement mcgradleconventions loader attribute -def loaderAttribute = Attribute.of('io.github.mcgradleconventions.loader', String) -['apiElements', 'runtimeElements', 'sourcesElements', 'javadocElements'].each { variant -> - configurations.named("$variant") { - attributes { - attribute(loaderAttribute, 'forge') - } - } -} -sourceSets.configureEach { - [it.compileClasspathConfigurationName, it.runtimeClasspathConfigurationName].each { variant-> - configurations.named("$variant") { - attributes { - attribute(loaderAttribute, 'forge') - } - } - } +dependencies { + implementation(minecraft.dependency("net.minecraftforge:forge:${minecraft_version}-${forge_version}")) } diff --git a/gradle.properties b/gradle.properties index acf137b..79c1eba 100644 --- a/gradle.properties +++ b/gradle.properties @@ -34,3 +34,6 @@ neoforge_loader_version_range=[4,) # Gradle org.gradle.jvmargs=-Xmx3G org.gradle.daemon=false + +# Enable Stonecutter Groovy support +dev.kikugie.stonecutter.hard_mode=true diff --git a/build-logic/src/main/groovy/multiloader-common.gradle b/gradle/shared.gradle similarity index 73% rename from build-logic/src/main/groovy/multiloader-common.gradle rename to gradle/shared.gradle index f9a55cf..1cfc02d 100644 --- a/build-logic/src/main/groovy/multiloader-common.gradle +++ b/gradle/shared.gradle @@ -1,10 +1,5 @@ -plugins { - id 'java-library' - id 'maven-publish' -} - base { - archivesName = "${mod_id}-${project.name}-${minecraft_version}" + archivesName = "${mod_id}-${loaderName}-${mcVersionName}" } java { @@ -13,6 +8,19 @@ java { withJavadocJar() } +sourceSets { + main { + java { + srcDir rootProject.file("src/main/java") + srcDir rootProject.file("src/${loaderName}/java") + } + resources { + srcDir rootProject.file("src/main/resources") + srcDir rootProject.file("src/${loaderName}/resources") + } + } +} + tasks.withType(Jar).configureEach { from(rootProject.file('LICENSE')) { rename { "${it}_${mod_name}" } @@ -20,7 +28,6 @@ tasks.withType(Jar).configureEach { } jar { - manifest { attributes([ 'Specification-Title' : mod_name, @@ -34,10 +41,20 @@ jar { } } +dependencies { + compileOnly('net.fabricmc:sponge-mixin:0.17.3+mixin.0.8.7') + compileOnly(annotationProcessor('io.github.llamalad7:mixinextras-common:0.5.3')) + if (loaderName == "fabric") { + minecraft("com.mojang:minecraft:${minecraft_version}") + implementation("net.fabricmc:fabric-loader:${fabric_loader_version}") + implementation("net.fabricmc.fabric-api:fabric-api:${fabric_version}") + } +} + processResources { var expandProps = [ 'version' : version, - 'group' : project.group, //Else we target the task's group. + 'group' : project.group, 'minecraft_version' : minecraft_version, 'minecraft_version_range' : minecraft_version_range, 'fabric_version' : fabric_version, diff --git a/neoforge/build.gradle b/neoforge/build.gradle index 055d5fd..54c5292 100644 --- a/neoforge/build.gradle +++ b/neoforge/build.gradle @@ -1,37 +1,43 @@ plugins { - id 'multiloader-loader' - id 'net.neoforged.moddev' + id 'java-library' + id 'maven-publish' + id 'net.neoforged.moddev' version '2.0.141' } +ext.loaderName = "neoforge" +ext.mcVersionName = "26.2" + +apply from: rootProject.file('gradle/shared.gradle') + neoForge { version = neoforge_version - // Automatically enable neoforge AccessTransformers if the file exists - def at = project(':common').file('src/main/resources/META-INF/accesstransformer.cfg') + + def at = rootProject.file("src/${loaderName}/resources/META-INF/accesstransformer.cfg") if (at.exists()) { accessTransformers.from(at.absolutePath) } + runs { configureEach { systemProperty('neoforge.enabledGameTestNamespaces', mod_id) - ideName = "NeoForge ${it.name.capitalize()} (${project.path})" // Unify the run config names with fabric + ideName = "NeoForge ${it.name.capitalize()} (${project.path})" } client { client() - gameDirectory = this.mkdir(this.file('runs/client')) + gameDirectory = rootProject.file('runs/client') } data { clientData() - gameDirectory = this.mkdir(this.file('runs/data')) - // DataGen can be run by - "./gradlew :neoforge:runData" in Terminal. - // Specify the modid for data generation, where to output the resulting resource, and where to look for existing resources. - programArguments.addAll '--mod', project.mod_id, '--all', '--output', file('src/generated/resources/').getAbsolutePath(), '--existing', file('src/main/resources/').getAbsolutePath() + gameDirectory = rootProject.file('runs/data') + programArguments.addAll '--mod', project.mod_id, '--all', '--output', rootProject.file("src/${loaderName}/generated/resources").getAbsolutePath(), '--existing', rootProject.file("src/${loaderName}/resources").getAbsolutePath() } server { server() - this.file('runs/server').createParentDirectories() - gameDirectory = this.mkdir(this.file('runs/server')) + rootProject.file('runs/server').mkdirs() + gameDirectory = rootProject.file('runs/server') } } + mods { "${mod_id}" { sourceSet sourceSets.main @@ -39,23 +45,4 @@ neoForge { } } -sourceSets.main.resources { srcDir 'src/generated/resources' } - -// Implement mcgradleconventions loader attribute -def loaderAttribute = Attribute.of('io.github.mcgradleconventions.loader', String) -['apiElements', 'runtimeElements', 'sourcesElements', 'javadocElements'].each { variant -> - configurations.named(variant) { - attributes { - attribute(loaderAttribute, 'neoforge') - } - } -} -sourceSets.configureEach { - [it.compileClasspathConfigurationName, it.runtimeClasspathConfigurationName, it.getTaskName(null, 'jarJar')].each { variant -> - configurations.named(variant) { - attributes { - attribute(loaderAttribute, 'neoforge') - } - } - } -} +sourceSets.main.resources.srcDir rootProject.file("src/${loaderName}/generated/resources") diff --git a/settings.gradle b/settings.gradle index c3ea7ca..080634d 100644 --- a/settings.gradle +++ b/settings.gradle @@ -35,17 +35,45 @@ pluginManagement { includeGroupAndSubgroups('net.minecraftforge') } } + exclusiveContent { + forRepository { + maven { + name = 'NeoForge' + url = uri('https://maven.neoforged.net/releases') + } + } + filter { + includeGroupAndSubgroups('net.neoforged') + } + } + maven { + name = 'Stonecutter' + url = uri('https://maven.kikugie.dev/releases') + } } } plugins { id 'org.gradle.toolchains.foojay-resolver-convention' version '1.0.0' + id 'dev.kikugie.stonecutter' version '0.9.6' } -// This should match the folder name of the project, or else IDEA may complain (see https://youtrack.jetbrains.com/issue/IDEA-317606) rootProject.name = 'forge-autofish' -includeBuild('build-logic') -include('common') -include('fabric') -include('neoforge') -include('forge') + +stonecutter { + create(rootProject) { + kotlinController = false + centralScript = "build.gradle" + vcsVersion = "26.2" + + branch("fabric") { + version("26.2") + } + branch("forge") { + version("26.2") + } + branch("neoforge") { + version("26.2") + } + } +} diff --git a/fabric/src/main/java/in/northwestw/autofish/AutoFishFabric.java b/src/fabric/java/in/northwestw/autofish/AutoFishFabric.java similarity index 100% rename from fabric/src/main/java/in/northwestw/autofish/AutoFishFabric.java rename to src/fabric/java/in/northwestw/autofish/AutoFishFabric.java diff --git a/fabric/src/main/resources/fabric.mod.json b/src/fabric/resources/fabric.mod.json similarity index 100% rename from fabric/src/main/resources/fabric.mod.json rename to src/fabric/resources/fabric.mod.json diff --git a/forge/src/main/java/in/northwestw/autofish/AutoFishForge.java b/src/forge/java/in/northwestw/autofish/AutoFishForge.java similarity index 100% rename from forge/src/main/java/in/northwestw/autofish/AutoFishForge.java rename to src/forge/java/in/northwestw/autofish/AutoFishForge.java diff --git a/forge/src/main/resources/META-INF/mods.toml b/src/forge/resources/META-INF/mods.toml similarity index 100% rename from forge/src/main/resources/META-INF/mods.toml rename to src/forge/resources/META-INF/mods.toml diff --git a/common/src/main/java/in/northwestw/autofish/AutoFish.java b/src/main/java/in/northwestw/autofish/AutoFish.java similarity index 100% rename from common/src/main/java/in/northwestw/autofish/AutoFish.java rename to src/main/java/in/northwestw/autofish/AutoFish.java diff --git a/common/src/main/java/in/northwestw/autofish/config/Config.java b/src/main/java/in/northwestw/autofish/config/Config.java similarity index 100% rename from common/src/main/java/in/northwestw/autofish/config/Config.java rename to src/main/java/in/northwestw/autofish/config/Config.java diff --git a/common/src/main/java/in/northwestw/autofish/config/gui/CheckIntervalScreen.java b/src/main/java/in/northwestw/autofish/config/gui/CheckIntervalScreen.java similarity index 100% rename from common/src/main/java/in/northwestw/autofish/config/gui/CheckIntervalScreen.java rename to src/main/java/in/northwestw/autofish/config/gui/CheckIntervalScreen.java diff --git a/common/src/main/java/in/northwestw/autofish/config/gui/FilterSelectionScreen.java b/src/main/java/in/northwestw/autofish/config/gui/FilterSelectionScreen.java similarity index 100% rename from common/src/main/java/in/northwestw/autofish/config/gui/FilterSelectionScreen.java rename to src/main/java/in/northwestw/autofish/config/gui/FilterSelectionScreen.java diff --git a/common/src/main/java/in/northwestw/autofish/config/gui/RecastDelayScreen.java b/src/main/java/in/northwestw/autofish/config/gui/RecastDelayScreen.java similarity index 100% rename from common/src/main/java/in/northwestw/autofish/config/gui/RecastDelayScreen.java rename to src/main/java/in/northwestw/autofish/config/gui/RecastDelayScreen.java diff --git a/common/src/main/java/in/northwestw/autofish/config/gui/ReelInDelayScreen.java b/src/main/java/in/northwestw/autofish/config/gui/ReelInDelayScreen.java similarity index 100% rename from common/src/main/java/in/northwestw/autofish/config/gui/ReelInDelayScreen.java rename to src/main/java/in/northwestw/autofish/config/gui/ReelInDelayScreen.java diff --git a/common/src/main/java/in/northwestw/autofish/config/gui/SettingsScreen.java b/src/main/java/in/northwestw/autofish/config/gui/SettingsScreen.java similarity index 100% rename from common/src/main/java/in/northwestw/autofish/config/gui/SettingsScreen.java rename to src/main/java/in/northwestw/autofish/config/gui/SettingsScreen.java diff --git a/common/src/main/java/in/northwestw/autofish/config/gui/SuperFilterScreen.java b/src/main/java/in/northwestw/autofish/config/gui/SuperFilterScreen.java similarity index 100% rename from common/src/main/java/in/northwestw/autofish/config/gui/SuperFilterScreen.java rename to src/main/java/in/northwestw/autofish/config/gui/SuperFilterScreen.java diff --git a/common/src/main/java/in/northwestw/autofish/config/gui/ThrowDelayScreen.java b/src/main/java/in/northwestw/autofish/config/gui/ThrowDelayScreen.java similarity index 100% rename from common/src/main/java/in/northwestw/autofish/config/gui/ThrowDelayScreen.java rename to src/main/java/in/northwestw/autofish/config/gui/ThrowDelayScreen.java diff --git a/common/src/main/java/in/northwestw/autofish/handler/AutoFishHandler.java b/src/main/java/in/northwestw/autofish/handler/AutoFishHandler.java similarity index 97% rename from common/src/main/java/in/northwestw/autofish/handler/AutoFishHandler.java rename to src/main/java/in/northwestw/autofish/handler/AutoFishHandler.java index ff61b04..97037a5 100644 --- a/common/src/main/java/in/northwestw/autofish/handler/AutoFishHandler.java +++ b/src/main/java/in/northwestw/autofish/handler/AutoFishHandler.java @@ -37,7 +37,7 @@ public static void onKeyInput() { LocalPlayer player = minecraft.player; if (KeyBinds.autofish.consumeClick()) { Config.setAutoFish(!Config.autoFish); - if (player != null) player.sendOverlayMessage(getText("forgeautofish", Config.autoFish)); + if (player != null) player.sendOverlayMessage(getText("autofish", Config.autoFish)); } else if (KeyBinds.rodprotect.consumeClick()) { Config.setRodProtect(!Config.rodProtect); if (player != null) player.sendOverlayMessage(getText("rodprotect", Config.rodProtect)); @@ -65,7 +65,6 @@ public static void onPlayerTick(final Player player) { if (afterDrop) { if (tick == 0 && rodSlot != -1) { player.getInventory().setSelectedSlot(rodSlot); - AutoFish.LOGGER.info("Swapped to hotbar slot {} for rod", rodSlot); rodSlot = -1; } tick++; @@ -197,7 +196,6 @@ private static void dropItem(Player player) { for (int ii = 0; ii < 9; ii++) { if (!player.getInventory().getItem(ii).getItem().equals(item)) continue; player.getInventory().setSelectedSlot(ii); - AutoFish.LOGGER.info("Swapped to hotbar slot {} for item", ii); dropCd = 20; return; } diff --git a/common/src/main/java/in/northwestw/autofish/keybind/KeyBinds.java b/src/main/java/in/northwestw/autofish/keybind/KeyBinds.java similarity index 100% rename from common/src/main/java/in/northwestw/autofish/keybind/KeyBinds.java rename to src/main/java/in/northwestw/autofish/keybind/KeyBinds.java diff --git a/common/src/main/resources/assets/forgeautofish/lang/en_us.json b/src/main/resources/assets/forgeautofish/lang/en_us.json similarity index 100% rename from common/src/main/resources/assets/forgeautofish/lang/en_us.json rename to src/main/resources/assets/forgeautofish/lang/en_us.json diff --git a/common/src/main/resources/assets/forgeautofish/lang/zh_tw.json b/src/main/resources/assets/forgeautofish/lang/zh_tw.json similarity index 100% rename from common/src/main/resources/assets/forgeautofish/lang/zh_tw.json rename to src/main/resources/assets/forgeautofish/lang/zh_tw.json diff --git a/common/src/main/resources/autofish.png b/src/main/resources/autofish.png similarity index 100% rename from common/src/main/resources/autofish.png rename to src/main/resources/autofish.png diff --git a/common/src/main/resources/pack.mcmeta b/src/main/resources/pack.mcmeta similarity index 100% rename from common/src/main/resources/pack.mcmeta rename to src/main/resources/pack.mcmeta diff --git a/neoforge/src/main/java/in/northwestw/autofish/AutoFishNeoForge.java b/src/neoforge/java/in/northwestw/autofish/AutoFishNeoForge.java similarity index 100% rename from neoforge/src/main/java/in/northwestw/autofish/AutoFishNeoForge.java rename to src/neoforge/java/in/northwestw/autofish/AutoFishNeoForge.java diff --git a/neoforge/src/main/resources/META-INF/neoforge.mods.toml b/src/neoforge/resources/META-INF/neoforge.mods.toml similarity index 100% rename from neoforge/src/main/resources/META-INF/neoforge.mods.toml rename to src/neoforge/resources/META-INF/neoforge.mods.toml diff --git a/stonecutter.gradle b/stonecutter.gradle new file mode 100644 index 0000000..1713427 --- /dev/null +++ b/stonecutter.gradle @@ -0,0 +1,4 @@ +plugins { + id "dev.kikugie.stonecutter" +} +stonecutter.active "26.2" \ No newline at end of file From c1c90822353dfa70361643f721d89acfc5d857d0 Mon Sep 17 00:00:00 2001 From: North-West-Wind Date: Mon, 29 Jun 2026 17:26:48 +0800 Subject: [PATCH 09/52] feat: support 26.1.2 & 1.21.11 --- fabric-o/build.gradle | 30 +++++++ fabric/build.gradle | 1 - forge/build.gradle | 2 - gradle.properties | 23 +++-- gradle/shared.gradle | 16 +++- neoforge/build.gradle | 1 - settings.gradle | 42 ++++++--- .../in/northwestw/autofish/config/Config.java | 16 ++-- .../config/gui/CheckIntervalScreen.java | 87 ------------------- .../config/gui/FilterSelectionScreen.java | 25 +++++- ...elayScreen.java => LongSettingScreen.java} | 50 +++++++---- .../config/gui/RecastDelayScreen.java | 87 ------------------- .../config/gui/ReelInDelayScreen.java | 87 ------------------- .../autofish/config/gui/SettingsScreen.java | 26 ++++-- .../config/gui/SuperFilterScreen.java | 22 ++++- .../autofish/handler/AutoFishHandler.java | 21 +++-- versions/1.21.11/gradle.properties | 22 +++++ versions/26.1.2/gradle.properties | 19 ++++ 18 files changed, 240 insertions(+), 337 deletions(-) create mode 100644 fabric-o/build.gradle delete mode 100644 src/main/java/in/northwestw/autofish/config/gui/CheckIntervalScreen.java rename src/main/java/in/northwestw/autofish/config/gui/{ThrowDelayScreen.java => LongSettingScreen.java} (53%) delete mode 100644 src/main/java/in/northwestw/autofish/config/gui/RecastDelayScreen.java delete mode 100644 src/main/java/in/northwestw/autofish/config/gui/ReelInDelayScreen.java create mode 100644 versions/1.21.11/gradle.properties create mode 100644 versions/26.1.2/gradle.properties diff --git a/fabric-o/build.gradle b/fabric-o/build.gradle new file mode 100644 index 0000000..485d5c9 --- /dev/null +++ b/fabric-o/build.gradle @@ -0,0 +1,30 @@ +plugins { + id 'java-library' + id 'maven-publish' + id 'fabric-loom' version '1.13-SNAPSHOT' +} + +ext.loaderName = "fabric-o" + +apply from: rootProject.file('gradle/shared.gradle') + +loom { + def aw = rootProject.file("src/fabric/resources/${mod_id}.accesswidener") + if (aw.exists()) { + accessWidenerPath.set(aw) + } + runs { + client { + client() + setConfigName('Fabric-o Client') + ideConfigGenerated(true) + runDir('runs/client') + } + server { + server() + setConfigName('Fabric-o Server') + ideConfigGenerated(true) + runDir('runs/server') + } + } +} diff --git a/fabric/build.gradle b/fabric/build.gradle index 65c36b5..4519dfc 100644 --- a/fabric/build.gradle +++ b/fabric/build.gradle @@ -5,7 +5,6 @@ plugins { } ext.loaderName = "fabric" -ext.mcVersionName = "26.2" apply from: rootProject.file('gradle/shared.gradle') diff --git a/forge/build.gradle b/forge/build.gradle index fb67ab6..03cc798 100644 --- a/forge/build.gradle +++ b/forge/build.gradle @@ -6,7 +6,6 @@ plugins { } ext.loaderName = "forge" -ext.mcVersionName = "26.2" apply from: rootProject.file('gradle/shared.gradle') @@ -39,7 +38,6 @@ minecraft { register('data') { workingDir = rootProject.file('runs/data') - args '--mod', mod_id, '--all', '--output', rootProject.file("src/${loaderName}/generated/resources"), '--existing', rootProject.file("src/${loaderName}/resources") } } diff --git a/gradle.properties b/gradle.properties index 79c1eba..fcf5ebc 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,19 +1,25 @@ -# Important Notes: -# Every field you add must be added to buildSrc/src/main/groovy/multiloader-common.gradle expandProps map. - # Project version=8.0.0 group=in.northwestw.in -java_version=25 # Common -minecraft_version=26.2 mod_name=AutoFish for Everyone mod_author=NorthWestWind mod_id=autofish license=GPLv3 credits= description=I like playing survival, but fishing is a boring activity...\nTherefore, I made this mod!\nNow you can AFK Fish like no one else!\n\nNote that this is my first mod, so there might be bugs. + +# Gradle +org.gradle.jvmargs=-Xmx3G +org.gradle.daemon=false + +# Stonecutter +dev.kikugie.stonecutter.hard_mode=true + +# Minecraft and loader versions +java_version=25 +minecraft_version=26.2 minecraft_version_range=[26.2, 26.3) ## This is the version of minecraft that the 'common' project uses, you can find a list of all versions here ## https://projects.neoforged.net/neoforged/neoform @@ -30,10 +36,3 @@ forge_loader_version_range=[65,) # NeoForge, see https://projects.neoforged.net/neoforged/neoforge for new versions neoforge_version=26.2.0.1-beta neoforge_loader_version_range=[4,) - -# Gradle -org.gradle.jvmargs=-Xmx3G -org.gradle.daemon=false - -# Enable Stonecutter Groovy support -dev.kikugie.stonecutter.hard_mode=true diff --git a/gradle/shared.gradle b/gradle/shared.gradle index 1cfc02d..ae2b2bc 100644 --- a/gradle/shared.gradle +++ b/gradle/shared.gradle @@ -1,5 +1,12 @@ +def versionPropsFile = rootProject.file("versions/${sc.current.version}/gradle.properties") +if (versionPropsFile.exists()) { + def props = new Properties() + versionPropsFile.withInputStream { props.load(it) } + props.each { key, value -> project.ext."${key}" = value } +} + base { - archivesName = "${mod_id}-${loaderName}-${mcVersionName}" + archivesName = "${mod_id}-${loaderName}-${minecraft_version}" } java { @@ -44,11 +51,16 @@ jar { dependencies { compileOnly('net.fabricmc:sponge-mixin:0.17.3+mixin.0.8.7') compileOnly(annotationProcessor('io.github.llamalad7:mixinextras-common:0.5.3')) - if (loaderName == "fabric") { + if (loaderName == "fabric" || loaderName == "fabric-o") { minecraft("com.mojang:minecraft:${minecraft_version}") implementation("net.fabricmc:fabric-loader:${fabric_loader_version}") implementation("net.fabricmc.fabric-api:fabric-api:${fabric_version}") } + if (loaderName == "fabric-o" && sc.current.parsed <= "1.21.11") { + mappings loom.layered { + officialMojangMappings() + } + } } processResources { diff --git a/neoforge/build.gradle b/neoforge/build.gradle index 54c5292..2ba83ea 100644 --- a/neoforge/build.gradle +++ b/neoforge/build.gradle @@ -5,7 +5,6 @@ plugins { } ext.loaderName = "neoforge" -ext.mcVersionName = "26.2" apply from: rootProject.file('gradle/shared.gradle') diff --git a/settings.gradle b/settings.gradle index 080634d..7fe5844 100644 --- a/settings.gradle +++ b/settings.gradle @@ -11,6 +11,7 @@ pluginManagement { } filter { includeGroupAndSubgroups('net.fabricmc') + includeGroup('fabric-loom') } } exclusiveContent { @@ -46,6 +47,17 @@ pluginManagement { includeGroupAndSubgroups('net.neoforged') } } + exclusiveContent { + forRepository { + maven { + name = 'ParchmentMC' + url = 'https://maven.parchmentmc.org' + } + } + filter { + includeGroupAndSubgroups('org.parchmentmc') + } + } maven { name = 'Stonecutter' url = uri('https://maven.kikugie.dev/releases') @@ -60,20 +72,22 @@ plugins { rootProject.name = 'forge-autofish' -stonecutter { - create(rootProject) { - kotlinController = false - centralScript = "build.gradle" - vcsVersion = "26.2" +stonecutter.create(rootProject) { + kotlinController = false + centralScript = "build.gradle" + versions("26.2", "26.1.2", "1.21.11") + vcsVersion = "26.2" - branch("fabric") { - version("26.2") - } - branch("forge") { - version("26.2") - } - branch("neoforge") { - version("26.2") - } + branch("fabric") { + versions("26.2", "26.1.2") + } + branch("fabric-o") { + versions("1.21.11") + } + branch("forge") { + versions("26.2", "26.1.2", "1.21.11") + } + branch("neoforge") { + versions("26.2", "26.1.2", "1.21.11") } } diff --git a/src/main/java/in/northwestw/autofish/config/Config.java b/src/main/java/in/northwestw/autofish/config/Config.java index e6c7074..90982f7 100644 --- a/src/main/java/in/northwestw/autofish/config/Config.java +++ b/src/main/java/in/northwestw/autofish/config/Config.java @@ -13,10 +13,10 @@ public class Config { private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); - public static final long[] RECAST_DELAY_RANGE = { 20L, 1L, 600L }; - public static final long[] REEL_IN_DELAY_RANGE = { 0L, 0L, 600L }; - public static final long[] THROW_DELAY_RANGE = { 10L, 5L, 600L }; - public static final long[] CHECK_INTERVAL_RANGE = { 200L, 20L, 72000L }; + public static final long[] RECAST_DELAY_RANGE = { 1L, 600L }; + public static final long[] REEL_IN_DELAY_RANGE = { 0L, 600L }; + public static final long[] THROW_DELAY_RANGE = { 5L, 600L }; + public static final long[] CHECK_INTERVAL_RANGE = { 20L, 72000L }; public static long recastDelay = 20, reelInDelay = 0, throwDelay = 10, checkInterval = 200; public static boolean autoFish = true, rodProtect = true, autoReplace = true, allFilters = true; @@ -75,19 +75,19 @@ public static void load() { filter = json.getAsJsonArray("filter").asList().stream().map(JsonElement::getAsString).toList(); // validate - if (recastDelay < 1 || recastDelay > 600) { + if (recastDelay < RECAST_DELAY_RANGE[0] || recastDelay > RECAST_DELAY_RANGE[1]) { AutoFish.LOGGER.warn("recast_delay must be in range [1, 600]. Defaults to 20"); recastDelay = 20; } - if (reelInDelay < 0 || reelInDelay > 600) { + if (reelInDelay < REEL_IN_DELAY_RANGE[0] || reelInDelay > REEL_IN_DELAY_RANGE[1]) { AutoFish.LOGGER.warn("reel_in_delay must be in range [0, 600]. Defaults to 0"); reelInDelay = 0; } - if (throwDelay < 5 || throwDelay > 600) { + if (throwDelay < THROW_DELAY_RANGE[0] || throwDelay > THROW_DELAY_RANGE[1]) { AutoFish.LOGGER.warn("throw_delay must be in range [5, 600]. Defaults to 10"); throwDelay = 10; } - if (checkInterval < 20 || checkInterval > 72000) { + if (checkInterval < CHECK_INTERVAL_RANGE[0] || checkInterval > CHECK_INTERVAL_RANGE[1]) { AutoFish.LOGGER.warn("check_interval must be in range [20, 72000]. Defaults to 200"); checkInterval = 200; } diff --git a/src/main/java/in/northwestw/autofish/config/gui/CheckIntervalScreen.java b/src/main/java/in/northwestw/autofish/config/gui/CheckIntervalScreen.java deleted file mode 100644 index 3e11c82..0000000 --- a/src/main/java/in/northwestw/autofish/config/gui/CheckIntervalScreen.java +++ /dev/null @@ -1,87 +0,0 @@ -package in.northwestw.autofish.config.gui; - -import in.northwestw.autofish.AutoFish; -import in.northwestw.autofish.config.Config; -import in.northwestw.autofish.handler.AutoFishHandler; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.GuiGraphicsExtractor; -import net.minecraft.client.gui.components.Button; -import net.minecraft.client.gui.components.EditBox; -import net.minecraft.client.gui.screens.Screen; -import net.minecraft.client.input.KeyEvent; -import net.minecraft.client.input.MouseButtonEvent; -import org.lwjgl.glfw.GLFW; - -import java.util.regex.Pattern; - -public class CheckIntervalScreen extends Screen { - private final Screen parent; - private EditBox checkInterval; - - protected CheckIntervalScreen(Screen parent) { - super(AutoFish.getTranslatableComponent("gui.setcheckinterval")); - this.parent = parent; - } - - @Override - protected void init() { - checkInterval = new EditBox(this.font, this.width / 2 - 75, this.height / 2 - 25, 150, 20, AutoFish.getTranslatableComponent("gui.setcheckinterval.checkinterval")) { - @Override - public boolean mouseClicked(MouseButtonEvent ev, boolean p_430750_) { - if (ev.button() == GLFW.GLFW_MOUSE_BUTTON_2) this.setValue(""); - return super.mouseClicked(ev, p_430750_); - } - }; - checkInterval.setValue(Long.toString(Config.checkInterval)); - addRenderableWidget(checkInterval); - Button save = new Button.Builder(AutoFish.getTranslatableComponent("gui.setcheckinterval.save"), button -> { - if (!isNumeric(checkInterval.getValue())) checkInterval.setValue(Long.toString(Config.checkInterval)); - else { - long delay = Long.parseLong(checkInterval.getValue()); - if (delay < Config.CHECK_INTERVAL_RANGE[1] || delay > Config.CHECK_INTERVAL_RANGE[2]) checkInterval.setValue(Long.toString(Config.checkInterval)); - else { - Config.setCheckInterval(delay); - Minecraft.getInstance().setScreenAndShow(parent); - } - } - }).pos(this.width / 2 - 75, this.height / 2).size(150, 20).build(); - addRenderableWidget(save); - } - - @Override - public void tick() { - //checkInterval.tick(); - super.tick(); - } - - private static final Pattern pattern = Pattern.compile("-?\\d+(\\.\\d+)?"); - public static boolean isNumeric(String strNum) { - if (strNum == null) { - return false; - } - return pattern.matcher(strNum).matches(); - } - - @Override - public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float partialTicks) { - super.extractRenderState(graphics, mouseX, mouseY, partialTicks); - graphics.centeredText(this.font, this.title, this.width / 2, 20, -1); - this.checkInterval.extractRenderState(graphics, mouseX, mouseY, partialTicks); - } - - @Override - public boolean shouldCloseOnEsc() { - return false; - } - - @Override - public boolean keyPressed(KeyEvent ev) { - if (ev.key() == GLFW.GLFW_KEY_ESCAPE) Minecraft.getInstance().setScreenAndShow(parent); - return super.keyPressed(ev); - } - - @Override - public boolean isPauseScreen() { - return false; - } -} diff --git a/src/main/java/in/northwestw/autofish/config/gui/FilterSelectionScreen.java b/src/main/java/in/northwestw/autofish/config/gui/FilterSelectionScreen.java index 2922895..e15da39 100644 --- a/src/main/java/in/northwestw/autofish/config/gui/FilterSelectionScreen.java +++ b/src/main/java/in/northwestw/autofish/config/gui/FilterSelectionScreen.java @@ -4,7 +4,10 @@ import in.northwestw.autofish.AutoFish; import in.northwestw.autofish.config.Config; import net.minecraft.client.Minecraft; +//? if >=26.1 { import net.minecraft.client.gui.GuiGraphicsExtractor; +//?} else +//import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.components.EditBox; import net.minecraft.client.gui.screens.Screen; @@ -18,7 +21,6 @@ import net.minecraft.world.item.ItemStack; import org.lwjgl.glfw.GLFW; -import java.awt.*; import java.util.List; import java.util.*; import java.util.stream.Collectors; @@ -79,7 +81,7 @@ public boolean mouseClicked(MouseButtonEvent ev, boolean p_430750_) { return matchmod && matchtag && matcharg; }).collect(Collectors.toList()); maxPage = (int) Math.ceil(searching.size() / (double) max); - if (page > maxPage - 1) page = maxPage - 1; + if (page > maxPage - 1) page = Math.max(0, maxPage - 1); }); addRenderableWidget(search); Button add = new Button.Builder(AutoFish.getTranslatableComponent("gui.filterselection.save"), button -> { @@ -99,9 +101,15 @@ public boolean mouseClicked(MouseButtonEvent ev, boolean p_430750_) { } @Override + //? if >=26.1 { public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float partialTicks) { super.extractRenderState(graphics, mouseX, mouseY, partialTicks); graphics.centeredText(this.font, this.title, this.width / 2, 20, -1); + //?} else { + /*public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) { + super.render(graphics, mouseX, mouseY, partialTicks); + graphics.drawCenteredString(this.font, this.title, this.width / 2, 20, -1); + *///?} Collection searchingCopy = Lists.newArrayList(); Collection prioritized = searching.stream().filter(item -> { Optional> opt = BuiltInRegistries.ITEM.getResourceKey(item); @@ -121,20 +129,29 @@ public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mo int y = getYPos(k, reducedHeight); ItemStack stack = new ItemStack(item); if (!stack.isEmpty()) { + //? if >=26.1 { graphics.item(stack, x, y); + //? } else + //graphics.renderItem(stack, x, y); if (!clickProcessed && isMouseInRange(clickX, clickY, x, y, x+16, y+16)) { if (selected.contains(item)) selected.remove(item); else selected.add(item); clickProcessed = true; } - if (selected.contains(item)) graphics.fillGradient(x - 2, y - 2, x + 18, y + 18, Color.GREEN.getRGB(), Color.GREEN.getRGB()); - else if (isMouseInRange(mouseX, mouseY, x, y,x + 16, y + 16)) graphics.fillGradient(x - 2, y - 2, x + 18, y + 18, Color.LIGHT_GRAY.getRGB(), Color.LIGHT_GRAY.getRGB()); + if (selected.contains(item)) graphics.fillGradient(x - 2, y - 2, x + 18, y + 18, 0xFF00FF00, 0xFF00FF00); + else if (isMouseInRange(mouseX, mouseY, x, y,x + 16, y + 16)) graphics.fillGradient(x - 2, y - 2, x + 18, y + 18, 0xFFC0C0C0, 0xFFC0C0C0); //if (isMouseInRange(mouseX, mouseY, x, y,x + 16, y + 16)) graphics.item(this.font, stack, mouseX, mouseY); + //? if >=26.1 { graphics.item(stack, x, y); + //? } else + //graphics.renderItem(stack, x, y); } } } + //? if >=26.1 { search.extractRenderState(graphics, mouseX, mouseY, partialTicks); + //?} else + //search.render(graphics, mouseX, mouseY, partialTicks); } private boolean isMouseInRange(double mouseX, double mouseY, int x1, int y1, int x2, int y2) { diff --git a/src/main/java/in/northwestw/autofish/config/gui/ThrowDelayScreen.java b/src/main/java/in/northwestw/autofish/config/gui/LongSettingScreen.java similarity index 53% rename from src/main/java/in/northwestw/autofish/config/gui/ThrowDelayScreen.java rename to src/main/java/in/northwestw/autofish/config/gui/LongSettingScreen.java index ae1a355..e95a923 100644 --- a/src/main/java/in/northwestw/autofish/config/gui/ThrowDelayScreen.java +++ b/src/main/java/in/northwestw/autofish/config/gui/LongSettingScreen.java @@ -1,10 +1,11 @@ package in.northwestw.autofish.config.gui; import in.northwestw.autofish.AutoFish; -import in.northwestw.autofish.config.Config; -import in.northwestw.autofish.handler.AutoFishHandler; import net.minecraft.client.Minecraft; +//? if >=26.1 { import net.minecraft.client.gui.GuiGraphicsExtractor; + //?} else +//import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.components.EditBox; import net.minecraft.client.gui.screens.Screen; @@ -12,35 +13,46 @@ import net.minecraft.client.input.MouseButtonEvent; import org.lwjgl.glfw.GLFW; +import java.util.function.Consumer; +import java.util.function.Supplier; import java.util.regex.Pattern; -public class ThrowDelayScreen extends Screen { +public class LongSettingScreen extends Screen { private final Screen parent; - private EditBox throwDelay; + private final String middleTranslationKey; + private final Supplier supplier; + private final Consumer consumer; + private final long min, max; + private EditBox editBox; - protected ThrowDelayScreen(Screen parent) { - super(AutoFish.getTranslatableComponent("gui.setthrowdelay")); + protected LongSettingScreen(Screen parent, String middleTranslationKey, Supplier supplier, Consumer consumer, long min, long max) { + super(AutoFish.getTranslatableComponent("gui." + middleTranslationKey)); this.parent = parent; + this.middleTranslationKey = middleTranslationKey; + this.supplier = supplier; + this.consumer = consumer; + this.min = min; + this.max = max; } @Override protected void init() { - throwDelay = new EditBox(this.font, this.width / 2 - 75, this.height / 2 - 25, 150, 20, AutoFish.getTranslatableComponent("gui.setthrowdelay.throwdelay")) { + editBox = new EditBox(this.font, this.width / 2 - 75, this.height / 2 - 25, 150, 20, AutoFish.getTranslatableComponent("gui." + this.middleTranslationKey + ".throwdelay")) { @Override public boolean mouseClicked(MouseButtonEvent ev, boolean flag) { if (ev.button() == GLFW.GLFW_MOUSE_BUTTON_2) this.setValue(""); return super.mouseClicked(ev, flag); } }; - throwDelay.setValue(Long.toString(Config.throwDelay)); - addRenderableWidget(throwDelay); - Button save = new Button.Builder(AutoFish.getTranslatableComponent("gui.setthrowdelay.save"), button -> { - if (!isNumeric(throwDelay.getValue())) throwDelay.setValue(Long.toString(Config.throwDelay)); + editBox.setValue(Long.toString(this.supplier.get())); + addRenderableWidget(editBox); + Button save = new Button.Builder(AutoFish.getTranslatableComponent("gui." + this.middleTranslationKey + ".save"), button -> { + if (!isNumeric(editBox.getValue())) editBox.setValue(Long.toString(this.supplier.get())); else { - long delay = Long.parseLong(throwDelay.getValue()); - if (delay < Config.THROW_DELAY_RANGE[1] || delay > Config.THROW_DELAY_RANGE[2]) throwDelay.setValue(Long.toString(Config.throwDelay)); + long delay = Long.parseLong(editBox.getValue()); + if (delay < this.min || delay > this.max) editBox.setValue(Long.toString(this.supplier.get())); else { - Config.setThrowDelay(delay); + this.consumer.accept(delay); Minecraft.getInstance().setScreenAndShow(parent); } } @@ -63,11 +75,17 @@ public static boolean isNumeric(String strNum) { } @Override + //? if >=26.1 { public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float partialTicks) { super.extractRenderState(graphics, mouseX, mouseY, partialTicks); graphics.centeredText(this.font, this.title, this.width / 2, 20, -1); - this.throwDelay.extractRenderState(graphics, mouseX, mouseY, partialTicks); - } + this.editBox.extractRenderState(graphics, mouseX, mouseY, partialTicks); + }//?} else { + /*public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) { + super.render(graphics, mouseX, mouseY, partialTicks); + graphics.drawCenteredString(this.font, this.title, this.width / 2, 20, -1); + this.editBox.render(graphics, mouseX, mouseY, partialTicks); + }*///?} @Override public boolean shouldCloseOnEsc() { diff --git a/src/main/java/in/northwestw/autofish/config/gui/RecastDelayScreen.java b/src/main/java/in/northwestw/autofish/config/gui/RecastDelayScreen.java deleted file mode 100644 index bf52499..0000000 --- a/src/main/java/in/northwestw/autofish/config/gui/RecastDelayScreen.java +++ /dev/null @@ -1,87 +0,0 @@ -package in.northwestw.autofish.config.gui; - -import in.northwestw.autofish.AutoFish; -import in.northwestw.autofish.config.Config; -import in.northwestw.autofish.handler.AutoFishHandler; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.GuiGraphicsExtractor; -import net.minecraft.client.gui.components.Button; -import net.minecraft.client.gui.components.EditBox; -import net.minecraft.client.gui.screens.Screen; -import net.minecraft.client.input.KeyEvent; -import net.minecraft.client.input.MouseButtonEvent; -import org.lwjgl.glfw.GLFW; - -import java.util.regex.Pattern; - -public class RecastDelayScreen extends Screen { - private final Screen parent; - private EditBox recastDelay; - - protected RecastDelayScreen(Screen parent) { - super(AutoFish.getTranslatableComponent("gui.setrecastdelay")); - this.parent = parent; - } - - @Override - protected void init() { - recastDelay = new EditBox(this.font, this.width / 2 - 75, this.height / 2 - 25, 150, 20, AutoFish.getTranslatableComponent("gui.setrecastdelay.recastdelay")) { - @Override - public boolean mouseClicked(MouseButtonEvent ev, boolean flag) { - if (ev.button() == GLFW.GLFW_MOUSE_BUTTON_2) this.setValue(""); - return super.mouseClicked(ev, flag); - } - }; - recastDelay.setValue(Long.toString(Config.recastDelay)); - addRenderableWidget(recastDelay); - Button save = new Button.Builder(AutoFish.getTranslatableComponent("gui.setrecastdelay.save"), button -> { - if (!isNumeric(recastDelay.getValue())) recastDelay.setValue(Long.toString(Config.recastDelay)); - else { - long delay = Long.parseLong(recastDelay.getValue()); - if (delay < Config.RECAST_DELAY_RANGE[1] || delay > Config.RECAST_DELAY_RANGE[2]) recastDelay.setValue(Long.toString(Config.recastDelay)); - else { - Config.setRecastDelay(delay); - Minecraft.getInstance().setScreenAndShow(parent); - } - } - }).pos(this.width / 2 - 75, this.height / 2).size(150, 20).build(); - addRenderableWidget(save); - } - - @Override - public void tick() { - //recastDelay.tick(); - super.tick(); - } - - private static final Pattern pattern = Pattern.compile("-?\\d+(\\.\\d+)?"); - public static boolean isNumeric(String strNum) { - if (strNum == null) { - return false; - } - return pattern.matcher(strNum).matches(); - } - - @Override - public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float partialTicks) { - super.extractRenderState(graphics, mouseX, mouseY, partialTicks); - graphics.centeredText(this.font, this.title, this.width / 2, 20, -1); - this.recastDelay.extractRenderState(graphics, mouseX, mouseY, partialTicks); - } - - @Override - public boolean shouldCloseOnEsc() { - return false; - } - - @Override - public boolean keyPressed(KeyEvent ev) { - if (ev.key() == GLFW.GLFW_KEY_ESCAPE) Minecraft.getInstance().setScreenAndShow(parent); - return super.keyPressed(ev); - } - - @Override - public boolean isPauseScreen() { - return false; - } -} diff --git a/src/main/java/in/northwestw/autofish/config/gui/ReelInDelayScreen.java b/src/main/java/in/northwestw/autofish/config/gui/ReelInDelayScreen.java deleted file mode 100644 index c188053..0000000 --- a/src/main/java/in/northwestw/autofish/config/gui/ReelInDelayScreen.java +++ /dev/null @@ -1,87 +0,0 @@ -package in.northwestw.autofish.config.gui; - -import in.northwestw.autofish.AutoFish; -import in.northwestw.autofish.config.Config; -import in.northwestw.autofish.handler.AutoFishHandler; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.GuiGraphicsExtractor; -import net.minecraft.client.gui.components.Button; -import net.minecraft.client.gui.components.EditBox; -import net.minecraft.client.gui.screens.Screen; -import net.minecraft.client.input.KeyEvent; -import net.minecraft.client.input.MouseButtonEvent; -import org.lwjgl.glfw.GLFW; - -import java.util.regex.Pattern; - -public class ReelInDelayScreen extends Screen { - private final Screen parent; - private EditBox reelInDelay; - - protected ReelInDelayScreen(Screen parent) { - super(AutoFish.getTranslatableComponent("gui.setreelindelay")); - this.parent = parent; - } - - @Override - protected void init() { - reelInDelay = new EditBox(this.font, this.width / 2 - 75, this.height / 2 - 25, 150, 20, AutoFish.getTranslatableComponent("gui.setreelindelay.reelindelay")) { - @Override - public boolean mouseClicked(MouseButtonEvent ev, boolean flag) { - if (ev.button() == GLFW.GLFW_MOUSE_BUTTON_2) this.setValue(""); - return super.mouseClicked(ev, flag); - } - }; - reelInDelay.setValue(Long.toString(Config.reelInDelay)); - addRenderableWidget(reelInDelay); - Button save = new Button.Builder(AutoFish.getTranslatableComponent("gui.setreelindelay.save"), button -> { - if (!isNumeric(reelInDelay.getValue())) reelInDelay.setValue(Long.toString(Config.recastDelay)); - else { - long delay = Long.parseLong(reelInDelay.getValue()); - if (delay < Config.REEL_IN_DELAY_RANGE[1] || delay > Config.REEL_IN_DELAY_RANGE[2]) reelInDelay.setValue(Long.toString(Config.reelInDelay)); - else { - Config.setReelInDelay(delay); - Minecraft.getInstance().setScreenAndShow(parent); - } - } - }).pos(this.width / 2 - 75, this.height / 2).size(150, 20).build(); - addRenderableWidget(save); - } - - @Override - public void tick() { - //reelInDelay.tick(); - super.tick(); - } - - private static final Pattern pattern = Pattern.compile("-?\\d+(\\.\\d+)?"); - public static boolean isNumeric(String strNum) { - if (strNum == null) { - return false; - } - return pattern.matcher(strNum).matches(); - } - - @Override - public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float partialTicks) { - super.extractRenderState(graphics, mouseX, mouseY, partialTicks); - graphics.centeredText(this.font, this.title, this.width / 2, 20, -1); - this.reelInDelay.extractRenderState(graphics, mouseX, mouseY, partialTicks); - } - - @Override - public boolean shouldCloseOnEsc() { - return false; - } - - @Override - public boolean keyPressed(KeyEvent ev) { - if (ev.key() == GLFW.GLFW_KEY_ESCAPE) Minecraft.getInstance().setScreenAndShow(parent); - return super.keyPressed(ev); - } - - @Override - public boolean isPauseScreen() { - return false; - } -} diff --git a/src/main/java/in/northwestw/autofish/config/gui/SettingsScreen.java b/src/main/java/in/northwestw/autofish/config/gui/SettingsScreen.java index 13c47c7..1c1c3ba 100644 --- a/src/main/java/in/northwestw/autofish/config/gui/SettingsScreen.java +++ b/src/main/java/in/northwestw/autofish/config/gui/SettingsScreen.java @@ -1,8 +1,12 @@ package in.northwestw.autofish.config.gui; import in.northwestw.autofish.AutoFish; +import in.northwestw.autofish.config.Config; import net.minecraft.client.Minecraft; +//? if >=26.1 { import net.minecraft.client.gui.GuiGraphicsExtractor; + //?} else +//import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.screens.Screen; @@ -21,11 +25,16 @@ public boolean isPauseScreen() { @Override protected void init() { Button.Builder[] builders = { - new Button.Builder(AutoFish.getTranslatableComponent("gui.autofish.recastdelay"), button -> Minecraft.getInstance().setScreenAndShow(new RecastDelayScreen(this))), - new Button.Builder(AutoFish.getTranslatableComponent("gui.autofish.reelindelay"), button -> Minecraft.getInstance().setScreenAndShow(new ReelInDelayScreen(this))), - new Button.Builder(AutoFish.getTranslatableComponent("gui.autofish.throwdelay"), button -> Minecraft.getInstance().setScreenAndShow(new ThrowDelayScreen(this))), - new Button.Builder(AutoFish.getTranslatableComponent("gui.autofish.checkinterval"), button -> Minecraft.getInstance().setScreenAndShow(new CheckIntervalScreen(this))), - new Button.Builder(AutoFish.getTranslatableComponent("gui.autofish.filter"), button -> Minecraft.getInstance().setScreenAndShow(new SuperFilterScreen(this))) + new Button.Builder(AutoFish.getTranslatableComponent("gui.autofish.recastdelay"), button -> + Minecraft.getInstance().setScreenAndShow(new LongSettingScreen(this, "setrecastdelay", () -> Config.recastDelay, (newDelay) -> Config.recastDelay = newDelay, Config.RECAST_DELAY_RANGE[0], Config.RECAST_DELAY_RANGE[1]))), + new Button.Builder(AutoFish.getTranslatableComponent("gui.autofish.reelindelay"), button -> + Minecraft.getInstance().setScreenAndShow(new LongSettingScreen(this, "setreelindelay", () -> Config.reelInDelay, (newDelay) -> Config.reelInDelay = newDelay, Config.REEL_IN_DELAY_RANGE[0], Config.REEL_IN_DELAY_RANGE[1]))), + new Button.Builder(AutoFish.getTranslatableComponent("gui.autofish.throwdelay"), button -> + Minecraft.getInstance().setScreenAndShow(new LongSettingScreen(this, "setthrowdelay", () -> Config.throwDelay, (newDelay) -> Config.throwDelay = newDelay, Config.THROW_DELAY_RANGE[0], Config.THROW_DELAY_RANGE[1]))), + new Button.Builder(AutoFish.getTranslatableComponent("gui.autofish.checkinterval"), button -> + Minecraft.getInstance().setScreenAndShow(new LongSettingScreen(this, "setcheckinterval", () -> Config.checkInterval, (newInterval) -> Config.checkInterval = newInterval, Config.CHECK_INTERVAL_RANGE[0], Config.CHECK_INTERVAL_RANGE[1]))), + new Button.Builder(AutoFish.getTranslatableComponent("gui.autofish.filter"), button -> + Minecraft.getInstance().setScreenAndShow(new SuperFilterScreen(this))) }; for (int ii = 0; ii < builders.length; ii++) { @@ -38,8 +47,13 @@ protected void init() { } @Override + //? if >=26.1 { public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float partialTicks) { super.extractRenderState(graphics, mouseX, mouseY, partialTicks); graphics.centeredText(this.font, this.title, this.width / 2, 20, -1); - } + }//?} else { + /*public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) { + super.render(graphics, mouseX, mouseY, partialTicks); + graphics.drawCenteredString(this.font, this.title, this.width / 2, 20, -1); + }*///?} } diff --git a/src/main/java/in/northwestw/autofish/config/gui/SuperFilterScreen.java b/src/main/java/in/northwestw/autofish/config/gui/SuperFilterScreen.java index 570f199..067c672 100644 --- a/src/main/java/in/northwestw/autofish/config/gui/SuperFilterScreen.java +++ b/src/main/java/in/northwestw/autofish/config/gui/SuperFilterScreen.java @@ -4,7 +4,10 @@ import in.northwestw.autofish.AutoFish; import in.northwestw.autofish.config.Config; import net.minecraft.client.Minecraft; +//? if >=26.1 { import net.minecraft.client.gui.GuiGraphicsExtractor; + //?} else +//import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.components.EditBox; import net.minecraft.client.gui.screens.Screen; @@ -18,7 +21,6 @@ import net.minecraft.world.item.ItemStack; import org.lwjgl.glfw.GLFW; -import java.awt.*; import java.util.Arrays; import java.util.Collection; import java.util.List; @@ -85,7 +87,7 @@ public boolean mouseClicked(MouseButtonEvent ev, boolean flag) { return matchmod && matchtag && matcharg; }).collect(Collectors.toList()); maxPage = (int) Math.ceil(original.size() / (double) max); - if (page > maxPage - 1) page = maxPage - 1; + if (page > maxPage - 1) page = Math.max(0, maxPage - 1); }); addRenderableWidget(search); Button add = new Button.Builder(AutoFish.getTranslatableComponent("gui.superfilterscreen.openfilter"), button -> Minecraft.getInstance().setScreenAndShow(new FilterSelectionScreen(this))).pos(this.width / 2 - 75, 60).size(72, 20).build(); @@ -101,9 +103,15 @@ public boolean mouseClicked(MouseButtonEvent ev, boolean flag) { } @Override + //? if >=26.1 { public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float partialTicks) { super.extractRenderState(graphics, mouseX, mouseY, partialTicks); graphics.centeredText(this.font, this.title, this.width / 2, 20, -1); + //? } else { + /*public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) { + super.render(graphics, mouseX, mouseY, partialTicks); + graphics.drawCenteredString(this.font, this.title, this.width / 2, 20, -1); + *///? } Item[] items = searching.toArray(new Item[0]); for (int i = page * max; i < Math.min((page + 1) * max, searching.size()); i++) { Item item = items[i]; @@ -111,11 +119,19 @@ public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mo int k = (i % max) % (max / 3); ItemStack stack = ItemStack.EMPTY; if (item != null) stack = new ItemStack(item); + //? if >=26.1 { if (!stack.isEmpty()) graphics.item(stack, (reducedWidth * h / 3) + 15, (reducedHeight * k / (max / 3)) + 90); - graphics.text(this.font, stack.getDisplayName().getString(), ((reducedWidth * h / 3) + 45), ((reducedHeight * k / (max / 3)) + 95), Color.WHITE.getRGB()); + graphics.text(this.font, stack.getDisplayName().getString(), ((reducedWidth * h / 3) + 45), ((reducedHeight * k / (max / 3)) + 95), 0xFFFFFFFF); + //? } else { + /*if (!stack.isEmpty()) graphics.renderItem(stack, (reducedWidth * h / 3) + 15, (reducedHeight * k / (max / 3)) + 90); + graphics.drawString(this.font, stack.getDisplayName().getString(), ((reducedWidth * h / 3) + 45), ((reducedHeight * k / (max / 3)) + 95), 0xFFFFFFFF); + *///? } //this.font.draw(graphics, stack.getDisplayName().getString(), (float) ((reducedWidth * h / 3) + 45), (float) ((reducedHeight * k / (max / 3)) + 95), Color.WHITE.getRGB()); } + //? if >=26.1 { search.extractRenderState(graphics, mouseX, mouseY, partialTicks); + //? } else + //search.render(graphics, mouseX, mouseY, partialTicks); } @Override diff --git a/src/main/java/in/northwestw/autofish/handler/AutoFishHandler.java b/src/main/java/in/northwestw/autofish/handler/AutoFishHandler.java index 97037a5..6bd8b46 100644 --- a/src/main/java/in/northwestw/autofish/handler/AutoFishHandler.java +++ b/src/main/java/in/northwestw/autofish/handler/AutoFishHandler.java @@ -37,16 +37,16 @@ public static void onKeyInput() { LocalPlayer player = minecraft.player; if (KeyBinds.autofish.consumeClick()) { Config.setAutoFish(!Config.autoFish); - if (player != null) player.sendOverlayMessage(getText("autofish", Config.autoFish)); + if (player != null) sendOverlayMessage(player, "autofish", Config.autoFish); } else if (KeyBinds.rodprotect.consumeClick()) { Config.setRodProtect(!Config.rodProtect); - if (player != null) player.sendOverlayMessage(getText("rodprotect", Config.rodProtect)); + if (player != null) sendOverlayMessage(player, "rodprotect", Config.rodProtect); } else if (KeyBinds.autoreplace.consumeClick()) { Config.setAutoReplace(!Config.autoReplace); - if (player != null) player.sendOverlayMessage(getText("autoreplace", Config.autoReplace)); + if (player != null) sendOverlayMessage(player, "autoreplace", Config.autoReplace); } else if (KeyBinds.itemfilter.consumeClick()) { Config.enableFilter(!Config.allFilters); - if (player != null) player.sendOverlayMessage(getText("itemfilter", Config.allFilters)); + if (player != null) sendOverlayMessage(player, "itemfilter", Config.allFilters); } else if (KeyBinds.settings.consumeClick()) minecraft.setScreenAndShow(new SettingsScreen()); } @@ -121,7 +121,10 @@ private static void reelIn(Player player) { if (hand == null) return; player.getInventory().getNonEquipmentItems().forEach(stack -> { Identifier rl = BuiltInRegistries.ITEM.getKey(stack.getItem()); + //? if >=26.1 { itemsBeforeFished.put(rl, itemsBeforeFished.getOrDefault(rl, 0) + stack.count()); + //? } else + //itemsBeforeFished.put(rl, itemsBeforeFished.getOrDefault(rl, 0) + stack.getCount()); }); click(player, hand, Minecraft.getInstance().gameMode); ItemStack fishingRod = player.getItemInHand(hand); @@ -133,7 +136,7 @@ else if (fishingRod.getMaxDamage() - fishingRod.getDamageValue() < 3 && !player. if (Config.autoReplace) needReplace = true; else { Config.autoFish = false; - player.sendOverlayMessage(getText("forgeautofish", Config.autoFish)); + sendOverlayMessage(player, "forgeautofish", Config.autoFish); return; } if (needReplace) { @@ -214,7 +217,11 @@ private static InteractionHand findHandOfRod(Player player) { else return null; } - private static Component getText(String key, boolean bool) { - return AutoFish.getTranslatableComponent("toggle." + key, AutoFish.getTranslatableComponent("toggle.enable." + bool).withStyle(bool ? ChatFormatting.GREEN : ChatFormatting.RED)); + private static void sendOverlayMessage(Player player, String key, boolean state) { + Component component = AutoFish.getTranslatableComponent("toggle." + key, AutoFish.getTranslatableComponent("toggle.enable." + state).withStyle(state ? ChatFormatting.GREEN : ChatFormatting.RED)); + //? if >=26.1 { + player.sendOverlayMessage(getText("autofish", Config.autoFish)); + //? } else + //player.displayClientMessage(component, true); } } \ No newline at end of file diff --git a/versions/1.21.11/gradle.properties b/versions/1.21.11/gradle.properties new file mode 100644 index 0000000..1147761 --- /dev/null +++ b/versions/1.21.11/gradle.properties @@ -0,0 +1,22 @@ +# Minecraft and loader versions +java_version=21 +minecraft_version=1.21.11 +minecraft_version_range=[1.21.11, 1.22) +## This is the version of minecraft that the 'common' project uses, you can find a list of all versions here +## https://projects.neoforged.net/neoforged/neoform +neo_form_version=1.21.11-20251209.172050 +# The version of ParchmentMC that is used, see https://parchmentmc.org/docs/getting-started#choose-a-version for new versions +parchment_minecraft=1.21.11 +parchment_version=2025.12.20 + +# Fabric, see https://fabricmc.net/develop/ for new versions +fabric_version=0.141.4+1.21.11 +fabric_loader_version=0.19.3 + +# Forge, see https://files.minecraftforge.net/net/minecraftforge/forge/ for new versions +forge_version=61.1.8 +forge_loader_version_range=[61,) + +# NeoForge, see https://projects.neoforged.net/neoforged/neoforge for new versions +neoforge_version=21.11.42 +neoforge_loader_version_range=[4,) diff --git a/versions/26.1.2/gradle.properties b/versions/26.1.2/gradle.properties new file mode 100644 index 0000000..44874f6 --- /dev/null +++ b/versions/26.1.2/gradle.properties @@ -0,0 +1,19 @@ +# Minecraft and loader versions +java_version=25 +minecraft_version=26.1.2 +minecraft_version_range=[26.1.2, 26.2) +## This is the version of minecraft that the 'common' project uses, you can find a list of all versions here +## https://projects.neoforged.net/neoforged/neoform +neo_form_version=26.1.2-1 + +# Fabric, see https://fabricmc.net/develop/ for new versions +fabric_version=0.152.1+26.1.2 +fabric_loader_version=0.19.3 + +# Forge, see https://files.minecraftforge.net/net/minecraftforge/forge/ for new versions +forge_version=64.0.10 +forge_loader_version_range=[64,) + +# NeoForge, see https://projects.neoforged.net/neoforged/neoforge for new versions +neoforge_version=26.1.2.0-beta +neoforge_loader_version_range=[4,) From e706469b70c8484957ba9e60e05af4ceea79af5c Mon Sep 17 00:00:00 2001 From: North-West-Wind Date: Mon, 29 Jun 2026 18:13:00 +0800 Subject: [PATCH 10/52] refactor: use a loader property --- fabric-o/build.gradle | 4 ++-- fabric/build.gradle | 4 ++-- forge/build.gradle | 8 ++++---- gradle.properties | 1 + gradle/shared.gradle | 17 +++++++++-------- neoforge/build.gradle | 8 ++++---- 6 files changed, 22 insertions(+), 20 deletions(-) diff --git a/fabric-o/build.gradle b/fabric-o/build.gradle index 485d5c9..a0cde22 100644 --- a/fabric-o/build.gradle +++ b/fabric-o/build.gradle @@ -4,12 +4,12 @@ plugins { id 'fabric-loom' version '1.13-SNAPSHOT' } -ext.loaderName = "fabric-o" +loader = "fabric" apply from: rootProject.file('gradle/shared.gradle') loom { - def aw = rootProject.file("src/fabric/resources/${mod_id}.accesswidener") + def aw = rootProject.file("src/${loader}/resources/${mod_id}.accesswidener") if (aw.exists()) { accessWidenerPath.set(aw) } diff --git a/fabric/build.gradle b/fabric/build.gradle index 4519dfc..257d6d5 100644 --- a/fabric/build.gradle +++ b/fabric/build.gradle @@ -4,12 +4,12 @@ plugins { id 'net.fabricmc.fabric-loom' version '1.16.3' } -ext.loaderName = "fabric" +loader = "fabric" apply from: rootProject.file('gradle/shared.gradle') loom { - def aw = rootProject.file("src/${loaderName}/resources/${mod_id}.accesswidener") + def aw = rootProject.file("src/${loader}/resources/${mod_id}.accesswidener") if (aw.exists()) { accessWidenerPath.set(aw) } diff --git a/forge/build.gradle b/forge/build.gradle index 03cc798..3bcbef3 100644 --- a/forge/build.gradle +++ b/forge/build.gradle @@ -5,14 +5,14 @@ plugins { id 'idea' } -ext.loaderName = "forge" +loader = "forge" apply from: rootProject.file('gradle/shared.gradle') minecraft { mappings channel: 'official', version: minecraft_version - def at = rootProject.file("src/${loaderName}/resources/META-INF/accesstransformer.cfg") + def at = rootProject.file("src/${loader}/resources/META-INF/accesstransformer.cfg") if (at.exists()) { accessTransformer = at } @@ -38,7 +38,7 @@ minecraft { register('data') { workingDir = rootProject.file('runs/data') - args '--mod', mod_id, '--all', '--output', rootProject.file("src/${loaderName}/generated/resources"), '--existing', rootProject.file("src/${loaderName}/resources") + args '--mod', mod_id, '--all', '--output', rootProject.file("src/${loader}/generated/resources"), '--existing', rootProject.file("src/${loader}/resources") } } } @@ -49,7 +49,7 @@ repositories { maven fg.minecraftLibsMaven } -sourceSets.main.resources.srcDir rootProject.file("src/${loaderName}/generated/resources") +sourceSets.main.resources.srcDir rootProject.file("src/${loader}/generated/resources") dependencies { implementation(minecraft.dependency("net.minecraftforge:forge:${minecraft_version}-${forge_version}")) diff --git a/gradle.properties b/gradle.properties index fcf5ebc..d7af91d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -21,6 +21,7 @@ dev.kikugie.stonecutter.hard_mode=true java_version=25 minecraft_version=26.2 minecraft_version_range=[26.2, 26.3) +loader= ## This is the version of minecraft that the 'common' project uses, you can find a list of all versions here ## https://projects.neoforged.net/neoforged/neoform neo_form_version=26.2-1 diff --git a/gradle/shared.gradle b/gradle/shared.gradle index ae2b2bc..3493114 100644 --- a/gradle/shared.gradle +++ b/gradle/shared.gradle @@ -6,7 +6,7 @@ if (versionPropsFile.exists()) { } base { - archivesName = "${mod_id}-${loaderName}-${minecraft_version}" + archivesName = "${mod_id}-${loader}-${minecraft_version}" } java { @@ -19,11 +19,11 @@ sourceSets { main { java { srcDir rootProject.file("src/main/java") - srcDir rootProject.file("src/${loaderName}/java") + srcDir rootProject.file("src/${loader}/java") } resources { srcDir rootProject.file("src/main/resources") - srcDir rootProject.file("src/${loaderName}/resources") + srcDir rootProject.file("src/${loader}/resources") } } } @@ -51,14 +51,15 @@ jar { dependencies { compileOnly('net.fabricmc:sponge-mixin:0.17.3+mixin.0.8.7') compileOnly(annotationProcessor('io.github.llamalad7:mixinextras-common:0.5.3')) - if (loaderName == "fabric" || loaderName == "fabric-o") { + if (loader == "fabric") { minecraft("com.mojang:minecraft:${minecraft_version}") implementation("net.fabricmc:fabric-loader:${fabric_loader_version}") implementation("net.fabricmc.fabric-api:fabric-api:${fabric_version}") - } - if (loaderName == "fabric-o" && sc.current.parsed <= "1.21.11") { - mappings loom.layered { - officialMojangMappings() + + if (sc.current.parsed <= "1.21.11") { + mappings loom.layered { + officialMojangMappings() + } } } } diff --git a/neoforge/build.gradle b/neoforge/build.gradle index 2ba83ea..7d983ed 100644 --- a/neoforge/build.gradle +++ b/neoforge/build.gradle @@ -4,14 +4,14 @@ plugins { id 'net.neoforged.moddev' version '2.0.141' } -ext.loaderName = "neoforge" +loader = "neoforge" apply from: rootProject.file('gradle/shared.gradle') neoForge { version = neoforge_version - def at = rootProject.file("src/${loaderName}/resources/META-INF/accesstransformer.cfg") + def at = rootProject.file("src/${loader}/resources/META-INF/accesstransformer.cfg") if (at.exists()) { accessTransformers.from(at.absolutePath) } @@ -28,7 +28,7 @@ neoForge { data { clientData() gameDirectory = rootProject.file('runs/data') - programArguments.addAll '--mod', project.mod_id, '--all', '--output', rootProject.file("src/${loaderName}/generated/resources").getAbsolutePath(), '--existing', rootProject.file("src/${loaderName}/resources").getAbsolutePath() + programArguments.addAll '--mod', project.mod_id, '--all', '--output', rootProject.file("src/${loader}/generated/resources").getAbsolutePath(), '--existing', rootProject.file("src/${loader}/resources").getAbsolutePath() } server { server() @@ -44,4 +44,4 @@ neoForge { } } -sourceSets.main.resources.srcDir rootProject.file("src/${loaderName}/generated/resources") +sourceSets.main.resources.srcDir rootProject.file("src/${loader}/generated/resources") From 03bdaed98115b33c250ecc81febab8e5c5dfb4ed Mon Sep 17 00:00:00 2001 From: North-West-Wind Date: Mon, 29 Jun 2026 18:14:22 +0800 Subject: [PATCH 11/52] fix: use in-scope component --- .../java/in/northwestw/autofish/handler/AutoFishHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/in/northwestw/autofish/handler/AutoFishHandler.java b/src/main/java/in/northwestw/autofish/handler/AutoFishHandler.java index 6bd8b46..bda940b 100644 --- a/src/main/java/in/northwestw/autofish/handler/AutoFishHandler.java +++ b/src/main/java/in/northwestw/autofish/handler/AutoFishHandler.java @@ -220,7 +220,7 @@ private static InteractionHand findHandOfRod(Player player) { private static void sendOverlayMessage(Player player, String key, boolean state) { Component component = AutoFish.getTranslatableComponent("toggle." + key, AutoFish.getTranslatableComponent("toggle.enable." + state).withStyle(state ? ChatFormatting.GREEN : ChatFormatting.RED)); //? if >=26.1 { - player.sendOverlayMessage(getText("autofish", Config.autoFish)); + player.sendOverlayMessage(component); //? } else //player.displayClientMessage(component, true); } From 1f553a8d7b764fa538db04ecddb58effbadf8885 Mon Sep 17 00:00:00 2001 From: North-West-Wind Date: Mon, 29 Jun 2026 23:05:10 +0800 Subject: [PATCH 12/52] refactor: how many templates can he find? --- .gitignore | 134 +++++++++-- README.md | 36 +-- build.gradle => build.gradle.kts | 0 buildSrc/build.gradle.kts | 15 ++ buildSrc/src/main/kotlin/build-extensions.kt | 46 ++++ .../main/kotlin/multiloader-common.gradle.kts | 87 +++++++ .../main/kotlin/multiloader-loader.gradle.kts | 31 +++ common/build.gradle.kts | 47 ++++ common/gradle.properties | 1 + .../java/in/northwestw/autofish/AutoFish.java | 26 ++ .../in/northwestw/autofish/config/Config.java | 169 +++++++++++++ .../config/gui/FilterSelectionScreen.java | 203 ++++++++++++++++ .../config/gui/LongSettingScreen.java | 105 ++++++++ .../autofish/config/gui/SettingsScreen.java | 59 +++++ .../config/gui/SuperFilterScreen.java | 152 ++++++++++++ .../autofish/handler/AutoFishHandler.java | 227 ++++++++++++++++++ .../northwestw/autofish/keybind/KeyBinds.java | 20 ++ .../assets/forgeautofish/lang/en_us.json | 51 ++++ .../assets/forgeautofish/lang/zh_tw.json | 41 ++++ common/src/main/resources/autofish.png | Bin 0 -> 92082 bytes common/src/main/resources/pack.mcmeta | 7 + fabric-o/build.gradle | 30 --- fabric-o/build.gradle.kts | 39 +++ fabric-o/gradle.properties | 1 + .../northwestw/autofish/AutoFishFabric.java | 24 ++ fabric-o/src/main/resources/fabric.mod.json | 32 +++ fabric/build.gradle | 30 --- fabric/build.gradle.kts | 32 +++ fabric/gradle.properties | 1 + .../northwestw/autofish/AutoFishFabric.java | 24 ++ fabric/src/main/resources/fabric.mod.json | 32 +++ forge/build.gradle | 56 ----- forge/build.gradle.kts | 55 +++++ forge/gradle.properties | 1 + .../in/northwestw/autofish/AutoFishForge.java | 40 +++ forge/src/main/resources/META-INF/mods.toml | 27 +++ gradle.properties | 82 ++++--- neoforge/build.gradle | 47 ---- neoforge/build.gradle.kts | 38 +++ neoforge/gradle.properties | 1 + .../northwestw/autofish/AutoFishNeoForge.java | 44 ++++ .../resources/META-INF/neoforge.mods.toml | 32 +++ settings.gradle | 93 ------- settings.gradle.kts | 52 ++++ stonecutter.gradle | 4 - stonecutter.gradle.kts | 4 + versions/1.21.11/gradle.properties | 39 +-- versions/26.1.2/gradle.properties | 36 +-- versions/26.2/gradle.properties | 25 ++ 49 files changed, 1998 insertions(+), 380 deletions(-) rename build.gradle => build.gradle.kts (100%) create mode 100644 buildSrc/build.gradle.kts create mode 100644 buildSrc/src/main/kotlin/build-extensions.kt create mode 100644 buildSrc/src/main/kotlin/multiloader-common.gradle.kts create mode 100644 buildSrc/src/main/kotlin/multiloader-loader.gradle.kts create mode 100644 common/build.gradle.kts create mode 100644 common/gradle.properties create mode 100644 common/src/main/java/in/northwestw/autofish/AutoFish.java create mode 100644 common/src/main/java/in/northwestw/autofish/config/Config.java create mode 100644 common/src/main/java/in/northwestw/autofish/config/gui/FilterSelectionScreen.java create mode 100644 common/src/main/java/in/northwestw/autofish/config/gui/LongSettingScreen.java create mode 100644 common/src/main/java/in/northwestw/autofish/config/gui/SettingsScreen.java create mode 100644 common/src/main/java/in/northwestw/autofish/config/gui/SuperFilterScreen.java create mode 100644 common/src/main/java/in/northwestw/autofish/handler/AutoFishHandler.java create mode 100644 common/src/main/java/in/northwestw/autofish/keybind/KeyBinds.java create mode 100644 common/src/main/resources/assets/forgeautofish/lang/en_us.json create mode 100644 common/src/main/resources/assets/forgeautofish/lang/zh_tw.json create mode 100644 common/src/main/resources/autofish.png create mode 100644 common/src/main/resources/pack.mcmeta delete mode 100644 fabric-o/build.gradle create mode 100644 fabric-o/build.gradle.kts create mode 100644 fabric-o/gradle.properties create mode 100644 fabric-o/src/main/java/in/northwestw/autofish/AutoFishFabric.java create mode 100644 fabric-o/src/main/resources/fabric.mod.json delete mode 100644 fabric/build.gradle create mode 100644 fabric/build.gradle.kts create mode 100644 fabric/gradle.properties create mode 100644 fabric/src/main/java/in/northwestw/autofish/AutoFishFabric.java create mode 100644 fabric/src/main/resources/fabric.mod.json delete mode 100644 forge/build.gradle create mode 100644 forge/build.gradle.kts create mode 100644 forge/gradle.properties create mode 100644 forge/src/main/java/in/northwestw/autofish/AutoFishForge.java create mode 100644 forge/src/main/resources/META-INF/mods.toml delete mode 100644 neoforge/build.gradle create mode 100644 neoforge/build.gradle.kts create mode 100644 neoforge/gradle.properties create mode 100644 neoforge/src/main/java/in/northwestw/autofish/AutoFishNeoForge.java create mode 100644 neoforge/src/main/resources/META-INF/neoforge.mods.toml delete mode 100644 settings.gradle create mode 100644 settings.gradle.kts delete mode 100644 stonecutter.gradle create mode 100644 stonecutter.gradle.kts create mode 100644 versions/26.2/gradle.properties diff --git a/.gitignore b/.gitignore index 3d14050..d5f737e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,27 +1,119 @@ -# eclipse -bin -*.launch -.settings -.metadata -.classpath -.project - -# idea -out +# User-specific stuff +.idea/ + +*.iml *.ipr *.iws -*.iml -.idea/* -!.idea/scopes -# gradle -build +# IntelliJ +out/ +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + .gradle +build/ + +# Ignore Gradle GUI config +gradle-app.setting + +# Cache of project +.gradletasknamecache + +**/build/ -# other -eclipse -run -runs +# Common working directory +run/ +runs/ -# stonecutter version build dirs -*/versions/*/build/ +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar diff --git a/README.md b/README.md index c88b432..6c6a23e 100644 --- a/README.md +++ b/README.md @@ -1,32 +1,10 @@ -# MultiLoader Template +# Multiloader - Stonecutter +A gradle project template allows you to make a multi-loaders+versions mod using [Multi-loader Template](https://github.com/jaredlll08/MultiLoader-Template/) and [Stonecutter](https://stonecutter.kikugie.dev/). -This project provides a Gradle project template that can compile Minecraft mods for multiple modloaders using a common project for the sources. This project does not require any third party libraries or dependencies. If you have any questions or want to discuss the project, please join our [Discord](https://discord.myceliummod.network). +This template is based on [Faboslav](https://github.com/Faboslav) mods, extracted to be used as template. -## Getting Started +# How-to Setup +// TODO -### IntelliJ IDEA -This guide will show how to import the MultiLoader Template into IntelliJ IDEA. The setup process is roughly equivalent to setting up the modloaders independently and should be very familiar to anyone who has worked with their MDKs. - -1. Clone or download this repository to your computer. -2. Configure the project by setting the properties in the `gradle.properties` file. You will also need to change the `rootProject.name` property in `settings.gradle`, this should match the folder name of your project, or else IDEA may complain. -3. Open the template's root folder as a new project in IDEA. This is the folder that contains this README.md file and the gradlew executable. -4. If your default JVM/JDK is not Java 25 you will encounter an error when opening the project. This error is fixed by going to `File > Settings > Build, Execution, Deployment > Build Tools > Gradle > Gradle JVM` and changing the value to a valid Java 25 JVM. You will also need to set the Project SDK to Java 25. This can be done by going to `File > Project Structure > Project SDK`. Once both have been set open the Gradle tab in IDEA and click the refresh button to reload the project. -5. Open your Run/Debug Configurations. Under the `Application` category there should now be options to run Fabric and NeoForge projects. Select one of the client options and try to run it. -6. Assuming you were able to run the game in step 5 your workspace should now be set up. - -### Eclipse -While it is possible to use this template in Eclipse it is not recommended. During the development of this template multiple critical bugs and quirks related to Eclipse were found at nearly every level of the required build tools. While we continue to work with these tools to report and resolve issues support for projects like these are not there yet. For now Eclipse is considered unsupported by this project. The development cycle for build tools is notoriously slow so there are no ETAs available. - -## Development Guide -When using this template the majority of your mod should be developed in the `common` project. The `common` project is compiled against the vanilla game and is used to hold code that is shared between the different loader-specific versions of your mod. The `common` project has no knowledge or access to ModLoader specific code, apis, or concepts. Code that requires something from a specific loader must be done through the project that is specific to that loader, such as the `fabric` or `neoforge` projects. - -Loader specific projects such as the `fabric` and `neoforge` project are used to load the `common` project into the game. These projects also define code that is specific to that loader. Loader specific projects can access all the code in the `common` project. It is important to remember that the `common` project can not access code from loader specific projects. - -## Removing Platforms and Loaders -While this template has support for many modloaders, new loaders may appear in the future, and existing loaders may become less relevant. - -Removing loader specific projects is as easy as deleting the folder, and removing the `include("projectname")` line from the `settings.gradle` file. -For example if you wanted to remove support for `forge` you would follow the following steps: - -1. Delete the subproject folder. For example, delete `MultiLoader-Template/forge`. -2. Remove the project from `settings.gradle`. For example, remove `include("forge")`. +--- +Note: This template has not carefully tested so there might be bugs, feel free to report or PR to make this template better! \ No newline at end of file diff --git a/build.gradle b/build.gradle.kts similarity index 100% rename from build.gradle rename to build.gradle.kts diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts new file mode 100644 index 0000000..638afe8 --- /dev/null +++ b/buildSrc/build.gradle.kts @@ -0,0 +1,15 @@ +plugins { + `kotlin-dsl` + kotlin("jvm") version "2.2.0" +} + +repositories { + mavenCentral() + gradlePluginPortal() + maven("https://maven.kikugie.dev/snapshots") +} + +dependencies { + fun plugin(id: String, version: String) = "$id:$id.gradle.plugin:$version" + implementation("dev.kikugie:stonecutter:0.9") +} \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/build-extensions.kt b/buildSrc/src/main/kotlin/build-extensions.kt new file mode 100644 index 0000000..ffff43b --- /dev/null +++ b/buildSrc/src/main/kotlin/build-extensions.kt @@ -0,0 +1,46 @@ +import dev.kikugie.stonecutter.build.StonecutterBuildExtension +import dev.kikugie.stonecutter.controller.StonecutterControllerExtension +import org.gradle.api.Project +import org.gradle.api.artifacts.dsl.RepositoryHandler +import org.gradle.kotlin.dsl.* + +val Project.mod: ModData get() = ModData(this) +fun Project.prop(key: String): String? = findProperty(key)?.toString() +fun String.upperCaseFirst() = replaceFirstChar { if (it.isLowerCase()) it.uppercaseChar() else it } + +fun RepositoryHandler.strictMaven(url: String, alias: String, vararg groups: String) = exclusiveContent { + forRepository { maven(url) { name = alias } } + filter { groups.forEach(::includeGroup) } +} + +val Project.stonecutterBuild get() = extensions.getByType() +val Project.stonecutterController get() = extensions.getByType() + +val Project.common get() = requireNotNull(stonecutterBuild.node.sibling("common")) { + "No common project for $project" +} +val Project.commonProject get() = rootProject.project(stonecutterBuild.current.project) +val Project.commonMod get() = commonProject.mod + +val Project.loader: String? get() = prop("loader") + +@JvmInline +value class ModData(private val project: Project) { + val id: String get() = modProp("id") + val name: String get() = modProp("name") + val version: String get() = modProp("version") + val group: String get() = modProp("group") + val author: String get() = modProp("author") + val description: String get() = modProp("description") + val license: String get() = modProp("license") + val github: String get() = modProp("github") + val mc: String get() = depOrNull("minecraft") ?: project.stonecutterBuild.current.version + + fun propOrNull(key: String) = project.prop(key) + fun prop(key: String) = requireNotNull(propOrNull(key)) { "Missing '$key'" } + fun modPropOrNull(key: String) = project.prop("mod.$key") + fun modProp(key: String) = requireNotNull(modPropOrNull(key)) { "Missing 'mod.$key'" } + fun depOrNull(key: String): String? = project.prop("deps.$key")?.takeIf { it.isNotEmpty() && it != "" } + fun dep(key: String) = requireNotNull(depOrNull(key)) { "Missing 'deps.$key'" } + fun modrinth(name: String, version:String) = "maven.modrinth:$name:$version" +} \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/multiloader-common.gradle.kts b/buildSrc/src/main/kotlin/multiloader-common.gradle.kts new file mode 100644 index 0000000..09f1186 --- /dev/null +++ b/buildSrc/src/main/kotlin/multiloader-common.gradle.kts @@ -0,0 +1,87 @@ +plugins { + id("java") + //id("idea") + id("java-library") +} + +version = "${loader}-${commonMod.version}+mc${stonecutterBuild.current.version}" + +base { + archivesName = commonMod.id +} + +java { + toolchain.languageVersion = JavaLanguageVersion.of(commonProject.prop("java.version")!!) + // withSourcesJar() + // withJavadocJar() +} + +repositories { + mavenCentral() + exclusiveContent { + forRepository { + maven("https://repo.spongepowered.org/repository/maven-public") { name = "Sponge" } + } + filter { includeGroupAndSubgroups("org.spongepowered") } + } + exclusiveContent { + forRepositories( + maven("https://maven.parchmentmc.org") { name = "ParchmentMC" }, + maven("https://maven.neoforged.net/releases") { name = "NeoForge" }, + maven("https://maven.minecraftforge.net/") { name = "MinecraftForge" } + ) + filter { includeGroup("org.parchmentmc.data") } + } + maven("https://www.cursemaven.com") + maven("https://api.modrinth.com/maven") { + name = "Modrinth" + content { + includeGroup("maven.modrinth") + } + } + maven("https://maven.terraformersmc.com/releases/") { name = "TerraformersMC" } + maven("https://maven.kikugie.dev/releases") { name = "KikuGie Releases" } + maven("https://maven.kikugie.dev/snapshots") { name = "KikuGie Snapshots" } + maven("https://thedarkcolour.github.io/KotlinForForge/") +} + +tasks { + + processResources { + val expandProps = mapOf( + "javaVersion" to commonMod.propOrNull("java.version"), + "modId" to commonMod.id, + "modName" to commonMod.name, + "modVersion" to commonMod.version, + "modGroup" to commonMod.group, + "modAuthor" to commonMod.author, + "modDescription" to commonMod.description, + "modLicense" to commonMod.license, + "modGitHub" to commonMod.github, + "minecraftVersion" to commonMod.propOrNull("minecraft_version"), + "minMinecraftVersion" to commonMod.propOrNull("min_minecraft_version"), + "fabricLoaderVersion" to commonMod.depOrNull("fabric-loader"), + "fabricApiVersion" to commonMod.depOrNull("fabric-api"), + "neoForgeVersion" to commonMod.depOrNull("neoforge"), + "forgeVersion" to commonMod.depOrNull("forge"), + // "yaclVersion" to commonMod.depOrNull("yacl"), + "modMenuVersion" to commonMod.depOrNull("modmenu") + ).filterValues { it?.isNotEmpty() == true }.mapValues { (_, v) -> v!! } + + val jsonExpandProps = expandProps.mapValues { (_, v) -> v.replace("\n", "\\\\n") } + + filesMatching(listOf("META-INF/mods.toml", "META-INF/neoforge.mods.toml")) { + expand(expandProps) + } + + filesMatching(listOf("pack.mcmeta", "fabric.mod.json")) { + expand(jsonExpandProps) + } + + inputs.properties(expandProps) + } +} + +tasks.named("processResources") { + dependsOn(":common:${commonMod.propOrNull("minecraft_version")}:stonecutterGenerate") +} diff --git a/buildSrc/src/main/kotlin/multiloader-loader.gradle.kts b/buildSrc/src/main/kotlin/multiloader-loader.gradle.kts new file mode 100644 index 0000000..b292d69 --- /dev/null +++ b/buildSrc/src/main/kotlin/multiloader-loader.gradle.kts @@ -0,0 +1,31 @@ +plugins { + id("java") + //id("idea") + id("multiloader-common") +} + +val commonJava: Configuration by configurations.creating { + isCanBeResolved = true +} +val commonResources: Configuration by configurations.creating { + isCanBeResolved = true +} + +dependencies { + val commonPath = common.hierarchy.toString() + compileOnly(project(path = commonPath)) + commonJava(project(path = commonPath, configuration = "commonJava")) + commonResources(project(path = commonPath, configuration = "commonResources")) +} + +tasks { + compileJava { + dependsOn(commonJava) + source(commonJava) + } + + processResources { + dependsOn(commonResources) + from(commonResources) + } +} \ No newline at end of file diff --git a/common/build.gradle.kts b/common/build.gradle.kts new file mode 100644 index 0000000..27fca4c --- /dev/null +++ b/common/build.gradle.kts @@ -0,0 +1,47 @@ +plugins { + id("multiloader-common") + id("net.neoforged.moddev") version "2.0.141" + kotlin("jvm") version "2.2.0" + id("com.google.devtools.ksp") version "2.2.0-2.0.2" +} + +neoForge { + neoFormVersion = commonMod.dep("neoform") + // Automatically enable AccessTransformers if the file exists + val at = rootProject.file("src/main/resources/META-INF/accesstransformer.cfg") + if (at.exists()) { + accessTransformers.from(at.absolutePath) + } +} + +dependencies { + // Fabric and NeoForge both bundle Fabric Mixin, so it is safe to use it in common + // If you need to update, check what version they are using to see what is compatible + // https://github.com/neoforged/NeoForge/blob/26.2.x/gradle.properties#L37 + // https://github.com/FabricMC/fabric-loader/blob/master/gradle.properties#L12 + compileOnly("net.fabricmc:sponge-mixin:0.17.3+mixin.0.8.7") + // Fabric and NeoForge both bundle MixinExtras, so it is safe to use it in common + annotationProcessor("io.github.llamalad7:mixinextras-common:0.5.3") +} + +val commonJava: Configuration by configurations.creating { + isCanBeResolved = false + isCanBeConsumed = true +} + +val commonResources: Configuration by configurations.creating { + isCanBeResolved = false + isCanBeConsumed = true +} + +/*artifacts { + afterEvaluate { + val mainSourceSet = sourceSets.main.get() + mainSourceSet.java.sourceDirectories.files.forEach { + add(commonJava.name, it) + } + mainSourceSet.resources.sourceDirectories.files.forEach { + add(commonResources.name, it) + } + } +}*/ \ No newline at end of file diff --git a/common/gradle.properties b/common/gradle.properties new file mode 100644 index 0000000..ed5175f --- /dev/null +++ b/common/gradle.properties @@ -0,0 +1 @@ +loader=common \ No newline at end of file diff --git a/common/src/main/java/in/northwestw/autofish/AutoFish.java b/common/src/main/java/in/northwestw/autofish/AutoFish.java new file mode 100644 index 0000000..6336c65 --- /dev/null +++ b/common/src/main/java/in/northwestw/autofish/AutoFish.java @@ -0,0 +1,26 @@ +package in.northwestw.autofish; + +import in.northwestw.autofish.config.Config; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.network.chat.contents.PlainTextContents; +import net.minecraft.network.chat.contents.TranslatableContents; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class AutoFish +{ + public static final String MOD_ID = "autofish"; + public static final Logger LOGGER = LogManager.getLogger(); + + static { + Config.load(); + } + + public static MutableComponent getTranslatableComponent(String key, Object... args) { + return MutableComponent.create(new TranslatableContents(key, null, args)); + } + + public static MutableComponent getLiteralComponent(String str) { + return MutableComponent.create(new PlainTextContents.LiteralContents(str)); + } +} diff --git a/common/src/main/java/in/northwestw/autofish/config/Config.java b/common/src/main/java/in/northwestw/autofish/config/Config.java new file mode 100644 index 0000000..90982f7 --- /dev/null +++ b/common/src/main/java/in/northwestw/autofish/config/Config.java @@ -0,0 +1,169 @@ +package in.northwestw.autofish.config; + +import com.google.common.collect.Lists; +import com.google.gson.*; +import in.northwestw.autofish.AutoFish; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.List; + +public class Config { + private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); + + public static final long[] RECAST_DELAY_RANGE = { 1L, 600L }; + public static final long[] REEL_IN_DELAY_RANGE = { 0L, 600L }; + public static final long[] THROW_DELAY_RANGE = { 5L, 600L }; + public static final long[] CHECK_INTERVAL_RANGE = { 20L, 72000L }; + + public static long recastDelay = 20, reelInDelay = 0, throwDelay = 10, checkInterval = 200; + public static boolean autoFish = true, rodProtect = true, autoReplace = true, allFilters = true; + public static List filter = Lists.newArrayList(), prioritize = Lists.newArrayList(); + + public static void save() { + try { + File file = new File("config/" + AutoFish.MOD_ID + ".json"); + JsonObject json = new JsonObject(); + json.addProperty("recast_delay", recastDelay); + json.addProperty("reel_in_delay", reelInDelay); + json.addProperty("throw_delay", throwDelay); + json.addProperty("check_interval", checkInterval); + json.addProperty("auto_fish", autoFish); + json.addProperty("rod_protect", rodProtect); + json.addProperty("auto_replace", autoReplace); + json.addProperty("all_filters", allFilters); + + JsonArray array = new JsonArray(); + filter.forEach(array::add); + json.add("filter", array); + + if (file.exists() || file.createNewFile()) { + PrintWriter writer = new PrintWriter(file); + writer.println(GSON.toJson(json)); + writer.close(); + } + } catch (IOException e) { + AutoFish.LOGGER.error(e); + } + } + public static void load() { + try { + File file = new File("config/" + AutoFish.MOD_ID + ".json"); + if (!file.exists()) { + save(); + } else { + JsonObject json = GSON.fromJson(new FileReader(file), JsonObject.class); + if (json.has("recast_delay")) + recastDelay = json.get("recast_delay").getAsLong(); + if (json.has("reel_in_delay")) + reelInDelay = json.get("reel_in_delay").getAsLong(); + if (json.has("throw_delay")) + throwDelay = json.get("throw_delay").getAsLong(); + if (json.has("check_interval")) + checkInterval = json.get("check_interval").getAsLong(); + if (json.has("auto_fish")) + autoFish = json.get("auto_fish").getAsBoolean(); + if (json.has("rod_protect")) + rodProtect = json.get("rod_protect").getAsBoolean(); + if (json.has("auto_replace")) + autoReplace = json.get("auto_replace").getAsBoolean(); + if (json.has("all_filters")) + allFilters = json.get("all_filters").getAsBoolean(); + if (json.has("filter")) + filter = json.getAsJsonArray("filter").asList().stream().map(JsonElement::getAsString).toList(); + + // validate + if (recastDelay < RECAST_DELAY_RANGE[0] || recastDelay > RECAST_DELAY_RANGE[1]) { + AutoFish.LOGGER.warn("recast_delay must be in range [1, 600]. Defaults to 20"); + recastDelay = 20; + } + if (reelInDelay < REEL_IN_DELAY_RANGE[0] || reelInDelay > REEL_IN_DELAY_RANGE[1]) { + AutoFish.LOGGER.warn("reel_in_delay must be in range [0, 600]. Defaults to 0"); + reelInDelay = 0; + } + if (throwDelay < THROW_DELAY_RANGE[0] || throwDelay > THROW_DELAY_RANGE[1]) { + AutoFish.LOGGER.warn("throw_delay must be in range [5, 600]. Defaults to 10"); + throwDelay = 10; + } + if (checkInterval < CHECK_INTERVAL_RANGE[0] || checkInterval > CHECK_INTERVAL_RANGE[1]) { + AutoFish.LOGGER.warn("check_interval must be in range [20, 72000]. Defaults to 200"); + checkInterval = 200; + } + } + } catch (IOException e) { + AutoFish.LOGGER.error(e); + } + } + + public static void setRecastDelay(long recastDelay) { + if (recastDelay < 1 || recastDelay > 600) { + AutoFish.LOGGER.warn("max_circuit_size must be in range [1, 600]. Defaults to 20"); + recastDelay = 20; + } + Config.recastDelay = recastDelay; + Config.save(); + AutoFish.LOGGER.debug("Set Recast Delay: " + recastDelay); + } + + public static void setReelInDelay(long reelInDelay) { + if (reelInDelay < 0 || reelInDelay > 600) { + AutoFish.LOGGER.warn("reel_in_delay must be in range [0, 600]. Defaults to 0"); + reelInDelay = 0; + } + Config.reelInDelay = reelInDelay; + Config.save(); + AutoFish.LOGGER.debug("Set Reel In Delay: " + reelInDelay); + } + + public static void setThrowDelay(long throwDelay) { + if (throwDelay < 5 || throwDelay > 600) { + AutoFish.LOGGER.warn("throw_delay must be in range [5, 600]. Defaults to 10"); + throwDelay = 10; + } + Config.throwDelay = throwDelay; + Config.save(); + AutoFish.LOGGER.debug("Set Throw Delay: " + throwDelay); + } + + public static void setCheckInterval(long checkInterval) { + if (checkInterval < 20 || checkInterval > 72000) { + AutoFish.LOGGER.warn("check_interval must be in range [20, 72000]. Defaults to 200"); + checkInterval = 200; + } + Config.checkInterval = checkInterval; + Config.save(); + AutoFish.LOGGER.debug("Set Check Interval: " + checkInterval); + } + + public static void setAutoFish(boolean autoFish) { + Config.autoFish = autoFish; + Config.save(); + AutoFish.LOGGER.info("Toggle AutoFish: " + autoFish); + } + + public static void setRodProtect(boolean rodProtect) { + Config.rodProtect = rodProtect; + Config.save(); + AutoFish.LOGGER.info("Toggle Rod Protect: " + rodProtect); + } + + public static void setAutoReplace(boolean autoReplace) { + Config.autoReplace = autoReplace; + Config.save(); + AutoFish.LOGGER.info("Toggle Auto Replace: " + autoReplace); + } + + public static void enableFilter(boolean filter) { + Config.allFilters = filter; + Config.save(); + AutoFish.LOGGER.info("Toggle Filter: " + filter); + } + + public static void setFilter(List list) { + Config.filter = list; + Config.save(); + AutoFish.LOGGER.info("Received new Filter"); + } +} diff --git a/common/src/main/java/in/northwestw/autofish/config/gui/FilterSelectionScreen.java b/common/src/main/java/in/northwestw/autofish/config/gui/FilterSelectionScreen.java new file mode 100644 index 0000000..e15da39 --- /dev/null +++ b/common/src/main/java/in/northwestw/autofish/config/gui/FilterSelectionScreen.java @@ -0,0 +1,203 @@ +package in.northwestw.autofish.config.gui; + +import com.google.common.collect.Lists; +import in.northwestw.autofish.AutoFish; +import in.northwestw.autofish.config.Config; +import net.minecraft.client.Minecraft; +//? if >=26.1 { +import net.minecraft.client.gui.GuiGraphicsExtractor; +//?} else +//import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.components.EditBox; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.input.KeyEvent; +import net.minecraft.client.input.MouseButtonEvent; +import net.minecraft.core.HolderSet; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.Identifier; +import net.minecraft.resources.ResourceKey; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import org.lwjgl.glfw.GLFW; + +import java.util.List; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class FilterSelectionScreen extends Screen { + private final Screen parent; + private EditBox search; + private final Collection original = BuiltInRegistries.ITEM.stream().toList(); + private Collection searching; + private final Set selected = new HashSet<>(Config.filter.stream().map(string -> BuiltInRegistries.ITEM.getOptional(Identifier.parse(string))).filter(Optional::isPresent).map(Optional::get).collect(Collectors.toList())); + private int page, maxPage = (int) Math.ceil(original.size() / 300.0), max = 300; + private boolean clickProcessed = true; + private double clickX, clickY; + private Button previous, next; + int reducedHeight; + int reducedWidth; + + public FilterSelectionScreen(Screen parent) { + super(AutoFish.getTranslatableComponent("gui.filterselection")); + this.parent = parent; + } + + @Override + protected void init() { + reducedHeight = this.height - 90; + reducedWidth = this.width - 30; + max = /* (int) Math.round(300 * (reducedWidth / 550.0 + reducedHeight / 330.0) / 2.0) */ 300; + maxPage = (int) Math.ceil(original.size() / (double) max); + searching = original; + search = new EditBox(this.font, this.width / 2 - 75, 35, 150, 20, AutoFish.getTranslatableComponent("gui.superfilterscreen.search")) { + @Override + public boolean mouseClicked(MouseButtonEvent ev, boolean p_430750_) { + if (ev.button() == GLFW.GLFW_MOUSE_BUTTON_2) this.setValue(""); + return super.mouseClicked(ev, p_430750_); + } + }; + search.setResponder(s -> { + String[] args = s.split("/ +/"); + List mods = Lists.newArrayList(), tags = Lists.newArrayList(), paths = Lists.newArrayList(); + for (String arg : args) { + if (arg.startsWith("@")) mods.add(arg.toLowerCase().substring(1)); + else if (arg.startsWith("#")) tags.add(arg.toLowerCase().substring(1)); + else paths.add(arg.toLowerCase()); + } + List> itemTags = BuiltInRegistries.ITEM.getTags().filter(tag -> tags.stream().anyMatch(t -> tag.key().location().getPath().contains(t))).toList(); + searching = original.stream().filter(item -> { + Optional> opt = BuiltInRegistries.ITEM.getResourceKey(item); + if (opt.isEmpty()) return false; + Identifier rl = opt.get().identifier(); + boolean matchmod = mods.isEmpty(), matchtag = tags.isEmpty(), matcharg = false; + for (String mod : mods) + matchmod = matchmod || rl.getNamespace().toLowerCase().contains(mod); + for (HolderSet.Named itemTag : itemTags) + matchtag = matchtag || itemTag.stream().anyMatch(tagItem -> tagItem.value() == item); + for (String arg : paths) + matcharg = matcharg || rl.getPath().contains(arg); + return matchmod && matchtag && matcharg; + }).collect(Collectors.toList()); + maxPage = (int) Math.ceil(searching.size() / (double) max); + if (page > maxPage - 1) page = Math.max(0, maxPage - 1); + }); + addRenderableWidget(search); + Button add = new Button.Builder(AutoFish.getTranslatableComponent("gui.filterselection.save"), button -> { + List items = selected.stream().map(item -> BuiltInRegistries.ITEM.getKey(item).toString()).collect(Collectors.toList()); + Config.setFilter(items); + Minecraft.getInstance().setScreenAndShow(parent); + }).pos(this.width / 2 - 75, 60).size(72, 20).build(); + addRenderableWidget(add); + Button done = new Button.Builder(AutoFish.getTranslatableComponent("gui.filterselection.cancel"), button -> Minecraft.getInstance().setScreenAndShow(parent)).pos(this.width / 2 + 3, 60).size(72, 20).build(); + addRenderableWidget(done); + previous = new Button.Builder(AutoFish.getLiteralComponent("<"), button -> { if (page > 0) page--; }).pos(this.width / 2 - 100, 60).size(20, 20).build(); + previous.visible = false; + addRenderableWidget(previous); + next = new Button.Builder(AutoFish.getLiteralComponent(">"), button -> { if (page < maxPage - 1) page++; }).pos(this.width / 2 + 80, 60).size(20, 20).build(); + next.visible = false; + addRenderableWidget(next); + } + + @Override + //? if >=26.1 { + public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float partialTicks) { + super.extractRenderState(graphics, mouseX, mouseY, partialTicks); + graphics.centeredText(this.font, this.title, this.width / 2, 20, -1); + //?} else { + /*public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) { + super.render(graphics, mouseX, mouseY, partialTicks); + graphics.drawCenteredString(this.font, this.title, this.width / 2, 20, -1); + *///?} + Collection searchingCopy = Lists.newArrayList(); + Collection prioritized = searching.stream().filter(item -> { + Optional> opt = BuiltInRegistries.ITEM.getResourceKey(item); + if (opt.isEmpty()) return false; + Identifier rl = opt.get().identifier(); + boolean pri = Config.prioritize.contains(rl.toString()); + if (!pri) searchingCopy.add(item); + return pri; + }).toList(); + Item[] items = Stream.concat(prioritized.stream(), searchingCopy.stream()).toArray(Item[]::new); + if (items.length > 0 && page >= 0) { + for (int i = page * max; i < Math.min((page + 1) * max, searching.size()); i++) { + Item item = items[i]; + int h = (i % max) / (max / 30); + int k = (i % max) % (max / 30); + int x = getXPos(h, reducedWidth); + int y = getYPos(k, reducedHeight); + ItemStack stack = new ItemStack(item); + if (!stack.isEmpty()) { + //? if >=26.1 { + graphics.item(stack, x, y); + //? } else + //graphics.renderItem(stack, x, y); + if (!clickProcessed && isMouseInRange(clickX, clickY, x, y, x+16, y+16)) { + if (selected.contains(item)) selected.remove(item); + else selected.add(item); + clickProcessed = true; + } + if (selected.contains(item)) graphics.fillGradient(x - 2, y - 2, x + 18, y + 18, 0xFF00FF00, 0xFF00FF00); + else if (isMouseInRange(mouseX, mouseY, x, y,x + 16, y + 16)) graphics.fillGradient(x - 2, y - 2, x + 18, y + 18, 0xFFC0C0C0, 0xFFC0C0C0); + //if (isMouseInRange(mouseX, mouseY, x, y,x + 16, y + 16)) graphics.item(this.font, stack, mouseX, mouseY); + //? if >=26.1 { + graphics.item(stack, x, y); + //? } else + //graphics.renderItem(stack, x, y); + } + } + } + //? if >=26.1 { + search.extractRenderState(graphics, mouseX, mouseY, partialTicks); + //?} else + //search.render(graphics, mouseX, mouseY, partialTicks); + } + + private boolean isMouseInRange(double mouseX, double mouseY, int x1, int y1, int x2, int y2) { + return mouseX > x1 && mouseX < x2 && mouseY > y1 && mouseY < y2; + } + + private int getXPos(int h, int width) { + return (width * h / 30) + 15; + } + + private int getYPos(int k, int height) { + return ((height * k / (max / 30)) + 90); + } + + @Override + public boolean keyPressed(KeyEvent ev) { + if (ev.key() == GLFW.GLFW_KEY_ESCAPE) { + if (!search.isFocused()) Minecraft.getInstance().setScreenAndShow(parent); + else search.setFocused(false); + } + return super.keyPressed(ev); + } + + @Override + public boolean mouseClicked(MouseButtonEvent ev, boolean flag) { + clickX = ev.x(); + clickY = ev.y(); + clickProcessed = false; + return super.mouseClicked(ev, flag); + } + + @Override + public boolean shouldCloseOnEsc() { + return false; + } + + @Override + public void tick() { + //search.tick(); + super.tick(); + previous.visible = page >= 1; + next.visible = page < maxPage - 1; + } + + @Override + public boolean isPauseScreen() { + return false; + } +} diff --git a/common/src/main/java/in/northwestw/autofish/config/gui/LongSettingScreen.java b/common/src/main/java/in/northwestw/autofish/config/gui/LongSettingScreen.java new file mode 100644 index 0000000..e95a923 --- /dev/null +++ b/common/src/main/java/in/northwestw/autofish/config/gui/LongSettingScreen.java @@ -0,0 +1,105 @@ +package in.northwestw.autofish.config.gui; + +import in.northwestw.autofish.AutoFish; +import net.minecraft.client.Minecraft; +//? if >=26.1 { +import net.minecraft.client.gui.GuiGraphicsExtractor; + //?} else +//import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.components.EditBox; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.input.KeyEvent; +import net.minecraft.client.input.MouseButtonEvent; +import org.lwjgl.glfw.GLFW; + +import java.util.function.Consumer; +import java.util.function.Supplier; +import java.util.regex.Pattern; + +public class LongSettingScreen extends Screen { + private final Screen parent; + private final String middleTranslationKey; + private final Supplier supplier; + private final Consumer consumer; + private final long min, max; + private EditBox editBox; + + protected LongSettingScreen(Screen parent, String middleTranslationKey, Supplier supplier, Consumer consumer, long min, long max) { + super(AutoFish.getTranslatableComponent("gui." + middleTranslationKey)); + this.parent = parent; + this.middleTranslationKey = middleTranslationKey; + this.supplier = supplier; + this.consumer = consumer; + this.min = min; + this.max = max; + } + + @Override + protected void init() { + editBox = new EditBox(this.font, this.width / 2 - 75, this.height / 2 - 25, 150, 20, AutoFish.getTranslatableComponent("gui." + this.middleTranslationKey + ".throwdelay")) { + @Override + public boolean mouseClicked(MouseButtonEvent ev, boolean flag) { + if (ev.button() == GLFW.GLFW_MOUSE_BUTTON_2) this.setValue(""); + return super.mouseClicked(ev, flag); + } + }; + editBox.setValue(Long.toString(this.supplier.get())); + addRenderableWidget(editBox); + Button save = new Button.Builder(AutoFish.getTranslatableComponent("gui." + this.middleTranslationKey + ".save"), button -> { + if (!isNumeric(editBox.getValue())) editBox.setValue(Long.toString(this.supplier.get())); + else { + long delay = Long.parseLong(editBox.getValue()); + if (delay < this.min || delay > this.max) editBox.setValue(Long.toString(this.supplier.get())); + else { + this.consumer.accept(delay); + Minecraft.getInstance().setScreenAndShow(parent); + } + } + }).pos(this.width / 2 - 75, this.height / 2).size(150, 20).build(); + addRenderableWidget(save); + } + + @Override + public void tick() { + //throwDelay.tick(); + super.tick(); + } + + private static final Pattern pattern = Pattern.compile("-?\\d+(\\.\\d+)?"); + public static boolean isNumeric(String strNum) { + if (strNum == null) { + return false; + } + return pattern.matcher(strNum).matches(); + } + + @Override + //? if >=26.1 { + public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float partialTicks) { + super.extractRenderState(graphics, mouseX, mouseY, partialTicks); + graphics.centeredText(this.font, this.title, this.width / 2, 20, -1); + this.editBox.extractRenderState(graphics, mouseX, mouseY, partialTicks); + }//?} else { + /*public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) { + super.render(graphics, mouseX, mouseY, partialTicks); + graphics.drawCenteredString(this.font, this.title, this.width / 2, 20, -1); + this.editBox.render(graphics, mouseX, mouseY, partialTicks); + }*///?} + + @Override + public boolean shouldCloseOnEsc() { + return false; + } + + @Override + public boolean keyPressed(KeyEvent ev) { + if (ev.key() == GLFW.GLFW_KEY_ESCAPE) Minecraft.getInstance().setScreenAndShow(parent); + return super.keyPressed(ev); + } + + @Override + public boolean isPauseScreen() { + return false; + } +} diff --git a/common/src/main/java/in/northwestw/autofish/config/gui/SettingsScreen.java b/common/src/main/java/in/northwestw/autofish/config/gui/SettingsScreen.java new file mode 100644 index 0000000..1c1c3ba --- /dev/null +++ b/common/src/main/java/in/northwestw/autofish/config/gui/SettingsScreen.java @@ -0,0 +1,59 @@ +package in.northwestw.autofish.config.gui; + +import in.northwestw.autofish.AutoFish; +import in.northwestw.autofish.config.Config; +import net.minecraft.client.Minecraft; +//? if >=26.1 { +import net.minecraft.client.gui.GuiGraphicsExtractor; + //?} else +//import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.screens.Screen; + +public class SettingsScreen extends Screen { + private static final int WIDTH = 150, HEIGHT = 20, MARGIN = 5; + + public SettingsScreen() { + super(AutoFish.getTranslatableComponent("gui.autofish")); + } + + @Override + public boolean isPauseScreen() { + return false; + } + + @Override + protected void init() { + Button.Builder[] builders = { + new Button.Builder(AutoFish.getTranslatableComponent("gui.autofish.recastdelay"), button -> + Minecraft.getInstance().setScreenAndShow(new LongSettingScreen(this, "setrecastdelay", () -> Config.recastDelay, (newDelay) -> Config.recastDelay = newDelay, Config.RECAST_DELAY_RANGE[0], Config.RECAST_DELAY_RANGE[1]))), + new Button.Builder(AutoFish.getTranslatableComponent("gui.autofish.reelindelay"), button -> + Minecraft.getInstance().setScreenAndShow(new LongSettingScreen(this, "setreelindelay", () -> Config.reelInDelay, (newDelay) -> Config.reelInDelay = newDelay, Config.REEL_IN_DELAY_RANGE[0], Config.REEL_IN_DELAY_RANGE[1]))), + new Button.Builder(AutoFish.getTranslatableComponent("gui.autofish.throwdelay"), button -> + Minecraft.getInstance().setScreenAndShow(new LongSettingScreen(this, "setthrowdelay", () -> Config.throwDelay, (newDelay) -> Config.throwDelay = newDelay, Config.THROW_DELAY_RANGE[0], Config.THROW_DELAY_RANGE[1]))), + new Button.Builder(AutoFish.getTranslatableComponent("gui.autofish.checkinterval"), button -> + Minecraft.getInstance().setScreenAndShow(new LongSettingScreen(this, "setcheckinterval", () -> Config.checkInterval, (newInterval) -> Config.checkInterval = newInterval, Config.CHECK_INTERVAL_RANGE[0], Config.CHECK_INTERVAL_RANGE[1]))), + new Button.Builder(AutoFish.getTranslatableComponent("gui.autofish.filter"), button -> + Minecraft.getInstance().setScreenAndShow(new SuperFilterScreen(this))) + }; + + for (int ii = 0; ii < builders.length; ii++) { + Button button = builders[ii].pos(this.width / 2 - WIDTH / 2, this.height / 2 + (ii - builders.length / 2) * (HEIGHT + MARGIN)).size(WIDTH, HEIGHT).build(); + addRenderableWidget(button); + } + + Button done = new Button.Builder(AutoFish.getTranslatableComponent("gui.autofish.done"), button -> onClose()).pos(this.width / 2 - 75, this.height - 25).size(150, 20).build(); + addRenderableWidget(done); + } + + @Override + //? if >=26.1 { + public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float partialTicks) { + super.extractRenderState(graphics, mouseX, mouseY, partialTicks); + graphics.centeredText(this.font, this.title, this.width / 2, 20, -1); + }//?} else { + /*public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) { + super.render(graphics, mouseX, mouseY, partialTicks); + graphics.drawCenteredString(this.font, this.title, this.width / 2, 20, -1); + }*///?} +} diff --git a/common/src/main/java/in/northwestw/autofish/config/gui/SuperFilterScreen.java b/common/src/main/java/in/northwestw/autofish/config/gui/SuperFilterScreen.java new file mode 100644 index 0000000..067c672 --- /dev/null +++ b/common/src/main/java/in/northwestw/autofish/config/gui/SuperFilterScreen.java @@ -0,0 +1,152 @@ +package in.northwestw.autofish.config.gui; + +import com.google.common.collect.Lists; +import in.northwestw.autofish.AutoFish; +import in.northwestw.autofish.config.Config; +import net.minecraft.client.Minecraft; +//? if >=26.1 { +import net.minecraft.client.gui.GuiGraphicsExtractor; + //?} else +//import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.components.EditBox; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.input.KeyEvent; +import net.minecraft.client.input.MouseButtonEvent; +import net.minecraft.core.HolderSet; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.Identifier; +import net.minecraft.resources.ResourceKey; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import org.lwjgl.glfw.GLFW; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +public class SuperFilterScreen extends Screen { + private final Screen parent; + private EditBox search; + private Collection original; + private Collection searching; + private int page = 0, maxPage, max = 30; + private Button previous, next; + int reducedHeight; + int reducedWidth; + + protected SuperFilterScreen(Screen parent) { + super(AutoFish.getTranslatableComponent("gui.superfilterscreen")); + this.parent = parent; + } + + @Override + public void tick() { + //search.tick(); + previous.visible = page >= 1; + next.visible = page < maxPage - 1; + } + + @Override + protected void init() { + reducedHeight = this.height - 90; + reducedWidth = this.width - 30; + max = /* (int) Math.round(30 * (reducedWidth / 550.0 + reducedHeight / 330.0) / 2.0) */ 30; + original = Config.filter.stream().map(string -> BuiltInRegistries.ITEM.getOptional(Identifier.parse(string))).filter(Optional::isPresent).map(Optional::get).collect(Collectors.toList()); + maxPage = (int) Math.ceil(original.size() / (double) max); + searching = original; + search = new EditBox(this.font, this.width / 2 - 75, 35, 150, 20, AutoFish.getTranslatableComponent("gui.superfilterscreen.search")) { + @Override + public boolean mouseClicked(MouseButtonEvent ev, boolean flag) { + if (ev.button() == GLFW.GLFW_MOUSE_BUTTON_2) this.setValue(""); + return super.mouseClicked(ev, flag); + } + }; + search.setResponder(s -> { + String[] args = s.split("/ +/"); + List mods = Lists.newArrayList(), tags = Lists.newArrayList(), paths = Lists.newArrayList(); + for (String arg : args) { + if (arg.startsWith("@")) mods.add(arg.toLowerCase().substring(1)); + else if (arg.startsWith("#")) tags.add(arg.toLowerCase().substring(1)); + else paths.add(arg.toLowerCase()); + } + List> itemTags = BuiltInRegistries.ITEM.getTags().filter(tag -> tags.stream().anyMatch(t -> tag.key().location().getPath().contains(t))).toList(); + searching = original.stream().filter(item -> { + Optional> opt = BuiltInRegistries.ITEM.getResourceKey(item); + if (opt.isEmpty()) return false; + Identifier rl = opt.get().identifier(); + boolean matchmod = mods.isEmpty(), matchtag = tags.isEmpty(), matcharg = false; + for (String mod : mods) + matchmod = matchmod || rl.getNamespace().toLowerCase().contains(mod); + for (HolderSet.Named itemTag : itemTags) + matchtag = matchtag || itemTag.stream().anyMatch(tagItem -> tagItem.value() == item); + for (String arg : paths) + matcharg = matcharg || rl.getPath().contains(arg); + return matchmod && matchtag && matcharg; + }).collect(Collectors.toList()); + maxPage = (int) Math.ceil(original.size() / (double) max); + if (page > maxPage - 1) page = Math.max(0, maxPage - 1); + }); + addRenderableWidget(search); + Button add = new Button.Builder(AutoFish.getTranslatableComponent("gui.superfilterscreen.openfilter"), button -> Minecraft.getInstance().setScreenAndShow(new FilterSelectionScreen(this))).pos(this.width / 2 - 75, 60).size(72, 20).build(); + addRenderableWidget(add); + Button done = new Button.Builder(AutoFish.getTranslatableComponent("gui.superfilterscreen.done"), button -> Minecraft.getInstance().setScreenAndShow(parent)).pos(this.width / 2 + 3, 60).size(72, 20).build(); + addRenderableWidget(done); + previous = new Button.Builder(AutoFish.getLiteralComponent("<"), button -> { if (page > 0) page--; }).pos(this.width / 2 - 100, 60).size(20, 20).build(); + previous.visible = false; + addRenderableWidget(previous); + next = new Button.Builder(AutoFish.getLiteralComponent(">"), button -> { if (page < maxPage - 1) page++; }).pos(this.width / 2 + 80, 60).size(20, 20).build(); + next.visible = false; + addRenderableWidget(next); + } + + @Override + //? if >=26.1 { + public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float partialTicks) { + super.extractRenderState(graphics, mouseX, mouseY, partialTicks); + graphics.centeredText(this.font, this.title, this.width / 2, 20, -1); + //? } else { + /*public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) { + super.render(graphics, mouseX, mouseY, partialTicks); + graphics.drawCenteredString(this.font, this.title, this.width / 2, 20, -1); + *///? } + Item[] items = searching.toArray(new Item[0]); + for (int i = page * max; i < Math.min((page + 1) * max, searching.size()); i++) { + Item item = items[i]; + int h = (i % max) / (max / 3); + int k = (i % max) % (max / 3); + ItemStack stack = ItemStack.EMPTY; + if (item != null) stack = new ItemStack(item); + //? if >=26.1 { + if (!stack.isEmpty()) graphics.item(stack, (reducedWidth * h / 3) + 15, (reducedHeight * k / (max / 3)) + 90); + graphics.text(this.font, stack.getDisplayName().getString(), ((reducedWidth * h / 3) + 45), ((reducedHeight * k / (max / 3)) + 95), 0xFFFFFFFF); + //? } else { + /*if (!stack.isEmpty()) graphics.renderItem(stack, (reducedWidth * h / 3) + 15, (reducedHeight * k / (max / 3)) + 90); + graphics.drawString(this.font, stack.getDisplayName().getString(), ((reducedWidth * h / 3) + 45), ((reducedHeight * k / (max / 3)) + 95), 0xFFFFFFFF); + *///? } + //this.font.draw(graphics, stack.getDisplayName().getString(), (float) ((reducedWidth * h / 3) + 45), (float) ((reducedHeight * k / (max / 3)) + 95), Color.WHITE.getRGB()); + } + //? if >=26.1 { + search.extractRenderState(graphics, mouseX, mouseY, partialTicks); + //? } else + //search.render(graphics, mouseX, mouseY, partialTicks); + } + + @Override + public boolean shouldCloseOnEsc() { + return false; + } + + @Override + public boolean keyPressed(KeyEvent ev) { + if (ev.key() == GLFW.GLFW_KEY_ESCAPE) Minecraft.getInstance().setScreenAndShow(parent); + return super.keyPressed(ev); + } + + @Override + public boolean isPauseScreen() { + return false; + } +} diff --git a/common/src/main/java/in/northwestw/autofish/handler/AutoFishHandler.java b/common/src/main/java/in/northwestw/autofish/handler/AutoFishHandler.java new file mode 100644 index 0000000..bda940b --- /dev/null +++ b/common/src/main/java/in/northwestw/autofish/handler/AutoFishHandler.java @@ -0,0 +1,227 @@ +package in.northwestw.autofish.handler; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import in.northwestw.autofish.AutoFish; +import in.northwestw.autofish.config.Config; +import in.northwestw.autofish.config.gui.SettingsScreen; +import in.northwestw.autofish.keybind.KeyBinds; +import net.minecraft.ChatFormatting; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.MultiPlayerGameMode; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.core.Holder; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.Identifier; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.FishingRodItem; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.phys.Vec3; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +public class AutoFishHandler { + private static final List shouldDrop = Lists.newArrayList(); + private static boolean processingDrop, pendingReelIn, pendingRecast, lastTickFishing, afterDrop; + private static int dropCd, rodSlot; + private static long tick, checkTick; + private static final Map itemsBeforeFished = Maps.newHashMap(); + + public static void onKeyInput() { + Minecraft minecraft = Minecraft.getInstance(); + LocalPlayer player = minecraft.player; + if (KeyBinds.autofish.consumeClick()) { + Config.setAutoFish(!Config.autoFish); + if (player != null) sendOverlayMessage(player, "autofish", Config.autoFish); + } else if (KeyBinds.rodprotect.consumeClick()) { + Config.setRodProtect(!Config.rodProtect); + if (player != null) sendOverlayMessage(player, "rodprotect", Config.rodProtect); + } else if (KeyBinds.autoreplace.consumeClick()) { + Config.setAutoReplace(!Config.autoReplace); + if (player != null) sendOverlayMessage(player, "autoreplace", Config.autoReplace); + } else if (KeyBinds.itemfilter.consumeClick()) { + Config.enableFilter(!Config.allFilters); + if (player != null) sendOverlayMessage(player, "itemfilter", Config.allFilters); + } else if (KeyBinds.settings.consumeClick()) + minecraft.setScreenAndShow(new SettingsScreen()); + } + + public static void onPlayerTick(final Player player) { + if (Minecraft.getInstance().player == null) return; + if (!player.getUUID().equals(Minecraft.getInstance().player.getUUID())) return; + if (checkTick > 0) checkTick--; + else { + checkTick = Config.checkInterval; + if (!pendingRecast) { + if (player.fishing == null) recast(player); + else if (player.fishing.getDeltaMovement().lengthSqr() == 0) pendingReelIn = true; + } + } + if (afterDrop) { + if (tick == 0 && rodSlot != -1) { + player.getInventory().setSelectedSlot(rodSlot); + rodSlot = -1; + } + tick++; + if (tick > 2) { + afterDrop = false; + tick = 0; + } + return; + } + if (pendingReelIn) { + tick++; + if (tick >= Config.reelInDelay) { + reelIn(player); + tick = 0; + pendingReelIn = false; + } + return; + } + if (processingDrop) { + if (dropCd > 0) dropCd--; + dropItem(player); + if (shouldDrop.isEmpty()) { + processingDrop = false; + afterDrop = true; + } + return; + } + if (pendingRecast) { + tick++; + if (tick >= Config.recastDelay) { + checkItem(player); + if (processingDrop) { + tick = 0; + return; + } + recast(player); + tick = 0; + pendingRecast = false; + } + return; + } + if (!Config.autoFish || player.fishing == null) return; + Vec3 vector = player.fishing.getDeltaMovement(); + double x = vector.x(); + double y = vector.y(); + double z = vector.z(); + if (y < -0.075 && !player.level().getFluidState(player.fishing.blockPosition()).isEmpty() && x == 0 && z == 0) + pendingReelIn = true; + } + + private static void reelIn(Player player) { + if (!Config.autoFish) return; + InteractionHand hand = findHandOfRod(player); + if (hand == null) return; + player.getInventory().getNonEquipmentItems().forEach(stack -> { + Identifier rl = BuiltInRegistries.ITEM.getKey(stack.getItem()); + //? if >=26.1 { + itemsBeforeFished.put(rl, itemsBeforeFished.getOrDefault(rl, 0) + stack.count()); + //? } else + //itemsBeforeFished.put(rl, itemsBeforeFished.getOrDefault(rl, 0) + stack.getCount()); + }); + click(player, hand, Minecraft.getInstance().gameMode); + ItemStack fishingRod = player.getItemInHand(hand); + boolean needReplace = false; + if (fishingRod.getMaxDamage() - fishingRod.getDamageValue() < 2) + if (Config.autoReplace) needReplace = true; + else return; + else if (fishingRod.getMaxDamage() - fishingRod.getDamageValue() < 3 && !player.isCreative() && Config.rodProtect) + if (Config.autoReplace) needReplace = true; + else { + Config.autoFish = false; + sendOverlayMessage(player, "forgeautofish", Config.autoFish); + return; + } + if (needReplace) { + AutoFish.LOGGER.info("Fishing rod broke. Finding replacement..."); + boolean found = false; + for (int i = 0; i < 9; i++) { + if (i == player.getInventory().getSelectedSlot()) continue; + ItemStack stack = player.getInventory().getItem(i); + if (stack.getItem() instanceof FishingRodItem) { + if (Config.rodProtect && stack.getMaxDamage() - stack.getDamageValue() < 2) continue; + AutoFish.LOGGER.info("Found fishing rod for replacement"); + player.getInventory().setSelectedSlot(i); + found = true; + break; + } + } + if (!found) return; + } + pendingRecast = true; + } + + private static void recast(Player player) { + if (!Config.autoFish) return; + InteractionHand hand = findHandOfRod(player); + if (hand == null) return; + ItemStack fishingRod = player.getItemInHand(hand); + if (fishingRod.isEmpty()) return; + click(player, hand, Minecraft.getInstance().gameMode); + } + + private static void checkItem(Player player) { + if (!itemsBeforeFished.isEmpty()) { + List items = player.getInventory().getNonEquipmentItems(); + for (String name : Config.filter) { + Identifier rl = Identifier.parse(name); + Optional opt = BuiltInRegistries.ITEM.getOptional(rl); + if (opt.isEmpty()) continue; + Item item = opt.get(); + int newCount = items.stream().filter(stack -> stack.getItem().toString().equals(rl.toString())).mapToInt(ItemStack::getCount).reduce(Integer::sum).orElse(0); + int oldCount = itemsBeforeFished.getOrDefault(rl, 0); + int diff = newCount - oldCount; + for (int ii = 0; ii < diff; ii++) shouldDrop.add(item); + } + itemsBeforeFished.clear(); + if (!shouldDrop.isEmpty()) { + processingDrop = true; + rodSlot = player.getInventory().getSelectedSlot(); + } + } + } + + private static void dropItem(Player player) { + if (dropCd != 10 && dropCd != 0) return; + Item item = shouldDrop.getFirst(); + if (dropCd == 10) { + ((LocalPlayer) player).drop(false); + shouldDrop.remove(item); + return; + } + for (int ii = 0; ii < 9; ii++) { + if (!player.getInventory().getItem(ii).getItem().equals(item)) continue; + player.getInventory().setSelectedSlot(ii); + dropCd = 20; + return; + } + // if item cannot be found in hotbar, just ignore it + shouldDrop.remove(item); + } + + private static void click(Player player, InteractionHand hand, MultiPlayerGameMode controller) { + if (controller == null) return; + controller.useItem(player, hand); + } + + private static InteractionHand findHandOfRod(Player player) { + if (player.getMainHandItem().getItem() instanceof FishingRodItem) return InteractionHand.MAIN_HAND; + else if (player.getOffhandItem().getItem() instanceof FishingRodItem) return InteractionHand.OFF_HAND; + else return null; + } + + private static void sendOverlayMessage(Player player, String key, boolean state) { + Component component = AutoFish.getTranslatableComponent("toggle." + key, AutoFish.getTranslatableComponent("toggle.enable." + state).withStyle(state ? ChatFormatting.GREEN : ChatFormatting.RED)); + //? if >=26.1 { + player.sendOverlayMessage(component); + //? } else + //player.displayClientMessage(component, true); + } +} \ No newline at end of file diff --git a/common/src/main/java/in/northwestw/autofish/keybind/KeyBinds.java b/common/src/main/java/in/northwestw/autofish/keybind/KeyBinds.java new file mode 100644 index 0000000..3be0934 --- /dev/null +++ b/common/src/main/java/in/northwestw/autofish/keybind/KeyBinds.java @@ -0,0 +1,20 @@ +package in.northwestw.autofish.keybind; + +import in.northwestw.autofish.AutoFish; +import net.minecraft.client.KeyMapping; +import net.minecraft.resources.Identifier; +import org.lwjgl.glfw.GLFW; + +public class KeyBinds { + + public static KeyMapping autofish, rodprotect, autoreplace, settings, itemfilter; + + static { + KeyMapping.Category cat = KeyMapping.Category.register(Identifier.fromNamespaceAndPath(AutoFish.MOD_ID, "autofish")); + autofish = new KeyMapping(AutoFish.getTranslatableComponent("key.forgeautofish.autofish").getString(), GLFW.GLFW_KEY_MINUS, cat); + rodprotect = new KeyMapping(AutoFish.getTranslatableComponent("key.forgeautofish.rodprotect").getString(), GLFW.GLFW_KEY_BACKSLASH, cat); + autoreplace = new KeyMapping(AutoFish.getTranslatableComponent("key.forgeautofish.autoreplace").getString(), GLFW.GLFW_KEY_RIGHT_BRACKET, cat); + settings = new KeyMapping(AutoFish.getTranslatableComponent("key.forgeautofish.settings").getString(), GLFW.GLFW_KEY_K, cat); + itemfilter = new KeyMapping(AutoFish.getTranslatableComponent("key.forgeautofish.itemfilter").getString(), GLFW.GLFW_KEY_APOSTROPHE, cat); + } +} diff --git a/common/src/main/resources/assets/forgeautofish/lang/en_us.json b/common/src/main/resources/assets/forgeautofish/lang/en_us.json new file mode 100644 index 0000000..de66ed5 --- /dev/null +++ b/common/src/main/resources/assets/forgeautofish/lang/en_us.json @@ -0,0 +1,51 @@ +{ + "key.autofish.autofish": "Toggle AutoFish", + "key.autofish.rodprotect": "Toggle Fishing Rod Protection", + "key.autofish.autoreplace": "Toggle Auto Replace", + "key.autofish.settings": "Open Settings", + "key.autofish.itemfilter": "Toggle Item Filter", + "key.categories.autofish": "AutoFish for Forge", + + "toggle.autofish": "%s AutoFish", + "toggle.rodprotect": "%s Fishing Rod Protection", + "toggle.autoreplace": "%s Auto Replace", + "toggle.itemfilter": "%s Item Filter", + + "warning.autoreplace": "Auto Replace Coming Soon", + + "gui.autofish": "AutoFish Configuration", + "gui.autofish.reelindelay": "Reel-In Delay", + "gui.autofish.recastdelay": "Recast Delay", + "gui.autofish.throwdelay": "Throw Delay", + "gui.autofish.checkinterval": "Check Interval", + "gui.autofish.filter": "Item Filter", + "gui.autofish.done": "Done", + + "gui.setreelindelay": "Set Reel-In Delay", + "gui.setreelindelay.reelindelay": "Reel-In Delay", + "gui.setreelindelay.save": "Save Reel-In Delay", + + "gui.setrecastdelay": "Set Recast Delay", + "gui.setrecastdelay.recastdelay": "Recast Delay", + "gui.setrecastdelay.save": "Save Recast Delay", + + "gui.setthrowdelay": "Set Throw Delay", + "gui.setthrowdelay.throwdelay": "Throw Delay", + "gui.setthrowdelay.save": "Save Throw Delay", + + "gui.setcheckinterval": "Set Check Interval", + "gui.setcheckinterval.checkinterval": "Check Interval", + "gui.setcheckinterval.save": "Save Check Interval", + + "gui.superfilterscreen": "Super Item Filter", + "gui.superfilterscreen.openfilter": "Config", + "gui.superfilterscreen.search": "Search", + "gui.superfilterscreen.done": "Done", + + "gui.filterselection": "Item Filter Configuration", + "gui.filterselection.save": "Save", + "gui.filterselection.cancel": "Cancel", + + "toggle.enable.true": "Enabled", + "toggle.enable.false": "Disabled" +} \ No newline at end of file diff --git a/common/src/main/resources/assets/forgeautofish/lang/zh_tw.json b/common/src/main/resources/assets/forgeautofish/lang/zh_tw.json new file mode 100644 index 0000000..4bd31f5 --- /dev/null +++ b/common/src/main/resources/assets/forgeautofish/lang/zh_tw.json @@ -0,0 +1,41 @@ +{ + "key.autofish.autofish": "切換 自動釣魚", + "key.autofish.rodprotect": "切換 釣竿保護", + "key.autofish.autoreplace": "切換 自動取代", + "key.autofish.settings": "開啟設定", + "key.autofish.itemfilter": "切換 物品過濾", + "key.categories.autofish": "自動釣魚", + + "toggle.autofish": "%s 自動釣魚", + "toggle.rodprotect": "%s 釣竿保護", + "toggle.autoreplace": "%s 自動取代", + "toggle.itemfilter": "%s 物品過濾", + + "warning.autoreplace": "自動過濾 即將來臨", + + "gui.autofish": "自動釣魚設定", + "gui.autofish.reelindelay": "收竿延遲", + "gui.autofish.recastdelay": "投竿延遲", + "gui.autofish.filter": "物品過濾", + "gui.autofish.done": "完成", + + "gui.setrecastdelay": "投竿延遲設定", + "gui.setrecastdelay.recastdelay": "投竿延遲", + "gui.setrecastdelay.save": "儲存", + + "gui.superfilterscreen": "超級物品過濾器", + "gui.superfilterscreen.openfilter": "設定", + "gui.superfilterscreen.search": "搜尋", + "gui.superfilterscreen.done": "完成", + + "gui.filterselection": "物品過濾設定", + "gui.filterselection.save": "儲存", + "gui.filterselection.cancel": "取消", + + "gui.setreelindelay": "收竿延遲設定", + "gui.setreelindelay.reelindelay": "收竿延遲", + "gui.setreelindelay.save": "儲存", + + "toggle.enable.true": "開啟", + "toggle.enable.false": "關閉" +} \ No newline at end of file diff --git a/common/src/main/resources/autofish.png b/common/src/main/resources/autofish.png new file mode 100644 index 0000000000000000000000000000000000000000..3346eca73f20eb1da0bf60fbb3e2b167c9dadeb1 GIT binary patch literal 92082 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 Ne0}_vUGNIu`#(x| + parchment("org.parchmentmc.data:parchment-${commonMod.mc}:$parchmentVersion@zip") + } + }) + + implementation("net.fabricmc:fabric-loader:${commonMod.dep("fabric_loader")}") + implementation("net.fabricmc.fabric-api:fabric-api:${commonMod.dep("fabric_api")}+${commonMod.mc}") +} + +loom { + runs { + getByName("client") { + client() + configName = "Fabric Client" + ideConfigGenerated(true) + } + getByName("server") { + server() + configName = "Fabric Server" + ideConfigGenerated(true) + } + } +} \ No newline at end of file diff --git a/fabric-o/gradle.properties b/fabric-o/gradle.properties new file mode 100644 index 0000000..fcdea26 --- /dev/null +++ b/fabric-o/gradle.properties @@ -0,0 +1 @@ +loader=fabric \ No newline at end of file diff --git a/fabric-o/src/main/java/in/northwestw/autofish/AutoFishFabric.java b/fabric-o/src/main/java/in/northwestw/autofish/AutoFishFabric.java new file mode 100644 index 0000000..0f2bb8f --- /dev/null +++ b/fabric-o/src/main/java/in/northwestw/autofish/AutoFishFabric.java @@ -0,0 +1,24 @@ +package in.northwestw.autofish; + +import in.northwestw.autofish.handler.AutoFishHandler; +import in.northwestw.autofish.keybind.KeyBinds; +import net.fabricmc.api.ModInitializer; +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; +import net.fabricmc.fabric.api.client.keymapping.v1.KeyMappingHelper; + +public class AutoFishFabric implements ModInitializer { + + @Override + public void onInitialize() { + KeyMappingHelper.registerKeyMapping(KeyBinds.autofish); + KeyMappingHelper.registerKeyMapping(KeyBinds.rodprotect); + KeyMappingHelper.registerKeyMapping(KeyBinds.autoreplace); + KeyMappingHelper.registerKeyMapping(KeyBinds.settings); + KeyMappingHelper.registerKeyMapping(KeyBinds.itemfilter); + + ClientTickEvents.END_CLIENT_TICK.register(_ -> AutoFishHandler.onKeyInput()); + ClientTickEvents.START_CLIENT_TICK.register(client -> { + AutoFishHandler.onPlayerTick(client.player); + }); + } +} diff --git a/fabric-o/src/main/resources/fabric.mod.json b/fabric-o/src/main/resources/fabric.mod.json new file mode 100644 index 0000000..d20efe4 --- /dev/null +++ b/fabric-o/src/main/resources/fabric.mod.json @@ -0,0 +1,32 @@ +{ + "schemaVersion": 1, + "id": "${mod_id}", + "version": "${version}", + "name": "${mod_name}", + "description": "${description}", + "authors": [ + "${mod_author}" + ], + "contact": { + "homepage": "https://fabricmc.net/", + "sources": "https://github.com/FabricMC/fabric-example-mod" + }, + "license": "${license}", + "icon": "${mod_id}.png", + "environment": "*", + "entrypoints": { + "main": [ + "in.northwestw.autofish.AutoFishFabric" + ] + }, + "depends": { + "fabricloader": ">=${fabric_loader_version}", + "fabric-api": "*", + "minecraft": "~${minecraft_version}", + "java": ">=${java_version}" + }, + "suggests": { + "another-mod": "*" + } +} + \ No newline at end of file diff --git a/fabric/build.gradle b/fabric/build.gradle deleted file mode 100644 index 257d6d5..0000000 --- a/fabric/build.gradle +++ /dev/null @@ -1,30 +0,0 @@ -plugins { - id 'java-library' - id 'maven-publish' - id 'net.fabricmc.fabric-loom' version '1.16.3' -} - -loader = "fabric" - -apply from: rootProject.file('gradle/shared.gradle') - -loom { - def aw = rootProject.file("src/${loader}/resources/${mod_id}.accesswidener") - if (aw.exists()) { - accessWidenerPath.set(aw) - } - runs { - client { - client() - setConfigName('Fabric Client') - ideConfigGenerated(true) - runDir('runs/client') - } - server { - server() - setConfigName('Fabric Server') - ideConfigGenerated(true) - runDir('runs/server') - } - } -} diff --git a/fabric/build.gradle.kts b/fabric/build.gradle.kts new file mode 100644 index 0000000..0144f57 --- /dev/null +++ b/fabric/build.gradle.kts @@ -0,0 +1,32 @@ +plugins { + id("multiloader-loader") + id("net.fabricmc.fabric-loom") version "1.16.3" + kotlin("jvm") version "2.2.0" + id("com.google.devtools.ksp") version "2.2.0-2.0.2" + id("dev.kikugie.fletching-table.fabric") version "0.1.0-alpha.22" +} + +stonecutter { + +} + +dependencies { + minecraft("com.mojang:minecraft:${commonMod.mc}") + implementation("net.fabricmc:fabric-loader:${commonMod.dep("fabric_loader")}") + implementation("net.fabricmc.fabric-api:fabric-api:${commonMod.dep("fabric_api")}+${commonMod.mc}") +} + +loom { + runs { + getByName("client") { + client() + configName = "Fabric Client" + ideConfigGenerated(true) + } + getByName("server") { + server() + configName = "Fabric Server" + ideConfigGenerated(true) + } + } +} \ No newline at end of file diff --git a/fabric/gradle.properties b/fabric/gradle.properties new file mode 100644 index 0000000..fcdea26 --- /dev/null +++ b/fabric/gradle.properties @@ -0,0 +1 @@ +loader=fabric \ No newline at end of file diff --git a/fabric/src/main/java/in/northwestw/autofish/AutoFishFabric.java b/fabric/src/main/java/in/northwestw/autofish/AutoFishFabric.java new file mode 100644 index 0000000..0f2bb8f --- /dev/null +++ b/fabric/src/main/java/in/northwestw/autofish/AutoFishFabric.java @@ -0,0 +1,24 @@ +package in.northwestw.autofish; + +import in.northwestw.autofish.handler.AutoFishHandler; +import in.northwestw.autofish.keybind.KeyBinds; +import net.fabricmc.api.ModInitializer; +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; +import net.fabricmc.fabric.api.client.keymapping.v1.KeyMappingHelper; + +public class AutoFishFabric implements ModInitializer { + + @Override + public void onInitialize() { + KeyMappingHelper.registerKeyMapping(KeyBinds.autofish); + KeyMappingHelper.registerKeyMapping(KeyBinds.rodprotect); + KeyMappingHelper.registerKeyMapping(KeyBinds.autoreplace); + KeyMappingHelper.registerKeyMapping(KeyBinds.settings); + KeyMappingHelper.registerKeyMapping(KeyBinds.itemfilter); + + ClientTickEvents.END_CLIENT_TICK.register(_ -> AutoFishHandler.onKeyInput()); + ClientTickEvents.START_CLIENT_TICK.register(client -> { + AutoFishHandler.onPlayerTick(client.player); + }); + } +} diff --git a/fabric/src/main/resources/fabric.mod.json b/fabric/src/main/resources/fabric.mod.json new file mode 100644 index 0000000..d20efe4 --- /dev/null +++ b/fabric/src/main/resources/fabric.mod.json @@ -0,0 +1,32 @@ +{ + "schemaVersion": 1, + "id": "${mod_id}", + "version": "${version}", + "name": "${mod_name}", + "description": "${description}", + "authors": [ + "${mod_author}" + ], + "contact": { + "homepage": "https://fabricmc.net/", + "sources": "https://github.com/FabricMC/fabric-example-mod" + }, + "license": "${license}", + "icon": "${mod_id}.png", + "environment": "*", + "entrypoints": { + "main": [ + "in.northwestw.autofish.AutoFishFabric" + ] + }, + "depends": { + "fabricloader": ">=${fabric_loader_version}", + "fabric-api": "*", + "minecraft": "~${minecraft_version}", + "java": ">=${java_version}" + }, + "suggests": { + "another-mod": "*" + } +} + \ No newline at end of file diff --git a/forge/build.gradle b/forge/build.gradle deleted file mode 100644 index 3bcbef3..0000000 --- a/forge/build.gradle +++ /dev/null @@ -1,56 +0,0 @@ -plugins { - id 'java-library' - id 'maven-publish' - id 'net.minecraftforge.gradle' version '[7.0.17,8)' - id 'idea' -} - -loader = "forge" - -apply from: rootProject.file('gradle/shared.gradle') - -minecraft { - mappings channel: 'official', version: minecraft_version - - def at = rootProject.file("src/${loader}/resources/META-INF/accesstransformer.cfg") - if (at.exists()) { - accessTransformer = at - } - - runs { - configureEach { - systemProperty 'eventbus.api.strictRuntimeChecks', 'true' - systemProperty 'forge.enabledGameTestNamespaces', mod_id - } - - register('client') { - workingDir = rootProject.file('runs/client') - } - - register('server') { - workingDir = rootProject.file('runs/server') - args '--nogui' - } - - register('gameTestServer') { - workingDir = rootProject.file('runs/gameTestServer') - } - - register('data') { - workingDir = rootProject.file('runs/data') - args '--mod', mod_id, '--all', '--output', rootProject.file("src/${loader}/generated/resources"), '--existing', rootProject.file("src/${loader}/resources") - } - } -} - -repositories { - minecraft.mavenizer(it) - maven fg.forgeMaven - maven fg.minecraftLibsMaven -} - -sourceSets.main.resources.srcDir rootProject.file("src/${loader}/generated/resources") - -dependencies { - implementation(minecraft.dependency("net.minecraftforge:forge:${minecraft_version}-${forge_version}")) -} diff --git a/forge/build.gradle.kts b/forge/build.gradle.kts new file mode 100644 index 0000000..6d4e09a --- /dev/null +++ b/forge/build.gradle.kts @@ -0,0 +1,55 @@ +plugins { + id("multiloader-loader") + id("net.minecraftforge.gradle") version "[7.0.17,8)" + kotlin("jvm") version "2.2.0" + id("com.google.devtools.ksp") version "2.2.0-2.0.2" + id("dev.kikugie.fletching-table") version "0.1.0-alpha.22" +} + +minecraft { + mappings("official", commonMod.mc) + + val at = rootProject.file("src/${loader}/resources/META-INF/accesstransformer.cfg") + if (at.exists()) { + //accessTransformer = at + } + + runs { + configureEach { + systemProperty("eventbus.api.strictRuntimeChecks", "true") + systemProperty("forge.enabledGameTestNamespaces", commonMod.id) + } + + register("client") { + workingDir = rootProject.file("runs/client") + } + + register("server") { + workingDir = rootProject.file("runs/server") + args("--nogui") + } + + register("gameTestServer") { + workingDir = rootProject.file("runs/gameTestServer") + } + + register("data") { + workingDir = rootProject.file("runs/data") + args("--mod", commonMod.id, "--all", "--output", rootProject.file("src/${loader}/generated/resources"), "--existing", rootProject.file("src/${loader}/resources")) + } + } +} + +sourceSets.main { + resources.srcDir("src/generated/resources") +} + +repositories { + minecraft.mavenizer(this) // In Kotlin, it = this + maven(fg.forgeMaven) + maven(fg.minecraftLibsMaven) +} + +dependencies { + implementation(minecraft.dependency("net.minecraftforge:forge:${commonMod.mc}-${commonMod.dep("forge")}")) +} \ No newline at end of file diff --git a/forge/gradle.properties b/forge/gradle.properties new file mode 100644 index 0000000..0079a24 --- /dev/null +++ b/forge/gradle.properties @@ -0,0 +1 @@ +loader=forge \ No newline at end of file diff --git a/forge/src/main/java/in/northwestw/autofish/AutoFishForge.java b/forge/src/main/java/in/northwestw/autofish/AutoFishForge.java new file mode 100644 index 0000000..b22a670 --- /dev/null +++ b/forge/src/main/java/in/northwestw/autofish/AutoFishForge.java @@ -0,0 +1,40 @@ +package in.northwestw.autofish; + +import in.northwestw.autofish.handler.AutoFishHandler; +import in.northwestw.autofish.keybind.KeyBinds; +import net.minecraftforge.client.event.InputEvent; +import net.minecraftforge.client.event.RegisterKeyMappingsEvent; +import net.minecraftforge.event.TickEvent; +import net.minecraftforge.eventbus.api.listener.SubscribeEvent; +import net.minecraftforge.fml.LogicalSide; +import net.minecraftforge.fml.common.Mod; + +@Mod(AutoFish.MOD_ID) +public class AutoFishForge { + + public AutoFishForge() { + } + + @Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD) + public static class ModEvents { + @SubscribeEvent + public static void registerKeyMappings(RegisterKeyMappingsEvent event) { + event.register(KeyBinds.autofish); + event.register(KeyBinds.rodprotect); + event.register(KeyBinds.autoreplace); + event.register(KeyBinds.settings); + event.register(KeyBinds.itemfilter); + } + + @SubscribeEvent + public static void inputKey(InputEvent.Key event) { + AutoFishHandler.onKeyInput(); + } + + @SubscribeEvent + public static void playerTickPre(TickEvent.PlayerTickEvent.Pre event) { + if (event.side() != LogicalSide.CLIENT) return; + AutoFishHandler.onPlayerTick(event.player()); + } + } +} \ No newline at end of file diff --git a/forge/src/main/resources/META-INF/mods.toml b/forge/src/main/resources/META-INF/mods.toml new file mode 100644 index 0000000..eed0909 --- /dev/null +++ b/forge/src/main/resources/META-INF/mods.toml @@ -0,0 +1,27 @@ +modLoader = "javafml" #mandatory +loaderVersion = "${forge_loader_version_range}" #mandatory This is typically bumped every Minecraft version by Forge. See https://files.minecraftforge.net/ for a list of versions. +license = "${license}" # Review your options at https://choosealicense.com/. +#issueTrackerURL="https://change.me.to.your.issue.tracker.example.invalid/" #optional +#clientSideOnly=true #optional +[[mods]] #mandatory +modId = "${mod_id}" #mandatory +version = "${version}" #mandatory +displayName = "${mod_name}" #mandatory +#updateJSONURL="https://change.me.example.invalid/updates.json" #optional (see https://mcforge.readthedocs.io/en/latest/gettingstarted/autoupdate/) +#displayURL="https://change.me.to.your.mods.homepage.example.invalid/" #optional (displayed in the mod UI) +logoFile = "${mod_id}.png" #optional +credits = "${credits}" #optional +authors = "${mod_author}" #optional +description = '''${description}''' #mandatory (Supports multiline text) +[[dependencies.${mod_id}]] #optional +modId = "forge" #mandatory +mandatory = true #mandatory +versionRange = "[${forge_version},)" #mandatory +ordering = "NONE" # The order that this dependency should load in relation to your mod, required to be either 'BEFORE' or 'AFTER' if the dependency is not mandatory +side = "BOTH" # Side this dependency is applied on - 'BOTH', 'CLIENT' or 'SERVER' +[[dependencies.${mod_id}]] +modId = "minecraft" +mandatory = true +versionRange = "${minecraft_version_range}" +ordering = "NONE" +side = "BOTH" \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index d7af91d..0d62d82 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,39 +1,47 @@ -# Project -version=8.0.0 -group=in.northwestw.in - -# Common -mod_name=AutoFish for Everyone -mod_author=NorthWestWind -mod_id=autofish -license=GPLv3 -credits= -description=I like playing survival, but fishing is a boring activity...\nTherefore, I made this mod!\nNow you can AFK Fish like no one else!\n\nNote that this is my first mod, so there might be bugs. - -# Gradle -org.gradle.jvmargs=-Xmx3G -org.gradle.daemon=false +# Dev +org.gradle.jvmargs=-Xmx2G + +# Mod - All field required. +mod.name=AutoFish for Everyone +mod.id=autofish +mod.group=in.northwestw.in +mod.version=8.0.0 +mod.author=NorthWestWind +mod.description=I like playing survival, but fishing is a boring activity...\nTherefore, I made this mod!\nNow you can AFK Fish like no one else! +mod.license=GPL-3.0 +mod.github= # Stonecutter -dev.kikugie.stonecutter.hard_mode=true - -# Minecraft and loader versions -java_version=25 -minecraft_version=26.2 -minecraft_version_range=[26.2, 26.3) -loader= -## This is the version of minecraft that the 'common' project uses, you can find a list of all versions here -## https://projects.neoforged.net/neoforged/neoform -neo_form_version=26.2-1 - -# Fabric, see https://fabricmc.net/develop/ for new versions -fabric_version=0.152.1+26.2 -fabric_loader_version=0.19.3 - -# Forge, see https://files.minecraftforge.net/net/minecraftforge/forge/ for new versions -forge_version=65.0.1 -forge_loader_version_range=[65,) - -# NeoForge, see https://projects.neoforged.net/neoforged/neoforge for new versions -neoforge_version=26.2.0.1-beta -neoforge_loader_version_range=[4,) +stonecutter_enabled_platforms=fabric, fabric-o, neoforge, forge +stonecutter_enabled_common_versions=26.2, 26.1.2, 1.21.11 +stonecutter_enabled_fabric_versions=26.2, 26.1.2 +stonecutter_enabled_fabric_o_versions=1.21.11 +stonecutter_enabled_forge_versions=26.2, 26.1.2, 1.21.11 +stonecutter_enabled_neoforge_versions=26.2, 26.1.2, 1.21.11 + +# The below field are intentionally left blank, +# to edit, please edit gradle.properties for each versions. + +# Java +java.version= + +# Minecraft +minecraft_version= +min_minecraft_version= + +# Mappings +deps.parchment= + +# Fabric https://fabricmc.net/versions.html +deps.fabric-loader= +deps.fabric-api= + +# Forge +deps.forge= + +# NeoForge https://projects.neoforged.net/neoforged/neoforge +deps.neoforge= +deps.neoform= + +# Dependencies +deps.modmenu= \ No newline at end of file diff --git a/neoforge/build.gradle b/neoforge/build.gradle deleted file mode 100644 index 7d983ed..0000000 --- a/neoforge/build.gradle +++ /dev/null @@ -1,47 +0,0 @@ -plugins { - id 'java-library' - id 'maven-publish' - id 'net.neoforged.moddev' version '2.0.141' -} - -loader = "neoforge" - -apply from: rootProject.file('gradle/shared.gradle') - -neoForge { - version = neoforge_version - - def at = rootProject.file("src/${loader}/resources/META-INF/accesstransformer.cfg") - if (at.exists()) { - accessTransformers.from(at.absolutePath) - } - - runs { - configureEach { - systemProperty('neoforge.enabledGameTestNamespaces', mod_id) - ideName = "NeoForge ${it.name.capitalize()} (${project.path})" - } - client { - client() - gameDirectory = rootProject.file('runs/client') - } - data { - clientData() - gameDirectory = rootProject.file('runs/data') - programArguments.addAll '--mod', project.mod_id, '--all', '--output', rootProject.file("src/${loader}/generated/resources").getAbsolutePath(), '--existing', rootProject.file("src/${loader}/resources").getAbsolutePath() - } - server { - server() - rootProject.file('runs/server').mkdirs() - gameDirectory = rootProject.file('runs/server') - } - } - - mods { - "${mod_id}" { - sourceSet sourceSets.main - } - } -} - -sourceSets.main.resources.srcDir rootProject.file("src/${loader}/generated/resources") diff --git a/neoforge/build.gradle.kts b/neoforge/build.gradle.kts new file mode 100644 index 0000000..867d9a4 --- /dev/null +++ b/neoforge/build.gradle.kts @@ -0,0 +1,38 @@ +plugins { + id("multiloader-loader") + id("net.neoforged.moddev") version "2.0.141" + kotlin("jvm") version "2.2.0" + id("com.google.devtools.ksp") version "2.2.0-2.0.2" +} + +neoForge { + version = commonMod.dep("neoforge") + + runs { + register("client") { + client() + ideName = "NeoForge Client (${project.path})" + } + register("server") { + server() + ideName = "NeoForge Server (${project.path})" + } + } + + commonMod.depOrNull("parchment")?.let { + parchment { + mappingsVersion = it + minecraftVersion = commonMod.mc + } + } + + mods { + register(commonMod.id) { + sourceSet(sourceSets.main.get()) + } + } +} + +sourceSets.main { + resources.srcDir("src/generated/resources") +} \ No newline at end of file diff --git a/neoforge/gradle.properties b/neoforge/gradle.properties new file mode 100644 index 0000000..cb4f12e --- /dev/null +++ b/neoforge/gradle.properties @@ -0,0 +1 @@ +loader=neoforge \ No newline at end of file diff --git a/neoforge/src/main/java/in/northwestw/autofish/AutoFishNeoForge.java b/neoforge/src/main/java/in/northwestw/autofish/AutoFishNeoForge.java new file mode 100644 index 0000000..a3f59d7 --- /dev/null +++ b/neoforge/src/main/java/in/northwestw/autofish/AutoFishNeoForge.java @@ -0,0 +1,44 @@ +package in.northwestw.autofish; + +import in.northwestw.autofish.handler.AutoFishHandler; +import in.northwestw.autofish.keybind.KeyBinds; +import net.minecraft.client.player.LocalPlayer; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.bus.api.IEventBus; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.LogicalSide; +import net.neoforged.fml.common.EventBusSubscriber; +import net.neoforged.fml.common.Mod; +import net.neoforged.neoforge.client.event.InputEvent; +import net.neoforged.neoforge.client.event.RegisterKeyMappingsEvent; +import net.neoforged.neoforge.event.tick.PlayerTickEvent; + +@Mod(AutoFish.MOD_ID) +public class AutoFishNeoForge { + + public AutoFishNeoForge(IEventBus eventBus) { + } + + @EventBusSubscriber + public static class ModEvents { + @SubscribeEvent + public static void registerKeyMappings(RegisterKeyMappingsEvent event) { + event.register(KeyBinds.autofish); + event.register(KeyBinds.rodprotect); + event.register(KeyBinds.autoreplace); + event.register(KeyBinds.settings); + event.register(KeyBinds.itemfilter); + } + + @SubscribeEvent + public static void inputKey(InputEvent.Key event) { + AutoFishHandler.onKeyInput(); + } + + @SubscribeEvent + public static void playerTickPre(PlayerTickEvent.Pre event) { + if (!(event.getEntity() instanceof LocalPlayer)) return; + AutoFishHandler.onPlayerTick(event.getEntity()); + } + } +} \ No newline at end of file diff --git a/neoforge/src/main/resources/META-INF/neoforge.mods.toml b/neoforge/src/main/resources/META-INF/neoforge.mods.toml new file mode 100644 index 0000000..fb9fd48 --- /dev/null +++ b/neoforge/src/main/resources/META-INF/neoforge.mods.toml @@ -0,0 +1,32 @@ +modLoader = "javafml" #mandatory +loaderVersion = "${neoforge_loader_version_range}" #mandatory +license = "${license}" # Review your options at https://choosealicense.com/. +#issueTrackerURL="https://change.me.to.your.issue.tracker.example.invalid/" #optional +[[mods]] #mandatory +modId = "${mod_id}" #mandatory +version = "${version}" #mandatory +displayName = "${mod_name}" #mandatory +#updateJSONURL="https://change.me.example.invalid/updates.json" #optional (see https://docs.neoforged.net/docs/misc/updatechecker/) +#displayURL="https://change.me.to.your.mods.homepage.example.invalid/" #optional (displayed in the mod UI) +logoFile="${mod_id}.png" #optional +credits="${credits}" #optional +authors = "${mod_author}" #optional +description = '''${description}''' #mandatory (Supports multiline text) +[[dependencies.${mod_id}]] #optional +modId = "neoforge" #mandatory +type="required" #mandatory (Can be one of "required", "optional", "incompatible" or "discouraged") +versionRange = "[${neoforge_version},)" #mandatory +ordering = "NONE" # The order that this dependency should load in relation to your mod, required to be either 'BEFORE' or 'AFTER' if the dependency is not mandatory +side = "BOTH" # Side this dependency is applied on - 'BOTH', 'CLIENT' or 'SERVER' +[[dependencies.${mod_id}]] +modId = "minecraft" +type="required" #mandatory (Can be one of "required", "optional", "incompatible" or "discouraged") +versionRange = "${minecraft_version_range}" +ordering = "NONE" +side = "BOTH" + +# Features are specific properties of the game environment, that you may want to declare you require. This example declares +# that your mod requires GL version 3.2 or higher. Other features will be added. They are side aware so declaring this won't +# stop your mod loading on the server for example. +#[features.${mod_id}] +#openGLVersion="[3.2,)" diff --git a/settings.gradle b/settings.gradle deleted file mode 100644 index 7fe5844..0000000 --- a/settings.gradle +++ /dev/null @@ -1,93 +0,0 @@ -pluginManagement { - repositories { - gradlePluginPortal() - mavenCentral() - exclusiveContent { - forRepository { - maven { - name = 'Fabric' - url = uri('https://maven.fabricmc.net') - } - } - filter { - includeGroupAndSubgroups('net.fabricmc') - includeGroup('fabric-loom') - } - } - exclusiveContent { - forRepository { - maven { - name = 'Sponge' - url = uri('https://repo.spongepowered.org/repository/maven-public') - } - } - filter { - includeGroupAndSubgroups("org.spongepowered") - } - } - exclusiveContent { - forRepository { - maven { - name = 'Forge' - url = uri('https://maven.minecraftforge.net') - } - } - filter { - includeGroupAndSubgroups('net.minecraftforge') - } - } - exclusiveContent { - forRepository { - maven { - name = 'NeoForge' - url = uri('https://maven.neoforged.net/releases') - } - } - filter { - includeGroupAndSubgroups('net.neoforged') - } - } - exclusiveContent { - forRepository { - maven { - name = 'ParchmentMC' - url = 'https://maven.parchmentmc.org' - } - } - filter { - includeGroupAndSubgroups('org.parchmentmc') - } - } - maven { - name = 'Stonecutter' - url = uri('https://maven.kikugie.dev/releases') - } - } -} - -plugins { - id 'org.gradle.toolchains.foojay-resolver-convention' version '1.0.0' - id 'dev.kikugie.stonecutter' version '0.9.6' -} - -rootProject.name = 'forge-autofish' - -stonecutter.create(rootProject) { - kotlinController = false - centralScript = "build.gradle" - versions("26.2", "26.1.2", "1.21.11") - vcsVersion = "26.2" - - branch("fabric") { - versions("26.2", "26.1.2") - } - branch("fabric-o") { - versions("1.21.11") - } - branch("forge") { - versions("26.2", "26.1.2", "1.21.11") - } - branch("neoforge") { - versions("26.2", "26.1.2", "1.21.11") - } -} diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..280efd3 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,52 @@ +val isCi = System.getenv("CI") == "true" +gradle.startParameter.isParallelProjectExecutionEnabled = !isCi +gradle.startParameter.isBuildCacheEnabled = !isCi +gradle.startParameter.isConfigureOnDemand = !isCi + +pluginManagement { + repositories { + gradlePluginPortal() + mavenCentral() + maven("https://maven.fabricmc.net/") + maven("https://maven.neoforged.net/releases/") + maven("https://maven.minecraftforge.net") + maven("https://maven.kikugie.dev/snapshots") + maven("https://maven.kikugie.dev/releases") + } +} + +plugins { + id("dev.kikugie.stonecutter") version "0.9" + id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0" +} + +val commonVersions = providers.gradleProperty("stonecutter_enabled_common_versions").orNull?.split(",")?.map { it.trim() } ?: emptyList() +val fabricVersions = providers.gradleProperty("stonecutter_enabled_fabric_versions").orNull?.split(",")?.map { it.trim() } ?: emptyList() +val fabricOVersions = providers.gradleProperty("stonecutter_enabled_fabric_o_versions").orNull?.split(",")?.map { it.trim() } ?: emptyList() +val forgeVersions = providers.gradleProperty("stonecutter_enabled_forge_versions").orNull?.split(",")?.map { it.trim() } ?: emptyList() +val neoforgeVersions = providers.gradleProperty("stonecutter_enabled_neoforge_versions").orNull?.split(",")?.map { it.trim() } ?: emptyList() +val dists = mapOf( + "common" to commonVersions, + "forge" to forgeVersions, + "fabric" to fabricVersions, + "fabric-o" to fabricOVersions, + "neoforge" to neoforgeVersions +) +val uniqueVersions = dists.values.flatten().distinct() + +stonecutter { + kotlinController = true + centralScript = "build.gradle.kts" + + create(rootProject) { + versions(*uniqueVersions.toTypedArray()) + + dists.forEach { (branchName, branchVersions) -> + branch(branchName) { + versions(*branchVersions.toTypedArray()) + } + } + } +} + +rootProject.name = "autofish" \ No newline at end of file diff --git a/stonecutter.gradle b/stonecutter.gradle deleted file mode 100644 index 1713427..0000000 --- a/stonecutter.gradle +++ /dev/null @@ -1,4 +0,0 @@ -plugins { - id "dev.kikugie.stonecutter" -} -stonecutter.active "26.2" \ No newline at end of file diff --git a/stonecutter.gradle.kts b/stonecutter.gradle.kts new file mode 100644 index 0000000..909446f --- /dev/null +++ b/stonecutter.gradle.kts @@ -0,0 +1,4 @@ +plugins { + id("dev.kikugie.stonecutter") +} +stonecutter active "26.2" \ No newline at end of file diff --git a/versions/1.21.11/gradle.properties b/versions/1.21.11/gradle.properties index 1147761..8778e61 100644 --- a/versions/1.21.11/gradle.properties +++ b/versions/1.21.11/gradle.properties @@ -1,22 +1,25 @@ -# Minecraft and loader versions -java_version=21 +# Stonecutter +stonecutter_enabled_platforms=fabric-o, forge, neoforge + +# Java +java.version=21 + +# Minecraft minecraft_version=1.21.11 -minecraft_version_range=[1.21.11, 1.22) -## This is the version of minecraft that the 'common' project uses, you can find a list of all versions here -## https://projects.neoforged.net/neoforged/neoform -neo_form_version=1.21.11-20251209.172050 -# The version of ParchmentMC that is used, see https://parchmentmc.org/docs/getting-started#choose-a-version for new versions -parchment_minecraft=1.21.11 -parchment_version=2025.12.20 +min_minecraft_version=1.21.11 + +# Mappings +deps.parchment=2025.12.20 + +# Fabric +deps.fabric_loader=0.19.3 +deps.fabric_api=0.141.4 -# Fabric, see https://fabricmc.net/develop/ for new versions -fabric_version=0.141.4+1.21.11 -fabric_loader_version=0.19.3 +# NeoForge +deps.neoforge=21.11.42 +deps.neoform=1.21.11-20251209.172050 -# Forge, see https://files.minecraftforge.net/net/minecraftforge/forge/ for new versions -forge_version=61.1.8 -forge_loader_version_range=[61,) +deps.forge=61.1.8 -# NeoForge, see https://projects.neoforged.net/neoforged/neoforge for new versions -neoforge_version=21.11.42 -neoforge_loader_version_range=[4,) +# Dependencies +deps.modmenu= \ No newline at end of file diff --git a/versions/26.1.2/gradle.properties b/versions/26.1.2/gradle.properties index 44874f6..fbb8ad7 100644 --- a/versions/26.1.2/gradle.properties +++ b/versions/26.1.2/gradle.properties @@ -1,19 +1,25 @@ -# Minecraft and loader versions -java_version=25 +# Stonecutter +stonecutter_enabled_platforms=fabric, forge, neoforge + +# Java +java.version=25 + +# Minecraft minecraft_version=26.1.2 -minecraft_version_range=[26.1.2, 26.2) -## This is the version of minecraft that the 'common' project uses, you can find a list of all versions here -## https://projects.neoforged.net/neoforged/neoform -neo_form_version=26.1.2-1 +min_minecraft_version=26.1 + +# Mappings +deps.parchment= + +# Fabric +deps.fabric_loader=0.19.3 +deps.fabric_api=0.152.1 -# Fabric, see https://fabricmc.net/develop/ for new versions -fabric_version=0.152.1+26.1.2 -fabric_loader_version=0.19.3 +# NeoForge +deps.neoforge=26.1.2.0-beta +deps.neoform=26.1.2-1 -# Forge, see https://files.minecraftforge.net/net/minecraftforge/forge/ for new versions -forge_version=64.0.10 -forge_loader_version_range=[64,) +deps.forge=64.0.10 -# NeoForge, see https://projects.neoforged.net/neoforged/neoforge for new versions -neoforge_version=26.1.2.0-beta -neoforge_loader_version_range=[4,) +# Dependencies +deps.modmenu= diff --git a/versions/26.2/gradle.properties b/versions/26.2/gradle.properties new file mode 100644 index 0000000..c5c2c2e --- /dev/null +++ b/versions/26.2/gradle.properties @@ -0,0 +1,25 @@ +# Stonecutter +stonecutter_enabled_platforms=fabric, forge, neoforge + +# Java +java.version=25 + +# Minecraft +minecraft_version=26.2 +min_minecraft_version=26.2 + +# Mappings +deps.parchment= + +# Fabric +deps.fabric_loader=0.19.3 +deps.fabric_api=0.152.1 + +# NeoForge +deps.neoforge=26.2.0.1-beta +deps.neoform=26.2-1 + +deps.forge=65.0.1 + +# Dependencies +deps.modmenu= \ No newline at end of file From d1aefaf75121755236702d0e027fc9edb138dc9e Mon Sep 17 00:00:00 2001 From: North-West-Wind Date: Mon, 29 Jun 2026 23:25:01 +0800 Subject: [PATCH 13/52] refactor: steal Faboslav's setup for Fabric --- .../src/main/kotlin/FabricLoomCompatPlugin.kt | 61 +++++++++++++++++++ .../fabric-loom-compat.properties | 1 + fabric-o/build.gradle.kts | 39 ------------ fabric-o/gradle.properties | 1 - .../northwestw/autofish/AutoFishFabric.java | 24 -------- fabric-o/src/main/resources/fabric.mod.json | 32 ---------- fabric/build.gradle.kts | 24 ++++++-- forge/build.gradle.kts | 1 - gradle.properties | 5 +- settings.gradle.kts | 2 - stonecutter.gradle.kts | 3 + 11 files changed, 85 insertions(+), 108 deletions(-) create mode 100644 buildSrc/src/main/kotlin/FabricLoomCompatPlugin.kt create mode 100644 buildSrc/src/main/resources/META-INF/gradle-plugins/fabric-loom-compat.properties delete mode 100644 fabric-o/build.gradle.kts delete mode 100644 fabric-o/gradle.properties delete mode 100644 fabric-o/src/main/java/in/northwestw/autofish/AutoFishFabric.java delete mode 100644 fabric-o/src/main/resources/fabric.mod.json diff --git a/buildSrc/src/main/kotlin/FabricLoomCompatPlugin.kt b/buildSrc/src/main/kotlin/FabricLoomCompatPlugin.kt new file mode 100644 index 0000000..ea5ea1b --- /dev/null +++ b/buildSrc/src/main/kotlin/FabricLoomCompatPlugin.kt @@ -0,0 +1,61 @@ +// Stolen from Faboslav +// https://github.com/Faboslav/friends-and-foes/blob/master/buildSrc/src/main/kotlin/FabricLoomCompatPlugin.kt + +import dev.kikugie.stonecutter.build.StonecutterBuildExtension +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.tasks.TaskProvider +import org.gradle.jvm.tasks.Jar +import org.gradle.kotlin.dsl.create +import org.gradle.kotlin.dsl.the +import org.gradle.kotlin.dsl.named + +open class FabricLoomCompatPlugin : Plugin { + override fun apply(target: Project): Unit = with(target) { + val current = the().current.parsed + + if (current > "26.0") { + setupNewLoomFacade() + } else { + setupOldLoomFacade() + } + + extensions.create("fabric", this, current > "26.0") + } + + private fun Project.setupNewLoomFacade() { + plugins.apply("net.fabricmc.fabric-loom") + + val names = listOf( + "api", "implementation", "compileOnly", "runtimeOnly", "localRuntime" + ) + + for (baseName in names) { + val loomified = "mod" + baseName.replaceFirstChar(Char::uppercaseChar) + val modConfiguration = configurations.findByName(loomified) ?: configurations.create(loomified) + + configurations.getByName(baseName).extendsFrom(modConfiguration) + } + + configurations.findByName("mappings") ?: configurations.register("mappings") { + isCanBeResolved = false + isCanBeConsumed = false + } + } + + private fun Project.setupOldLoomFacade() { + plugins.apply("net.fabricmc.fabric-loom-remap") + } + + open class FabricExtensions(val project: Project, val isNew: Boolean) { + val modJar: TaskProvider by lazy { + if (isNew) project.tasks.named("jar") + else project.tasks.named("remapJar") + } + + val modSourcesJar: TaskProvider by lazy { + if (isNew) project.tasks.named("sourcesJar") + else project.tasks.named("remapSourcesJar") + } + } +} \ No newline at end of file diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/fabric-loom-compat.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/fabric-loom-compat.properties new file mode 100644 index 0000000..bd727b9 --- /dev/null +++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/fabric-loom-compat.properties @@ -0,0 +1 @@ +implementation-class=FabricLoomCompatPlugin \ No newline at end of file diff --git a/fabric-o/build.gradle.kts b/fabric-o/build.gradle.kts deleted file mode 100644 index 8dc0cd3..0000000 --- a/fabric-o/build.gradle.kts +++ /dev/null @@ -1,39 +0,0 @@ -plugins { - id("multiloader-loader") - id("fabric-loom") version "1.11-SNAPSHOT" - kotlin("jvm") version "2.2.0" - id("com.google.devtools.ksp") version "2.2.0-2.0.2" - id("dev.kikugie.fletching-table.fabric") version "0.1.0-alpha.22" -} - -stonecutter { - -} - -dependencies { - minecraft("com.mojang:minecraft:${commonMod.mc}") - mappings(loom.layered { - officialMojangMappings() - commonMod.depOrNull("parchment")?.let { parchmentVersion -> - parchment("org.parchmentmc.data:parchment-${commonMod.mc}:$parchmentVersion@zip") - } - }) - - implementation("net.fabricmc:fabric-loader:${commonMod.dep("fabric_loader")}") - implementation("net.fabricmc.fabric-api:fabric-api:${commonMod.dep("fabric_api")}+${commonMod.mc}") -} - -loom { - runs { - getByName("client") { - client() - configName = "Fabric Client" - ideConfigGenerated(true) - } - getByName("server") { - server() - configName = "Fabric Server" - ideConfigGenerated(true) - } - } -} \ No newline at end of file diff --git a/fabric-o/gradle.properties b/fabric-o/gradle.properties deleted file mode 100644 index fcdea26..0000000 --- a/fabric-o/gradle.properties +++ /dev/null @@ -1 +0,0 @@ -loader=fabric \ No newline at end of file diff --git a/fabric-o/src/main/java/in/northwestw/autofish/AutoFishFabric.java b/fabric-o/src/main/java/in/northwestw/autofish/AutoFishFabric.java deleted file mode 100644 index 0f2bb8f..0000000 --- a/fabric-o/src/main/java/in/northwestw/autofish/AutoFishFabric.java +++ /dev/null @@ -1,24 +0,0 @@ -package in.northwestw.autofish; - -import in.northwestw.autofish.handler.AutoFishHandler; -import in.northwestw.autofish.keybind.KeyBinds; -import net.fabricmc.api.ModInitializer; -import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; -import net.fabricmc.fabric.api.client.keymapping.v1.KeyMappingHelper; - -public class AutoFishFabric implements ModInitializer { - - @Override - public void onInitialize() { - KeyMappingHelper.registerKeyMapping(KeyBinds.autofish); - KeyMappingHelper.registerKeyMapping(KeyBinds.rodprotect); - KeyMappingHelper.registerKeyMapping(KeyBinds.autoreplace); - KeyMappingHelper.registerKeyMapping(KeyBinds.settings); - KeyMappingHelper.registerKeyMapping(KeyBinds.itemfilter); - - ClientTickEvents.END_CLIENT_TICK.register(_ -> AutoFishHandler.onKeyInput()); - ClientTickEvents.START_CLIENT_TICK.register(client -> { - AutoFishHandler.onPlayerTick(client.player); - }); - } -} diff --git a/fabric-o/src/main/resources/fabric.mod.json b/fabric-o/src/main/resources/fabric.mod.json deleted file mode 100644 index d20efe4..0000000 --- a/fabric-o/src/main/resources/fabric.mod.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "schemaVersion": 1, - "id": "${mod_id}", - "version": "${version}", - "name": "${mod_name}", - "description": "${description}", - "authors": [ - "${mod_author}" - ], - "contact": { - "homepage": "https://fabricmc.net/", - "sources": "https://github.com/FabricMC/fabric-example-mod" - }, - "license": "${license}", - "icon": "${mod_id}.png", - "environment": "*", - "entrypoints": { - "main": [ - "in.northwestw.autofish.AutoFishFabric" - ] - }, - "depends": { - "fabricloader": ">=${fabric_loader_version}", - "fabric-api": "*", - "minecraft": "~${minecraft_version}", - "java": ">=${java_version}" - }, - "suggests": { - "another-mod": "*" - } -} - \ No newline at end of file diff --git a/fabric/build.gradle.kts b/fabric/build.gradle.kts index 0144f57..57da1bb 100644 --- a/fabric/build.gradle.kts +++ b/fabric/build.gradle.kts @@ -1,17 +1,22 @@ plugins { id("multiloader-loader") - id("net.fabricmc.fabric-loom") version "1.16.3" + id("fabric-loom-compat") kotlin("jvm") version "2.2.0" id("com.google.devtools.ksp") version "2.2.0-2.0.2" - id("dev.kikugie.fletching-table.fabric") version "0.1.0-alpha.22" -} - -stonecutter { - } dependencies { minecraft("com.mojang:minecraft:${commonMod.mc}") + + if (stonecutter.eval(commonMod.mc, "<=1.21.11")) { + mappings(loom.layered { + officialMojangMappings() + commonMod.depOrNull("parchment")?.let { parchmentVersion -> + parchment("org.parchmentmc.data:parchment-${commonMod.mc}:$parchmentVersion@zip") + } + }) + } + implementation("net.fabricmc:fabric-loader:${commonMod.dep("fabric_loader")}") implementation("net.fabricmc.fabric-api:fabric-api:${commonMod.dep("fabric_api")}+${commonMod.mc}") } @@ -29,4 +34,11 @@ loom { ideConfigGenerated(true) } } + + if (stonecutter.eval(commonMod.mc, "<=1.21.11")) { + mixin { + useLegacyMixinAp = true + defaultRefmapName = "${mod.id}.refmap.json" + } + } } \ No newline at end of file diff --git a/forge/build.gradle.kts b/forge/build.gradle.kts index 6d4e09a..eed5199 100644 --- a/forge/build.gradle.kts +++ b/forge/build.gradle.kts @@ -3,7 +3,6 @@ plugins { id("net.minecraftforge.gradle") version "[7.0.17,8)" kotlin("jvm") version "2.2.0" id("com.google.devtools.ksp") version "2.2.0-2.0.2" - id("dev.kikugie.fletching-table") version "0.1.0-alpha.22" } minecraft { diff --git a/gradle.properties b/gradle.properties index 0d62d82..303204c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -12,10 +12,9 @@ mod.license=GPL-3.0 mod.github= # Stonecutter -stonecutter_enabled_platforms=fabric, fabric-o, neoforge, forge +stonecutter_enabled_platforms=fabric, neoforge, forge stonecutter_enabled_common_versions=26.2, 26.1.2, 1.21.11 -stonecutter_enabled_fabric_versions=26.2, 26.1.2 -stonecutter_enabled_fabric_o_versions=1.21.11 +stonecutter_enabled_fabric_versions=26.2, 26.1.2, 1.21.11 stonecutter_enabled_forge_versions=26.2, 26.1.2, 1.21.11 stonecutter_enabled_neoforge_versions=26.2, 26.1.2, 1.21.11 diff --git a/settings.gradle.kts b/settings.gradle.kts index 280efd3..581899c 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -22,14 +22,12 @@ plugins { val commonVersions = providers.gradleProperty("stonecutter_enabled_common_versions").orNull?.split(",")?.map { it.trim() } ?: emptyList() val fabricVersions = providers.gradleProperty("stonecutter_enabled_fabric_versions").orNull?.split(",")?.map { it.trim() } ?: emptyList() -val fabricOVersions = providers.gradleProperty("stonecutter_enabled_fabric_o_versions").orNull?.split(",")?.map { it.trim() } ?: emptyList() val forgeVersions = providers.gradleProperty("stonecutter_enabled_forge_versions").orNull?.split(",")?.map { it.trim() } ?: emptyList() val neoforgeVersions = providers.gradleProperty("stonecutter_enabled_neoforge_versions").orNull?.split(",")?.map { it.trim() } ?: emptyList() val dists = mapOf( "common" to commonVersions, "forge" to forgeVersions, "fabric" to fabricVersions, - "fabric-o" to fabricOVersions, "neoforge" to neoforgeVersions ) val uniqueVersions = dists.values.flatten().distinct() diff --git a/stonecutter.gradle.kts b/stonecutter.gradle.kts index 909446f..2db553e 100644 --- a/stonecutter.gradle.kts +++ b/stonecutter.gradle.kts @@ -1,4 +1,7 @@ plugins { id("dev.kikugie.stonecutter") + id("net.neoforged.moddev") version "2.0.141" apply false + id("net.fabricmc.fabric-loom") version "1.17-SNAPSHOT" apply false + id("net.fabricmc.fabric-loom-remap") version "1.17-SNAPSHOT" apply false } stonecutter active "26.2" \ No newline at end of file From 5ef73b06aad292467f89823c003d57e42e62dd59 Mon Sep 17 00:00:00 2001 From: North-West-Wind Date: Tue, 30 Jun 2026 07:05:11 +0800 Subject: [PATCH 14/52] fix: fabric building --- build.gradle.kts | 4 ++++ buildSrc/build.gradle.kts | 2 +- fabric/build.gradle.kts | 4 ++-- .../northwestw/autofish/AutoFishFabric.java | 20 +++++++++++++++---- settings.gradle.kts | 2 +- 5 files changed, 24 insertions(+), 8 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index e69de29..5d0afb8 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -0,0 +1,4 @@ +// Disable building for versions, only for branches (fabric, forge, neoforge, common). +tasks.matching { it.name == "build" || it.name.startsWith("compile") || it.name == "classes" || it.name == "jar" || it.name == "javadoc" }.configureEach { + enabled = false +} diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 638afe8..05996d6 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -11,5 +11,5 @@ repositories { dependencies { fun plugin(id: String, version: String) = "$id:$id.gradle.plugin:$version" - implementation("dev.kikugie:stonecutter:0.9") + implementation("dev.kikugie:stonecutter:0.9.5") } \ No newline at end of file diff --git a/fabric/build.gradle.kts b/fabric/build.gradle.kts index 57da1bb..3e119b6 100644 --- a/fabric/build.gradle.kts +++ b/fabric/build.gradle.kts @@ -17,8 +17,8 @@ dependencies { }) } - implementation("net.fabricmc:fabric-loader:${commonMod.dep("fabric_loader")}") - implementation("net.fabricmc.fabric-api:fabric-api:${commonMod.dep("fabric_api")}+${commonMod.mc}") + modImplementation("net.fabricmc:fabric-loader:${commonMod.dep("fabric_loader")}") + modApi("net.fabricmc.fabric-api:fabric-api:${commonMod.dep("fabric_api")}+${commonMod.mc}") } loom { diff --git a/fabric/src/main/java/in/northwestw/autofish/AutoFishFabric.java b/fabric/src/main/java/in/northwestw/autofish/AutoFishFabric.java index 0f2bb8f..4f86b2a 100644 --- a/fabric/src/main/java/in/northwestw/autofish/AutoFishFabric.java +++ b/fabric/src/main/java/in/northwestw/autofish/AutoFishFabric.java @@ -4,21 +4,33 @@ import in.northwestw.autofish.keybind.KeyBinds; import net.fabricmc.api.ModInitializer; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; +//? if >=26.1 { import net.fabricmc.fabric.api.client.keymapping.v1.KeyMappingHelper; +//? } else +//import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; public class AutoFishFabric implements ModInitializer { @Override public void onInitialize() { + //? if >=26.1 { KeyMappingHelper.registerKeyMapping(KeyBinds.autofish); KeyMappingHelper.registerKeyMapping(KeyBinds.rodprotect); KeyMappingHelper.registerKeyMapping(KeyBinds.autoreplace); KeyMappingHelper.registerKeyMapping(KeyBinds.settings); KeyMappingHelper.registerKeyMapping(KeyBinds.itemfilter); + //? } else { + /*KeyBindingHelper.registerKeyBinding(KeyBinds.autofish); + KeyBindingHelper.registerKeyBinding(KeyBinds.rodprotect); + KeyBindingHelper.registerKeyBinding(KeyBinds.autoreplace); + KeyBindingHelper.registerKeyBinding(KeyBinds.settings); + KeyBindingHelper.registerKeyBinding(KeyBinds.itemfilter); + *///? } - ClientTickEvents.END_CLIENT_TICK.register(_ -> AutoFishHandler.onKeyInput()); - ClientTickEvents.START_CLIENT_TICK.register(client -> { - AutoFishHandler.onPlayerTick(client.player); - }); + ClientTickEvents.END_CLIENT_TICK.register(client -> AutoFishHandler.onKeyInput()); + //? if >=26.1 { + ClientTickEvents.START_CLIENT_TICK.register(client -> AutoFishHandler.onPlayerTick(client.player)); + //? } else + //ClientTickEvents.START_CLIENT_TICK.register(client -> AutoFishHandler.onPlayerTick(client.player)); } } diff --git a/settings.gradle.kts b/settings.gradle.kts index 581899c..620cfc0 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -16,7 +16,7 @@ pluginManagement { } plugins { - id("dev.kikugie.stonecutter") version "0.9" + id("dev.kikugie.stonecutter") version "0.9.5" id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0" } From 9e75c9152b17f71327ec18ddb6a7b293bbb6cc9c Mon Sep 17 00:00:00 2001 From: North-West-Wind Date: Tue, 30 Jun 2026 10:29:52 +0800 Subject: [PATCH 15/52] fix: template names --- .../main/kotlin/multiloader-common.gradle.kts | 6 ++-- fabric/src/main/resources/fabric.mod.json | 28 ++++++++-------- forge/src/main/resources/META-INF/mods.toml | 26 +++++++-------- gradle.properties | 6 ++-- .../resources/META-INF/neoforge.mods.toml | 32 ++++++++----------- 5 files changed, 45 insertions(+), 53 deletions(-) diff --git a/buildSrc/src/main/kotlin/multiloader-common.gradle.kts b/buildSrc/src/main/kotlin/multiloader-common.gradle.kts index 09f1186..79df3de 100644 --- a/buildSrc/src/main/kotlin/multiloader-common.gradle.kts +++ b/buildSrc/src/main/kotlin/multiloader-common.gradle.kts @@ -1,6 +1,6 @@ plugins { id("java") - //id("idea") + //Cid("idea") id("java-library") } @@ -60,8 +60,8 @@ tasks { "modGitHub" to commonMod.github, "minecraftVersion" to commonMod.propOrNull("minecraft_version"), "minMinecraftVersion" to commonMod.propOrNull("min_minecraft_version"), - "fabricLoaderVersion" to commonMod.depOrNull("fabric-loader"), - "fabricApiVersion" to commonMod.depOrNull("fabric-api"), + "fabricLoaderVersion" to commonMod.depOrNull("fabric_loader"), + "fabricApiVersion" to commonMod.depOrNull("fabric_api"), "neoForgeVersion" to commonMod.depOrNull("neoforge"), "forgeVersion" to commonMod.depOrNull("forge"), // "yaclVersion" to commonMod.depOrNull("yacl"), diff --git a/fabric/src/main/resources/fabric.mod.json b/fabric/src/main/resources/fabric.mod.json index d20efe4..fcb5f06 100644 --- a/fabric/src/main/resources/fabric.mod.json +++ b/fabric/src/main/resources/fabric.mod.json @@ -1,18 +1,19 @@ { "schemaVersion": 1, - "id": "${mod_id}", - "version": "${version}", - "name": "${mod_name}", - "description": "${description}", + "id": "${modId}", + "version": "${modVersion}", + "name": "${modName}", + "description": "${modDescription}", "authors": [ - "${mod_author}" + "${modAuthor}" ], "contact": { - "homepage": "https://fabricmc.net/", - "sources": "https://github.com/FabricMC/fabric-example-mod" + "homepage": "${modGitHub}", + "sources": "${modGitHub}", + "issues": "${modGitHub}/issues" }, - "license": "${license}", - "icon": "${mod_id}.png", + "license": "${modLicense}", + "icon": "${modId}.png", "environment": "*", "entrypoints": { "main": [ @@ -20,13 +21,10 @@ ] }, "depends": { - "fabricloader": ">=${fabric_loader_version}", + "fabricloader": ">=${fabricLoaderVersion}", "fabric-api": "*", - "minecraft": "~${minecraft_version}", - "java": ">=${java_version}" - }, - "suggests": { - "another-mod": "*" + "minecraft": ">=${minMinecraftVersion}", + "java": ">=${javaVersion}" } } \ No newline at end of file diff --git a/forge/src/main/resources/META-INF/mods.toml b/forge/src/main/resources/META-INF/mods.toml index eed0909..8f81f49 100644 --- a/forge/src/main/resources/META-INF/mods.toml +++ b/forge/src/main/resources/META-INF/mods.toml @@ -1,27 +1,27 @@ modLoader = "javafml" #mandatory -loaderVersion = "${forge_loader_version_range}" #mandatory This is typically bumped every Minecraft version by Forge. See https://files.minecraftforge.net/ for a list of versions. -license = "${license}" # Review your options at https://choosealicense.com/. +loaderVersion = "*" #mandatory This is typically bumped every Minecraft version by Forge. See https://files.minecraftforge.net/ for a list of versions. +license = "${modLicense}" # Review your options at https://choosealicense.com/. #issueTrackerURL="https://change.me.to.your.issue.tracker.example.invalid/" #optional #clientSideOnly=true #optional [[mods]] #mandatory -modId = "${mod_id}" #mandatory -version = "${version}" #mandatory -displayName = "${mod_name}" #mandatory +modId = "${modId}" #mandatory +version = "${modVersion}" #mandatory +displayName = "${modName}" #mandatory #updateJSONURL="https://change.me.example.invalid/updates.json" #optional (see https://mcforge.readthedocs.io/en/latest/gettingstarted/autoupdate/) #displayURL="https://change.me.to.your.mods.homepage.example.invalid/" #optional (displayed in the mod UI) -logoFile = "${mod_id}.png" #optional -credits = "${credits}" #optional -authors = "${mod_author}" #optional -description = '''${description}''' #mandatory (Supports multiline text) -[[dependencies.${mod_id}]] #optional +logoFile = "${modId}.png" #optional +credits = "" #optional +authors = "${modAuthor}" #optional +description = '''${modDescription}''' #mandatory (Supports multiline text) +[[dependencies.${modId}]] #optional modId = "forge" #mandatory mandatory = true #mandatory -versionRange = "[${forge_version},)" #mandatory +versionRange = "[${forgeVersion},)" #mandatory ordering = "NONE" # The order that this dependency should load in relation to your mod, required to be either 'BEFORE' or 'AFTER' if the dependency is not mandatory side = "BOTH" # Side this dependency is applied on - 'BOTH', 'CLIENT' or 'SERVER' -[[dependencies.${mod_id}]] +[[dependencies.${modId}]] modId = "minecraft" mandatory = true -versionRange = "${minecraft_version_range}" +versionRange = "[${minMinecraftVersion},)" ordering = "NONE" side = "BOTH" \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 303204c..1a28ae3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,7 +9,7 @@ mod.version=8.0.0 mod.author=NorthWestWind mod.description=I like playing survival, but fishing is a boring activity...\nTherefore, I made this mod!\nNow you can AFK Fish like no one else! mod.license=GPL-3.0 -mod.github= +mod.github=https://github.com/North-West-Wind/forge-autofish # Stonecutter stonecutter_enabled_platforms=fabric, neoforge, forge @@ -32,8 +32,8 @@ min_minecraft_version= deps.parchment= # Fabric https://fabricmc.net/versions.html -deps.fabric-loader= -deps.fabric-api= +deps.fabric_loader= +deps.fabric_api= # Forge deps.forge= diff --git a/neoforge/src/main/resources/META-INF/neoforge.mods.toml b/neoforge/src/main/resources/META-INF/neoforge.mods.toml index fb9fd48..3a018c3 100644 --- a/neoforge/src/main/resources/META-INF/neoforge.mods.toml +++ b/neoforge/src/main/resources/META-INF/neoforge.mods.toml @@ -1,32 +1,26 @@ modLoader = "javafml" #mandatory -loaderVersion = "${neoforge_loader_version_range}" #mandatory -license = "${license}" # Review your options at https://choosealicense.com/. +loaderVersion = "*" #mandatory +license = "${modLicense}" # Review your options at https://choosealicense.com/. #issueTrackerURL="https://change.me.to.your.issue.tracker.example.invalid/" #optional [[mods]] #mandatory -modId = "${mod_id}" #mandatory -version = "${version}" #mandatory -displayName = "${mod_name}" #mandatory +modId = "${modId}" #mandatory +version = "${modVersion}" #mandatory +displayName = "${modName}" #mandatory #updateJSONURL="https://change.me.example.invalid/updates.json" #optional (see https://docs.neoforged.net/docs/misc/updatechecker/) #displayURL="https://change.me.to.your.mods.homepage.example.invalid/" #optional (displayed in the mod UI) -logoFile="${mod_id}.png" #optional -credits="${credits}" #optional -authors = "${mod_author}" #optional -description = '''${description}''' #mandatory (Supports multiline text) -[[dependencies.${mod_id}]] #optional +logoFile="${modId}.png" #optional +credits="" #optional +authors = "${modAuthor}" #optional +description = '''${modDescription}''' #mandatory (Supports multiline text) +[[dependencies.${modId}]] #optional modId = "neoforge" #mandatory type="required" #mandatory (Can be one of "required", "optional", "incompatible" or "discouraged") -versionRange = "[${neoforge_version},)" #mandatory +versionRange = "[${neoForgeVersion},)" #mandatory ordering = "NONE" # The order that this dependency should load in relation to your mod, required to be either 'BEFORE' or 'AFTER' if the dependency is not mandatory side = "BOTH" # Side this dependency is applied on - 'BOTH', 'CLIENT' or 'SERVER' -[[dependencies.${mod_id}]] +[[dependencies.${modId}]] modId = "minecraft" type="required" #mandatory (Can be one of "required", "optional", "incompatible" or "discouraged") -versionRange = "${minecraft_version_range}" +versionRange = "[${minMinecraftVersion},)" ordering = "NONE" side = "BOTH" - -# Features are specific properties of the game environment, that you may want to declare you require. This example declares -# that your mod requires GL version 3.2 or higher. Other features will be added. They are side aware so declaring this won't -# stop your mod loading on the server for example. -#[features.${mod_id}] -#openGLVersion="[3.2,)" From a16980a549a32b16753838ab156153bf559609de Mon Sep 17 00:00:00 2001 From: North-West-Wind Date: Tue, 30 Jun 2026 11:13:18 +0800 Subject: [PATCH 16/52] feat: support 1.21.1 --- common/build.gradle.kts | 4 +- .../config/gui/FilterSelectionScreen.java | 44 +++++++++++++++++-- .../config/gui/LongSettingScreen.java | 24 ++++++++-- .../autofish/config/gui/ScreenHelper.java | 13 ++++++ .../autofish/config/gui/SettingsScreen.java | 10 ++--- .../config/gui/SuperFilterScreen.java | 33 +++++++++++--- .../autofish/handler/AutoFishHandler.java | 27 +++++++++++- .../northwestw/autofish/keybind/KeyBinds.java | 5 +++ .../assets/forgeautofish/lang/en_us.json | 2 +- .../northwestw/autofish/AutoFishFabric.java | 3 -- gradle.properties | 8 ++-- stonecutter.gradle.kts | 10 ++++- versions/1.21.1/gradle.properties | 25 +++++++++++ versions/1.21.11/gradle.properties | 2 +- 14 files changed, 179 insertions(+), 31 deletions(-) create mode 100644 common/src/main/java/in/northwestw/autofish/config/gui/ScreenHelper.java create mode 100644 versions/1.21.1/gradle.properties diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 27fca4c..04bd887 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -34,7 +34,7 @@ val commonResources: Configuration by configurations.creating { isCanBeConsumed = true } -/*artifacts { +artifacts { afterEvaluate { val mainSourceSet = sourceSets.main.get() mainSourceSet.java.sourceDirectories.files.forEach { @@ -44,4 +44,4 @@ val commonResources: Configuration by configurations.creating { add(commonResources.name, it) } } -}*/ \ No newline at end of file +} \ No newline at end of file diff --git a/common/src/main/java/in/northwestw/autofish/config/gui/FilterSelectionScreen.java b/common/src/main/java/in/northwestw/autofish/config/gui/FilterSelectionScreen.java index e15da39..41247ec 100644 --- a/common/src/main/java/in/northwestw/autofish/config/gui/FilterSelectionScreen.java +++ b/common/src/main/java/in/northwestw/autofish/config/gui/FilterSelectionScreen.java @@ -1,6 +1,7 @@ package in.northwestw.autofish.config.gui; import com.google.common.collect.Lists; +import com.mojang.datafixers.util.Pair; import in.northwestw.autofish.AutoFish; import in.northwestw.autofish.config.Config; import net.minecraft.client.Minecraft; @@ -11,8 +12,10 @@ import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.components.EditBox; import net.minecraft.client.gui.screens.Screen; +//? if >=1.21.11 { import net.minecraft.client.input.KeyEvent; import net.minecraft.client.input.MouseButtonEvent; +//? } import net.minecraft.core.HolderSet; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.resources.Identifier; @@ -53,10 +56,17 @@ protected void init() { searching = original; search = new EditBox(this.font, this.width / 2 - 75, 35, 150, 20, AutoFish.getTranslatableComponent("gui.superfilterscreen.search")) { @Override + //? if >=1.21.11 { public boolean mouseClicked(MouseButtonEvent ev, boolean p_430750_) { if (ev.button() == GLFW.GLFW_MOUSE_BUTTON_2) this.setValue(""); return super.mouseClicked(ev, p_430750_); } + //? } else { + /*public boolean mouseClicked(double mouseX, double mouseY, int button) { + if (button == GLFW.GLFW_MOUSE_BUTTON_2) this.setValue(""); + return super.mouseClicked(mouseX, mouseY, button); + } + *///? } }; search.setResponder(s -> { String[] args = s.split("/ +/"); @@ -66,11 +76,17 @@ public boolean mouseClicked(MouseButtonEvent ev, boolean p_430750_) { else if (arg.startsWith("#")) tags.add(arg.toLowerCase().substring(1)); else paths.add(arg.toLowerCase()); } + //? if >=1.21.11 { List> itemTags = BuiltInRegistries.ITEM.getTags().filter(tag -> tags.stream().anyMatch(t -> tag.key().location().getPath().contains(t))).toList(); + //? } else + //List> itemTags = BuiltInRegistries.ITEM.getTags().map(Pair::getSecond).filter(tag -> tags.stream().anyMatch(t -> tag.key().location().getPath().contains(t))).toList(); searching = original.stream().filter(item -> { Optional> opt = BuiltInRegistries.ITEM.getResourceKey(item); if (opt.isEmpty()) return false; + //? if >=1.21.11 { Identifier rl = opt.get().identifier(); + //? } else + //Identifier rl = opt.get().location(); boolean matchmod = mods.isEmpty(), matchtag = tags.isEmpty(), matcharg = false; for (String mod : mods) matchmod = matchmod || rl.getNamespace().toLowerCase().contains(mod); @@ -87,10 +103,10 @@ public boolean mouseClicked(MouseButtonEvent ev, boolean p_430750_) { Button add = new Button.Builder(AutoFish.getTranslatableComponent("gui.filterselection.save"), button -> { List items = selected.stream().map(item -> BuiltInRegistries.ITEM.getKey(item).toString()).collect(Collectors.toList()); Config.setFilter(items); - Minecraft.getInstance().setScreenAndShow(parent); + ScreenHelper.showScreen(parent); }).pos(this.width / 2 - 75, 60).size(72, 20).build(); addRenderableWidget(add); - Button done = new Button.Builder(AutoFish.getTranslatableComponent("gui.filterselection.cancel"), button -> Minecraft.getInstance().setScreenAndShow(parent)).pos(this.width / 2 + 3, 60).size(72, 20).build(); + Button done = new Button.Builder(AutoFish.getTranslatableComponent("gui.filterselection.cancel"), button -> ScreenHelper.showScreen(parent)).pos(this.width / 2 + 3, 60).size(72, 20).build(); addRenderableWidget(done); previous = new Button.Builder(AutoFish.getLiteralComponent("<"), button -> { if (page > 0) page--; }).pos(this.width / 2 - 100, 60).size(20, 20).build(); previous.visible = false; @@ -114,7 +130,10 @@ public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mo Collection prioritized = searching.stream().filter(item -> { Optional> opt = BuiltInRegistries.ITEM.getResourceKey(item); if (opt.isEmpty()) return false; + //? if >=1.21.11 { Identifier rl = opt.get().identifier(); + //? } else + //Identifier rl = opt.get().location(); boolean pri = Config.prioritize.contains(rl.toString()); if (!pri) searchingCopy.add(item); return pri; @@ -167,21 +186,40 @@ private int getYPos(int k, int height) { } @Override + //? if >=1.21.11 { public boolean keyPressed(KeyEvent ev) { if (ev.key() == GLFW.GLFW_KEY_ESCAPE) { - if (!search.isFocused()) Minecraft.getInstance().setScreenAndShow(parent); + if (!search.isFocused()) ScreenHelper.showScreen(parent); else search.setFocused(false); } return super.keyPressed(ev); } + //? } else { + /*public boolean keyPressed(int keyCode, int scanCode, int modifiers) { + if (keyCode == GLFW.GLFW_KEY_ESCAPE) { + if (!search.isFocused()) ScreenHelper.showScreen(parent); + else search.setFocused(false); + } + return super.keyPressed(keyCode, scanCode, modifiers); + } + *///? } @Override + //? if >=1.21.11 { public boolean mouseClicked(MouseButtonEvent ev, boolean flag) { clickX = ev.x(); clickY = ev.y(); clickProcessed = false; return super.mouseClicked(ev, flag); } + //? } else { + /*public boolean mouseClicked(double mouseX, double mouseY, int button) { + clickX = mouseX; + clickY = mouseY; + clickProcessed = false; + return super.mouseClicked(mouseX, mouseY, button); + } + *///? } @Override public boolean shouldCloseOnEsc() { diff --git a/common/src/main/java/in/northwestw/autofish/config/gui/LongSettingScreen.java b/common/src/main/java/in/northwestw/autofish/config/gui/LongSettingScreen.java index e95a923..6e04d32 100644 --- a/common/src/main/java/in/northwestw/autofish/config/gui/LongSettingScreen.java +++ b/common/src/main/java/in/northwestw/autofish/config/gui/LongSettingScreen.java @@ -9,8 +9,10 @@ import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.components.EditBox; import net.minecraft.client.gui.screens.Screen; +//? if >=1.21.11 { import net.minecraft.client.input.KeyEvent; import net.minecraft.client.input.MouseButtonEvent; +//? } import org.lwjgl.glfw.GLFW; import java.util.function.Consumer; @@ -39,10 +41,17 @@ protected LongSettingScreen(Screen parent, String middleTranslationKey, Supplier protected void init() { editBox = new EditBox(this.font, this.width / 2 - 75, this.height / 2 - 25, 150, 20, AutoFish.getTranslatableComponent("gui." + this.middleTranslationKey + ".throwdelay")) { @Override - public boolean mouseClicked(MouseButtonEvent ev, boolean flag) { + //? if >=1.21.11 { + public boolean mouseClicked(MouseButtonEvent ev, boolean p_430750_) { if (ev.button() == GLFW.GLFW_MOUSE_BUTTON_2) this.setValue(""); - return super.mouseClicked(ev, flag); + return super.mouseClicked(ev, p_430750_); } + //? } else { + /*public boolean mouseClicked(double mouseX, double mouseY, int button) { + if (button == GLFW.GLFW_MOUSE_BUTTON_2) this.setValue(""); + return super.mouseClicked(mouseX, mouseY, button); + } + *///? } }; editBox.setValue(Long.toString(this.supplier.get())); addRenderableWidget(editBox); @@ -53,7 +62,7 @@ public boolean mouseClicked(MouseButtonEvent ev, boolean flag) { if (delay < this.min || delay > this.max) editBox.setValue(Long.toString(this.supplier.get())); else { this.consumer.accept(delay); - Minecraft.getInstance().setScreenAndShow(parent); + ScreenHelper.showScreen(parent); } } }).pos(this.width / 2 - 75, this.height / 2).size(150, 20).build(); @@ -93,10 +102,17 @@ public boolean shouldCloseOnEsc() { } @Override + //? if >=1.21.11 { public boolean keyPressed(KeyEvent ev) { - if (ev.key() == GLFW.GLFW_KEY_ESCAPE) Minecraft.getInstance().setScreenAndShow(parent); + if (ev.key() == GLFW.GLFW_KEY_ESCAPE) ScreenHelper.showScreen(parent); return super.keyPressed(ev); } + //? } else { + /*public boolean keyPressed(int keyCode, int scanCode, int modifiers) { + if (keyCode == GLFW.GLFW_KEY_ESCAPE) ScreenHelper.showScreen(parent); + return super.keyPressed(keyCode, scanCode, modifiers); + } + *///? } @Override public boolean isPauseScreen() { diff --git a/common/src/main/java/in/northwestw/autofish/config/gui/ScreenHelper.java b/common/src/main/java/in/northwestw/autofish/config/gui/ScreenHelper.java new file mode 100644 index 0000000..b85a015 --- /dev/null +++ b/common/src/main/java/in/northwestw/autofish/config/gui/ScreenHelper.java @@ -0,0 +1,13 @@ +package in.northwestw.autofish.config.gui; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.screens.Screen; + +public class ScreenHelper { + public static void showScreen(Screen screen) { + //? if >=1.21.11 { + Minecraft.getInstance().setScreenAndShow(screen); + //? } else + //Minecraft.getInstance().setScreen(screen); + } +} diff --git a/common/src/main/java/in/northwestw/autofish/config/gui/SettingsScreen.java b/common/src/main/java/in/northwestw/autofish/config/gui/SettingsScreen.java index 1c1c3ba..7bb7c6a 100644 --- a/common/src/main/java/in/northwestw/autofish/config/gui/SettingsScreen.java +++ b/common/src/main/java/in/northwestw/autofish/config/gui/SettingsScreen.java @@ -26,15 +26,15 @@ public boolean isPauseScreen() { protected void init() { Button.Builder[] builders = { new Button.Builder(AutoFish.getTranslatableComponent("gui.autofish.recastdelay"), button -> - Minecraft.getInstance().setScreenAndShow(new LongSettingScreen(this, "setrecastdelay", () -> Config.recastDelay, (newDelay) -> Config.recastDelay = newDelay, Config.RECAST_DELAY_RANGE[0], Config.RECAST_DELAY_RANGE[1]))), + ScreenHelper.showScreen(new LongSettingScreen(this, "setrecastdelay", () -> Config.recastDelay, (newDelay) -> Config.recastDelay = newDelay, Config.RECAST_DELAY_RANGE[0], Config.RECAST_DELAY_RANGE[1]))), new Button.Builder(AutoFish.getTranslatableComponent("gui.autofish.reelindelay"), button -> - Minecraft.getInstance().setScreenAndShow(new LongSettingScreen(this, "setreelindelay", () -> Config.reelInDelay, (newDelay) -> Config.reelInDelay = newDelay, Config.REEL_IN_DELAY_RANGE[0], Config.REEL_IN_DELAY_RANGE[1]))), + ScreenHelper.showScreen(new LongSettingScreen(this, "setreelindelay", () -> Config.reelInDelay, (newDelay) -> Config.reelInDelay = newDelay, Config.REEL_IN_DELAY_RANGE[0], Config.REEL_IN_DELAY_RANGE[1]))), new Button.Builder(AutoFish.getTranslatableComponent("gui.autofish.throwdelay"), button -> - Minecraft.getInstance().setScreenAndShow(new LongSettingScreen(this, "setthrowdelay", () -> Config.throwDelay, (newDelay) -> Config.throwDelay = newDelay, Config.THROW_DELAY_RANGE[0], Config.THROW_DELAY_RANGE[1]))), + ScreenHelper.showScreen(new LongSettingScreen(this, "setthrowdelay", () -> Config.throwDelay, (newDelay) -> Config.throwDelay = newDelay, Config.THROW_DELAY_RANGE[0], Config.THROW_DELAY_RANGE[1]))), new Button.Builder(AutoFish.getTranslatableComponent("gui.autofish.checkinterval"), button -> - Minecraft.getInstance().setScreenAndShow(new LongSettingScreen(this, "setcheckinterval", () -> Config.checkInterval, (newInterval) -> Config.checkInterval = newInterval, Config.CHECK_INTERVAL_RANGE[0], Config.CHECK_INTERVAL_RANGE[1]))), + ScreenHelper.showScreen(new LongSettingScreen(this, "setcheckinterval", () -> Config.checkInterval, (newInterval) -> Config.checkInterval = newInterval, Config.CHECK_INTERVAL_RANGE[0], Config.CHECK_INTERVAL_RANGE[1]))), new Button.Builder(AutoFish.getTranslatableComponent("gui.autofish.filter"), button -> - Minecraft.getInstance().setScreenAndShow(new SuperFilterScreen(this))) + ScreenHelper.showScreen(new SuperFilterScreen(this))) }; for (int ii = 0; ii < builders.length; ii++) { diff --git a/common/src/main/java/in/northwestw/autofish/config/gui/SuperFilterScreen.java b/common/src/main/java/in/northwestw/autofish/config/gui/SuperFilterScreen.java index 067c672..ca5e04f 100644 --- a/common/src/main/java/in/northwestw/autofish/config/gui/SuperFilterScreen.java +++ b/common/src/main/java/in/northwestw/autofish/config/gui/SuperFilterScreen.java @@ -1,6 +1,7 @@ package in.northwestw.autofish.config.gui; import com.google.common.collect.Lists; +import com.mojang.datafixers.util.Pair; import in.northwestw.autofish.AutoFish; import in.northwestw.autofish.config.Config; import net.minecraft.client.Minecraft; @@ -11,8 +12,10 @@ import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.components.EditBox; import net.minecraft.client.gui.screens.Screen; +//? if >=1.21.11 { import net.minecraft.client.input.KeyEvent; import net.minecraft.client.input.MouseButtonEvent; +//? } import net.minecraft.core.HolderSet; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.resources.Identifier; @@ -59,10 +62,17 @@ protected void init() { searching = original; search = new EditBox(this.font, this.width / 2 - 75, 35, 150, 20, AutoFish.getTranslatableComponent("gui.superfilterscreen.search")) { @Override - public boolean mouseClicked(MouseButtonEvent ev, boolean flag) { + //? if >=1.21.11 { + public boolean mouseClicked(MouseButtonEvent ev, boolean p_430750_) { if (ev.button() == GLFW.GLFW_MOUSE_BUTTON_2) this.setValue(""); - return super.mouseClicked(ev, flag); + return super.mouseClicked(ev, p_430750_); } + //? } else { + /*public boolean mouseClicked(double mouseX, double mouseY, int button) { + if (button == GLFW.GLFW_MOUSE_BUTTON_2) this.setValue(""); + return super.mouseClicked(mouseX, mouseY, button); + } + *///? } }; search.setResponder(s -> { String[] args = s.split("/ +/"); @@ -72,11 +82,17 @@ public boolean mouseClicked(MouseButtonEvent ev, boolean flag) { else if (arg.startsWith("#")) tags.add(arg.toLowerCase().substring(1)); else paths.add(arg.toLowerCase()); } + //? if >=1.21.11 { List> itemTags = BuiltInRegistries.ITEM.getTags().filter(tag -> tags.stream().anyMatch(t -> tag.key().location().getPath().contains(t))).toList(); + //? } else + //List> itemTags = BuiltInRegistries.ITEM.getTags().map(Pair::getSecond).filter(tag -> tags.stream().anyMatch(t -> tag.key().location().getPath().contains(t))).toList(); searching = original.stream().filter(item -> { Optional> opt = BuiltInRegistries.ITEM.getResourceKey(item); if (opt.isEmpty()) return false; + //? if >=1.21.11 { Identifier rl = opt.get().identifier(); + //? } else + //Identifier rl = opt.get().location(); boolean matchmod = mods.isEmpty(), matchtag = tags.isEmpty(), matcharg = false; for (String mod : mods) matchmod = matchmod || rl.getNamespace().toLowerCase().contains(mod); @@ -90,9 +106,9 @@ public boolean mouseClicked(MouseButtonEvent ev, boolean flag) { if (page > maxPage - 1) page = Math.max(0, maxPage - 1); }); addRenderableWidget(search); - Button add = new Button.Builder(AutoFish.getTranslatableComponent("gui.superfilterscreen.openfilter"), button -> Minecraft.getInstance().setScreenAndShow(new FilterSelectionScreen(this))).pos(this.width / 2 - 75, 60).size(72, 20).build(); + Button add = new Button.Builder(AutoFish.getTranslatableComponent("gui.superfilterscreen.openfilter"), button -> ScreenHelper.showScreen(new FilterSelectionScreen(this))).pos(this.width / 2 - 75, 60).size(72, 20).build(); addRenderableWidget(add); - Button done = new Button.Builder(AutoFish.getTranslatableComponent("gui.superfilterscreen.done"), button -> Minecraft.getInstance().setScreenAndShow(parent)).pos(this.width / 2 + 3, 60).size(72, 20).build(); + Button done = new Button.Builder(AutoFish.getTranslatableComponent("gui.superfilterscreen.done"), button -> ScreenHelper.showScreen(parent)).pos(this.width / 2 + 3, 60).size(72, 20).build(); addRenderableWidget(done); previous = new Button.Builder(AutoFish.getLiteralComponent("<"), button -> { if (page > 0) page--; }).pos(this.width / 2 - 100, 60).size(20, 20).build(); previous.visible = false; @@ -140,10 +156,17 @@ public boolean shouldCloseOnEsc() { } @Override + //? if >=1.21.11 { public boolean keyPressed(KeyEvent ev) { - if (ev.key() == GLFW.GLFW_KEY_ESCAPE) Minecraft.getInstance().setScreenAndShow(parent); + if (ev.key() == GLFW.GLFW_KEY_ESCAPE) ScreenHelper.showScreen(parent); return super.keyPressed(ev); } + //? } else { + /*public boolean keyPressed(int keyCode, int scanCode, int modifiers) { + if (keyCode == GLFW.GLFW_KEY_ESCAPE) ScreenHelper.showScreen(parent); + return super.keyPressed(keyCode, scanCode, modifiers); + } + *///? } @Override public boolean isPauseScreen() { diff --git a/common/src/main/java/in/northwestw/autofish/handler/AutoFishHandler.java b/common/src/main/java/in/northwestw/autofish/handler/AutoFishHandler.java index bda940b..639224a 100644 --- a/common/src/main/java/in/northwestw/autofish/handler/AutoFishHandler.java +++ b/common/src/main/java/in/northwestw/autofish/handler/AutoFishHandler.java @@ -4,6 +4,7 @@ import com.google.common.collect.Maps; import in.northwestw.autofish.AutoFish; import in.northwestw.autofish.config.Config; +import in.northwestw.autofish.config.gui.ScreenHelper; import in.northwestw.autofish.config.gui.SettingsScreen; import in.northwestw.autofish.keybind.KeyBinds; import net.minecraft.ChatFormatting; @@ -48,7 +49,7 @@ public static void onKeyInput() { Config.enableFilter(!Config.allFilters); if (player != null) sendOverlayMessage(player, "itemfilter", Config.allFilters); } else if (KeyBinds.settings.consumeClick()) - minecraft.setScreenAndShow(new SettingsScreen()); + ScreenHelper.showScreen(new SettingsScreen()); } public static void onPlayerTick(final Player player) { @@ -64,7 +65,10 @@ public static void onPlayerTick(final Player player) { } if (afterDrop) { if (tick == 0 && rodSlot != -1) { + //? if >=1.21.11 { player.getInventory().setSelectedSlot(rodSlot); + //? } else + //player.getInventory().selected = rodSlot; rodSlot = -1; } tick++; @@ -119,7 +123,11 @@ private static void reelIn(Player player) { if (!Config.autoFish) return; InteractionHand hand = findHandOfRod(player); if (hand == null) return; - player.getInventory().getNonEquipmentItems().forEach(stack -> { + //? if >=1.21.11 { + List items = player.getInventory().getNonEquipmentItems(); + //? } else + //List items = player.getInventory().items; + items.forEach(stack -> { Identifier rl = BuiltInRegistries.ITEM.getKey(stack.getItem()); //? if >=26.1 { itemsBeforeFished.put(rl, itemsBeforeFished.getOrDefault(rl, 0) + stack.count()); @@ -143,12 +151,18 @@ else if (fishingRod.getMaxDamage() - fishingRod.getDamageValue() < 3 && !player. AutoFish.LOGGER.info("Fishing rod broke. Finding replacement..."); boolean found = false; for (int i = 0; i < 9; i++) { + //? if >=1.21.11 { if (i == player.getInventory().getSelectedSlot()) continue; + //? } else + //if (i == player.getInventory().selected) continue; ItemStack stack = player.getInventory().getItem(i); if (stack.getItem() instanceof FishingRodItem) { if (Config.rodProtect && stack.getMaxDamage() - stack.getDamageValue() < 2) continue; AutoFish.LOGGER.info("Found fishing rod for replacement"); + //? if >=1.21.11 { player.getInventory().setSelectedSlot(i); + //? } else + //player.getInventory().selected = i; found = true; break; } @@ -169,7 +183,10 @@ private static void recast(Player player) { private static void checkItem(Player player) { if (!itemsBeforeFished.isEmpty()) { + //? if >=1.21.11 { List items = player.getInventory().getNonEquipmentItems(); + //? } else + //List items = player.getInventory().items; for (String name : Config.filter) { Identifier rl = Identifier.parse(name); Optional opt = BuiltInRegistries.ITEM.getOptional(rl); @@ -183,7 +200,10 @@ private static void checkItem(Player player) { itemsBeforeFished.clear(); if (!shouldDrop.isEmpty()) { processingDrop = true; + //? if >=1.21.11 { rodSlot = player.getInventory().getSelectedSlot(); + //? } else + //rodSlot = player.getInventory().selected; } } } @@ -198,7 +218,10 @@ private static void dropItem(Player player) { } for (int ii = 0; ii < 9; ii++) { if (!player.getInventory().getItem(ii).getItem().equals(item)) continue; + //? if >=1.21.11 { player.getInventory().setSelectedSlot(ii); + //? } else + //player.getInventory().selected = ii; dropCd = 20; return; } diff --git a/common/src/main/java/in/northwestw/autofish/keybind/KeyBinds.java b/common/src/main/java/in/northwestw/autofish/keybind/KeyBinds.java index 3be0934..08c7eb7 100644 --- a/common/src/main/java/in/northwestw/autofish/keybind/KeyBinds.java +++ b/common/src/main/java/in/northwestw/autofish/keybind/KeyBinds.java @@ -2,7 +2,9 @@ import in.northwestw.autofish.AutoFish; import net.minecraft.client.KeyMapping; +//? if >=1.21.11 { import net.minecraft.resources.Identifier; +//? } import org.lwjgl.glfw.GLFW; public class KeyBinds { @@ -10,7 +12,10 @@ public class KeyBinds { public static KeyMapping autofish, rodprotect, autoreplace, settings, itemfilter; static { + //? if >= 1.21.11 { KeyMapping.Category cat = KeyMapping.Category.register(Identifier.fromNamespaceAndPath(AutoFish.MOD_ID, "autofish")); + //? } else + //String cat = "key.categories.autofish"; autofish = new KeyMapping(AutoFish.getTranslatableComponent("key.forgeautofish.autofish").getString(), GLFW.GLFW_KEY_MINUS, cat); rodprotect = new KeyMapping(AutoFish.getTranslatableComponent("key.forgeautofish.rodprotect").getString(), GLFW.GLFW_KEY_BACKSLASH, cat); autoreplace = new KeyMapping(AutoFish.getTranslatableComponent("key.forgeautofish.autoreplace").getString(), GLFW.GLFW_KEY_RIGHT_BRACKET, cat); diff --git a/common/src/main/resources/assets/forgeautofish/lang/en_us.json b/common/src/main/resources/assets/forgeautofish/lang/en_us.json index de66ed5..377e926 100644 --- a/common/src/main/resources/assets/forgeautofish/lang/en_us.json +++ b/common/src/main/resources/assets/forgeautofish/lang/en_us.json @@ -4,7 +4,7 @@ "key.autofish.autoreplace": "Toggle Auto Replace", "key.autofish.settings": "Open Settings", "key.autofish.itemfilter": "Toggle Item Filter", - "key.categories.autofish": "AutoFish for Forge", + "key.categories.autofish": "AutoFish for Everyone", "toggle.autofish": "%s AutoFish", "toggle.rodprotect": "%s Fishing Rod Protection", diff --git a/fabric/src/main/java/in/northwestw/autofish/AutoFishFabric.java b/fabric/src/main/java/in/northwestw/autofish/AutoFishFabric.java index 4f86b2a..39de37e 100644 --- a/fabric/src/main/java/in/northwestw/autofish/AutoFishFabric.java +++ b/fabric/src/main/java/in/northwestw/autofish/AutoFishFabric.java @@ -28,9 +28,6 @@ public void onInitialize() { *///? } ClientTickEvents.END_CLIENT_TICK.register(client -> AutoFishHandler.onKeyInput()); - //? if >=26.1 { ClientTickEvents.START_CLIENT_TICK.register(client -> AutoFishHandler.onPlayerTick(client.player)); - //? } else - //ClientTickEvents.START_CLIENT_TICK.register(client -> AutoFishHandler.onPlayerTick(client.player)); } } diff --git a/gradle.properties b/gradle.properties index 1a28ae3..88d38b0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,10 +13,10 @@ mod.github=https://github.com/North-West-Wind/forge-autofish # Stonecutter stonecutter_enabled_platforms=fabric, neoforge, forge -stonecutter_enabled_common_versions=26.2, 26.1.2, 1.21.11 -stonecutter_enabled_fabric_versions=26.2, 26.1.2, 1.21.11 -stonecutter_enabled_forge_versions=26.2, 26.1.2, 1.21.11 -stonecutter_enabled_neoforge_versions=26.2, 26.1.2, 1.21.11 +stonecutter_enabled_common_versions=26.2, 26.1.2, 1.21.11, 1.21.1 +stonecutter_enabled_fabric_versions=26.2, 26.1.2, 1.21.11, 1.21.1 +stonecutter_enabled_forge_versions=26.2, 26.1.2, 1.21.11, 1.21.1 +stonecutter_enabled_neoforge_versions=26.2, 26.1.2, 1.21.11, 1.21.1 # The below field are intentionally left blank, # to edit, please edit gradle.properties for each versions. diff --git a/stonecutter.gradle.kts b/stonecutter.gradle.kts index 2db553e..546c76c 100644 --- a/stonecutter.gradle.kts +++ b/stonecutter.gradle.kts @@ -4,4 +4,12 @@ plugins { id("net.fabricmc.fabric-loom") version "1.17-SNAPSHOT" apply false id("net.fabricmc.fabric-loom-remap") version "1.17-SNAPSHOT" apply false } -stonecutter active "26.2" \ No newline at end of file +stonecutter active "26.2" + +stonecutter { + parameters { + replacements.string(current.parsed < "1.21.11") { + replace("Identifier", "ResourceLocation") + } + } +} \ No newline at end of file diff --git a/versions/1.21.1/gradle.properties b/versions/1.21.1/gradle.properties new file mode 100644 index 0000000..613fa41 --- /dev/null +++ b/versions/1.21.1/gradle.properties @@ -0,0 +1,25 @@ +# Stonecutter +stonecutter_enabled_platforms=fabric, forge, neoforge + +# Java +java.version=21 + +# Minecraft +minecraft_version=1.21.1 +min_minecraft_version=1.21 + +# Mappings +deps.parchment=2024.11.17 + +# Fabric +deps.fabric_loader=0.19.3 +deps.fabric_api=0.116.12 + +# NeoForge +deps.neoforge=21.1.234 +deps.neoform=1.21.1-20240808.144430 + +deps.forge=52.1.0 + +# Dependencies +deps.modmenu= \ No newline at end of file diff --git a/versions/1.21.11/gradle.properties b/versions/1.21.11/gradle.properties index 8778e61..c0a8f58 100644 --- a/versions/1.21.11/gradle.properties +++ b/versions/1.21.11/gradle.properties @@ -1,5 +1,5 @@ # Stonecutter -stonecutter_enabled_platforms=fabric-o, forge, neoforge +stonecutter_enabled_platforms=fabric, forge, neoforge # Java java.version=21 From 052ab6a642a181cf5e8ba80f5745a7d87671ff01 Mon Sep 17 00:00:00 2001 From: North-West-Wind Date: Tue, 30 Jun 2026 11:21:16 +0800 Subject: [PATCH 17/52] refactor: clean up, re-add AT for Forge, change version naming --- .../main/kotlin/multiloader-common.gradle.kts | 2 +- forge/build.gradle.kts | 2 +- .../northwestw/autofish/AutoFishFabric.java | 24 -- src/fabric/resources/fabric.mod.json | 32 --- .../in/northwestw/autofish/AutoFishForge.java | 40 --- src/forge/resources/META-INF/mods.toml | 27 --- .../java/in/northwestw/autofish/AutoFish.java | 26 -- .../in/northwestw/autofish/config/Config.java | 169 ------------- .../config/gui/FilterSelectionScreen.java | 203 ---------------- .../config/gui/LongSettingScreen.java | 105 -------- .../autofish/config/gui/SettingsScreen.java | 59 ----- .../config/gui/SuperFilterScreen.java | 152 ------------ .../autofish/handler/AutoFishHandler.java | 227 ------------------ .../northwestw/autofish/keybind/KeyBinds.java | 20 -- .../assets/forgeautofish/lang/en_us.json | 51 ---- .../assets/forgeautofish/lang/zh_tw.json | 41 ---- src/main/resources/autofish.png | Bin 92082 -> 0 bytes src/main/resources/pack.mcmeta | 7 - .../northwestw/autofish/AutoFishNeoForge.java | 44 ---- .../resources/META-INF/neoforge.mods.toml | 32 --- 20 files changed, 2 insertions(+), 1261 deletions(-) delete mode 100644 src/fabric/java/in/northwestw/autofish/AutoFishFabric.java delete mode 100644 src/fabric/resources/fabric.mod.json delete mode 100644 src/forge/java/in/northwestw/autofish/AutoFishForge.java delete mode 100644 src/forge/resources/META-INF/mods.toml delete mode 100644 src/main/java/in/northwestw/autofish/AutoFish.java delete mode 100644 src/main/java/in/northwestw/autofish/config/Config.java delete mode 100644 src/main/java/in/northwestw/autofish/config/gui/FilterSelectionScreen.java delete mode 100644 src/main/java/in/northwestw/autofish/config/gui/LongSettingScreen.java delete mode 100644 src/main/java/in/northwestw/autofish/config/gui/SettingsScreen.java delete mode 100644 src/main/java/in/northwestw/autofish/config/gui/SuperFilterScreen.java delete mode 100644 src/main/java/in/northwestw/autofish/handler/AutoFishHandler.java delete mode 100644 src/main/java/in/northwestw/autofish/keybind/KeyBinds.java delete mode 100644 src/main/resources/assets/forgeautofish/lang/en_us.json delete mode 100644 src/main/resources/assets/forgeautofish/lang/zh_tw.json delete mode 100644 src/main/resources/autofish.png delete mode 100644 src/main/resources/pack.mcmeta delete mode 100644 src/neoforge/java/in/northwestw/autofish/AutoFishNeoForge.java delete mode 100644 src/neoforge/resources/META-INF/neoforge.mods.toml diff --git a/buildSrc/src/main/kotlin/multiloader-common.gradle.kts b/buildSrc/src/main/kotlin/multiloader-common.gradle.kts index 79df3de..8846e26 100644 --- a/buildSrc/src/main/kotlin/multiloader-common.gradle.kts +++ b/buildSrc/src/main/kotlin/multiloader-common.gradle.kts @@ -4,7 +4,7 @@ plugins { id("java-library") } -version = "${loader}-${commonMod.version}+mc${stonecutterBuild.current.version}" +version = "${commonMod.version}-${stonecutterBuild.current.version}-${loader}" base { archivesName = commonMod.id diff --git a/forge/build.gradle.kts b/forge/build.gradle.kts index eed5199..c2c9768 100644 --- a/forge/build.gradle.kts +++ b/forge/build.gradle.kts @@ -10,7 +10,7 @@ minecraft { val at = rootProject.file("src/${loader}/resources/META-INF/accesstransformer.cfg") if (at.exists()) { - //accessTransformer = at + accessTransformer.from(at) } runs { diff --git a/src/fabric/java/in/northwestw/autofish/AutoFishFabric.java b/src/fabric/java/in/northwestw/autofish/AutoFishFabric.java deleted file mode 100644 index 0f2bb8f..0000000 --- a/src/fabric/java/in/northwestw/autofish/AutoFishFabric.java +++ /dev/null @@ -1,24 +0,0 @@ -package in.northwestw.autofish; - -import in.northwestw.autofish.handler.AutoFishHandler; -import in.northwestw.autofish.keybind.KeyBinds; -import net.fabricmc.api.ModInitializer; -import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; -import net.fabricmc.fabric.api.client.keymapping.v1.KeyMappingHelper; - -public class AutoFishFabric implements ModInitializer { - - @Override - public void onInitialize() { - KeyMappingHelper.registerKeyMapping(KeyBinds.autofish); - KeyMappingHelper.registerKeyMapping(KeyBinds.rodprotect); - KeyMappingHelper.registerKeyMapping(KeyBinds.autoreplace); - KeyMappingHelper.registerKeyMapping(KeyBinds.settings); - KeyMappingHelper.registerKeyMapping(KeyBinds.itemfilter); - - ClientTickEvents.END_CLIENT_TICK.register(_ -> AutoFishHandler.onKeyInput()); - ClientTickEvents.START_CLIENT_TICK.register(client -> { - AutoFishHandler.onPlayerTick(client.player); - }); - } -} diff --git a/src/fabric/resources/fabric.mod.json b/src/fabric/resources/fabric.mod.json deleted file mode 100644 index d20efe4..0000000 --- a/src/fabric/resources/fabric.mod.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "schemaVersion": 1, - "id": "${mod_id}", - "version": "${version}", - "name": "${mod_name}", - "description": "${description}", - "authors": [ - "${mod_author}" - ], - "contact": { - "homepage": "https://fabricmc.net/", - "sources": "https://github.com/FabricMC/fabric-example-mod" - }, - "license": "${license}", - "icon": "${mod_id}.png", - "environment": "*", - "entrypoints": { - "main": [ - "in.northwestw.autofish.AutoFishFabric" - ] - }, - "depends": { - "fabricloader": ">=${fabric_loader_version}", - "fabric-api": "*", - "minecraft": "~${minecraft_version}", - "java": ">=${java_version}" - }, - "suggests": { - "another-mod": "*" - } -} - \ No newline at end of file diff --git a/src/forge/java/in/northwestw/autofish/AutoFishForge.java b/src/forge/java/in/northwestw/autofish/AutoFishForge.java deleted file mode 100644 index b22a670..0000000 --- a/src/forge/java/in/northwestw/autofish/AutoFishForge.java +++ /dev/null @@ -1,40 +0,0 @@ -package in.northwestw.autofish; - -import in.northwestw.autofish.handler.AutoFishHandler; -import in.northwestw.autofish.keybind.KeyBinds; -import net.minecraftforge.client.event.InputEvent; -import net.minecraftforge.client.event.RegisterKeyMappingsEvent; -import net.minecraftforge.event.TickEvent; -import net.minecraftforge.eventbus.api.listener.SubscribeEvent; -import net.minecraftforge.fml.LogicalSide; -import net.minecraftforge.fml.common.Mod; - -@Mod(AutoFish.MOD_ID) -public class AutoFishForge { - - public AutoFishForge() { - } - - @Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD) - public static class ModEvents { - @SubscribeEvent - public static void registerKeyMappings(RegisterKeyMappingsEvent event) { - event.register(KeyBinds.autofish); - event.register(KeyBinds.rodprotect); - event.register(KeyBinds.autoreplace); - event.register(KeyBinds.settings); - event.register(KeyBinds.itemfilter); - } - - @SubscribeEvent - public static void inputKey(InputEvent.Key event) { - AutoFishHandler.onKeyInput(); - } - - @SubscribeEvent - public static void playerTickPre(TickEvent.PlayerTickEvent.Pre event) { - if (event.side() != LogicalSide.CLIENT) return; - AutoFishHandler.onPlayerTick(event.player()); - } - } -} \ No newline at end of file diff --git a/src/forge/resources/META-INF/mods.toml b/src/forge/resources/META-INF/mods.toml deleted file mode 100644 index eed0909..0000000 --- a/src/forge/resources/META-INF/mods.toml +++ /dev/null @@ -1,27 +0,0 @@ -modLoader = "javafml" #mandatory -loaderVersion = "${forge_loader_version_range}" #mandatory This is typically bumped every Minecraft version by Forge. See https://files.minecraftforge.net/ for a list of versions. -license = "${license}" # Review your options at https://choosealicense.com/. -#issueTrackerURL="https://change.me.to.your.issue.tracker.example.invalid/" #optional -#clientSideOnly=true #optional -[[mods]] #mandatory -modId = "${mod_id}" #mandatory -version = "${version}" #mandatory -displayName = "${mod_name}" #mandatory -#updateJSONURL="https://change.me.example.invalid/updates.json" #optional (see https://mcforge.readthedocs.io/en/latest/gettingstarted/autoupdate/) -#displayURL="https://change.me.to.your.mods.homepage.example.invalid/" #optional (displayed in the mod UI) -logoFile = "${mod_id}.png" #optional -credits = "${credits}" #optional -authors = "${mod_author}" #optional -description = '''${description}''' #mandatory (Supports multiline text) -[[dependencies.${mod_id}]] #optional -modId = "forge" #mandatory -mandatory = true #mandatory -versionRange = "[${forge_version},)" #mandatory -ordering = "NONE" # The order that this dependency should load in relation to your mod, required to be either 'BEFORE' or 'AFTER' if the dependency is not mandatory -side = "BOTH" # Side this dependency is applied on - 'BOTH', 'CLIENT' or 'SERVER' -[[dependencies.${mod_id}]] -modId = "minecraft" -mandatory = true -versionRange = "${minecraft_version_range}" -ordering = "NONE" -side = "BOTH" \ No newline at end of file diff --git a/src/main/java/in/northwestw/autofish/AutoFish.java b/src/main/java/in/northwestw/autofish/AutoFish.java deleted file mode 100644 index 6336c65..0000000 --- a/src/main/java/in/northwestw/autofish/AutoFish.java +++ /dev/null @@ -1,26 +0,0 @@ -package in.northwestw.autofish; - -import in.northwestw.autofish.config.Config; -import net.minecraft.network.chat.MutableComponent; -import net.minecraft.network.chat.contents.PlainTextContents; -import net.minecraft.network.chat.contents.TranslatableContents; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -public class AutoFish -{ - public static final String MOD_ID = "autofish"; - public static final Logger LOGGER = LogManager.getLogger(); - - static { - Config.load(); - } - - public static MutableComponent getTranslatableComponent(String key, Object... args) { - return MutableComponent.create(new TranslatableContents(key, null, args)); - } - - public static MutableComponent getLiteralComponent(String str) { - return MutableComponent.create(new PlainTextContents.LiteralContents(str)); - } -} diff --git a/src/main/java/in/northwestw/autofish/config/Config.java b/src/main/java/in/northwestw/autofish/config/Config.java deleted file mode 100644 index 90982f7..0000000 --- a/src/main/java/in/northwestw/autofish/config/Config.java +++ /dev/null @@ -1,169 +0,0 @@ -package in.northwestw.autofish.config; - -import com.google.common.collect.Lists; -import com.google.gson.*; -import in.northwestw.autofish.AutoFish; - -import java.io.File; -import java.io.FileReader; -import java.io.IOException; -import java.io.PrintWriter; -import java.util.List; - -public class Config { - private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); - - public static final long[] RECAST_DELAY_RANGE = { 1L, 600L }; - public static final long[] REEL_IN_DELAY_RANGE = { 0L, 600L }; - public static final long[] THROW_DELAY_RANGE = { 5L, 600L }; - public static final long[] CHECK_INTERVAL_RANGE = { 20L, 72000L }; - - public static long recastDelay = 20, reelInDelay = 0, throwDelay = 10, checkInterval = 200; - public static boolean autoFish = true, rodProtect = true, autoReplace = true, allFilters = true; - public static List filter = Lists.newArrayList(), prioritize = Lists.newArrayList(); - - public static void save() { - try { - File file = new File("config/" + AutoFish.MOD_ID + ".json"); - JsonObject json = new JsonObject(); - json.addProperty("recast_delay", recastDelay); - json.addProperty("reel_in_delay", reelInDelay); - json.addProperty("throw_delay", throwDelay); - json.addProperty("check_interval", checkInterval); - json.addProperty("auto_fish", autoFish); - json.addProperty("rod_protect", rodProtect); - json.addProperty("auto_replace", autoReplace); - json.addProperty("all_filters", allFilters); - - JsonArray array = new JsonArray(); - filter.forEach(array::add); - json.add("filter", array); - - if (file.exists() || file.createNewFile()) { - PrintWriter writer = new PrintWriter(file); - writer.println(GSON.toJson(json)); - writer.close(); - } - } catch (IOException e) { - AutoFish.LOGGER.error(e); - } - } - public static void load() { - try { - File file = new File("config/" + AutoFish.MOD_ID + ".json"); - if (!file.exists()) { - save(); - } else { - JsonObject json = GSON.fromJson(new FileReader(file), JsonObject.class); - if (json.has("recast_delay")) - recastDelay = json.get("recast_delay").getAsLong(); - if (json.has("reel_in_delay")) - reelInDelay = json.get("reel_in_delay").getAsLong(); - if (json.has("throw_delay")) - throwDelay = json.get("throw_delay").getAsLong(); - if (json.has("check_interval")) - checkInterval = json.get("check_interval").getAsLong(); - if (json.has("auto_fish")) - autoFish = json.get("auto_fish").getAsBoolean(); - if (json.has("rod_protect")) - rodProtect = json.get("rod_protect").getAsBoolean(); - if (json.has("auto_replace")) - autoReplace = json.get("auto_replace").getAsBoolean(); - if (json.has("all_filters")) - allFilters = json.get("all_filters").getAsBoolean(); - if (json.has("filter")) - filter = json.getAsJsonArray("filter").asList().stream().map(JsonElement::getAsString).toList(); - - // validate - if (recastDelay < RECAST_DELAY_RANGE[0] || recastDelay > RECAST_DELAY_RANGE[1]) { - AutoFish.LOGGER.warn("recast_delay must be in range [1, 600]. Defaults to 20"); - recastDelay = 20; - } - if (reelInDelay < REEL_IN_DELAY_RANGE[0] || reelInDelay > REEL_IN_DELAY_RANGE[1]) { - AutoFish.LOGGER.warn("reel_in_delay must be in range [0, 600]. Defaults to 0"); - reelInDelay = 0; - } - if (throwDelay < THROW_DELAY_RANGE[0] || throwDelay > THROW_DELAY_RANGE[1]) { - AutoFish.LOGGER.warn("throw_delay must be in range [5, 600]. Defaults to 10"); - throwDelay = 10; - } - if (checkInterval < CHECK_INTERVAL_RANGE[0] || checkInterval > CHECK_INTERVAL_RANGE[1]) { - AutoFish.LOGGER.warn("check_interval must be in range [20, 72000]. Defaults to 200"); - checkInterval = 200; - } - } - } catch (IOException e) { - AutoFish.LOGGER.error(e); - } - } - - public static void setRecastDelay(long recastDelay) { - if (recastDelay < 1 || recastDelay > 600) { - AutoFish.LOGGER.warn("max_circuit_size must be in range [1, 600]. Defaults to 20"); - recastDelay = 20; - } - Config.recastDelay = recastDelay; - Config.save(); - AutoFish.LOGGER.debug("Set Recast Delay: " + recastDelay); - } - - public static void setReelInDelay(long reelInDelay) { - if (reelInDelay < 0 || reelInDelay > 600) { - AutoFish.LOGGER.warn("reel_in_delay must be in range [0, 600]. Defaults to 0"); - reelInDelay = 0; - } - Config.reelInDelay = reelInDelay; - Config.save(); - AutoFish.LOGGER.debug("Set Reel In Delay: " + reelInDelay); - } - - public static void setThrowDelay(long throwDelay) { - if (throwDelay < 5 || throwDelay > 600) { - AutoFish.LOGGER.warn("throw_delay must be in range [5, 600]. Defaults to 10"); - throwDelay = 10; - } - Config.throwDelay = throwDelay; - Config.save(); - AutoFish.LOGGER.debug("Set Throw Delay: " + throwDelay); - } - - public static void setCheckInterval(long checkInterval) { - if (checkInterval < 20 || checkInterval > 72000) { - AutoFish.LOGGER.warn("check_interval must be in range [20, 72000]. Defaults to 200"); - checkInterval = 200; - } - Config.checkInterval = checkInterval; - Config.save(); - AutoFish.LOGGER.debug("Set Check Interval: " + checkInterval); - } - - public static void setAutoFish(boolean autoFish) { - Config.autoFish = autoFish; - Config.save(); - AutoFish.LOGGER.info("Toggle AutoFish: " + autoFish); - } - - public static void setRodProtect(boolean rodProtect) { - Config.rodProtect = rodProtect; - Config.save(); - AutoFish.LOGGER.info("Toggle Rod Protect: " + rodProtect); - } - - public static void setAutoReplace(boolean autoReplace) { - Config.autoReplace = autoReplace; - Config.save(); - AutoFish.LOGGER.info("Toggle Auto Replace: " + autoReplace); - } - - public static void enableFilter(boolean filter) { - Config.allFilters = filter; - Config.save(); - AutoFish.LOGGER.info("Toggle Filter: " + filter); - } - - public static void setFilter(List list) { - Config.filter = list; - Config.save(); - AutoFish.LOGGER.info("Received new Filter"); - } -} diff --git a/src/main/java/in/northwestw/autofish/config/gui/FilterSelectionScreen.java b/src/main/java/in/northwestw/autofish/config/gui/FilterSelectionScreen.java deleted file mode 100644 index e15da39..0000000 --- a/src/main/java/in/northwestw/autofish/config/gui/FilterSelectionScreen.java +++ /dev/null @@ -1,203 +0,0 @@ -package in.northwestw.autofish.config.gui; - -import com.google.common.collect.Lists; -import in.northwestw.autofish.AutoFish; -import in.northwestw.autofish.config.Config; -import net.minecraft.client.Minecraft; -//? if >=26.1 { -import net.minecraft.client.gui.GuiGraphicsExtractor; -//?} else -//import net.minecraft.client.gui.GuiGraphics; -import net.minecraft.client.gui.components.Button; -import net.minecraft.client.gui.components.EditBox; -import net.minecraft.client.gui.screens.Screen; -import net.minecraft.client.input.KeyEvent; -import net.minecraft.client.input.MouseButtonEvent; -import net.minecraft.core.HolderSet; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.resources.Identifier; -import net.minecraft.resources.ResourceKey; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; -import org.lwjgl.glfw.GLFW; - -import java.util.List; -import java.util.*; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -public class FilterSelectionScreen extends Screen { - private final Screen parent; - private EditBox search; - private final Collection original = BuiltInRegistries.ITEM.stream().toList(); - private Collection searching; - private final Set selected = new HashSet<>(Config.filter.stream().map(string -> BuiltInRegistries.ITEM.getOptional(Identifier.parse(string))).filter(Optional::isPresent).map(Optional::get).collect(Collectors.toList())); - private int page, maxPage = (int) Math.ceil(original.size() / 300.0), max = 300; - private boolean clickProcessed = true; - private double clickX, clickY; - private Button previous, next; - int reducedHeight; - int reducedWidth; - - public FilterSelectionScreen(Screen parent) { - super(AutoFish.getTranslatableComponent("gui.filterselection")); - this.parent = parent; - } - - @Override - protected void init() { - reducedHeight = this.height - 90; - reducedWidth = this.width - 30; - max = /* (int) Math.round(300 * (reducedWidth / 550.0 + reducedHeight / 330.0) / 2.0) */ 300; - maxPage = (int) Math.ceil(original.size() / (double) max); - searching = original; - search = new EditBox(this.font, this.width / 2 - 75, 35, 150, 20, AutoFish.getTranslatableComponent("gui.superfilterscreen.search")) { - @Override - public boolean mouseClicked(MouseButtonEvent ev, boolean p_430750_) { - if (ev.button() == GLFW.GLFW_MOUSE_BUTTON_2) this.setValue(""); - return super.mouseClicked(ev, p_430750_); - } - }; - search.setResponder(s -> { - String[] args = s.split("/ +/"); - List mods = Lists.newArrayList(), tags = Lists.newArrayList(), paths = Lists.newArrayList(); - for (String arg : args) { - if (arg.startsWith("@")) mods.add(arg.toLowerCase().substring(1)); - else if (arg.startsWith("#")) tags.add(arg.toLowerCase().substring(1)); - else paths.add(arg.toLowerCase()); - } - List> itemTags = BuiltInRegistries.ITEM.getTags().filter(tag -> tags.stream().anyMatch(t -> tag.key().location().getPath().contains(t))).toList(); - searching = original.stream().filter(item -> { - Optional> opt = BuiltInRegistries.ITEM.getResourceKey(item); - if (opt.isEmpty()) return false; - Identifier rl = opt.get().identifier(); - boolean matchmod = mods.isEmpty(), matchtag = tags.isEmpty(), matcharg = false; - for (String mod : mods) - matchmod = matchmod || rl.getNamespace().toLowerCase().contains(mod); - for (HolderSet.Named itemTag : itemTags) - matchtag = matchtag || itemTag.stream().anyMatch(tagItem -> tagItem.value() == item); - for (String arg : paths) - matcharg = matcharg || rl.getPath().contains(arg); - return matchmod && matchtag && matcharg; - }).collect(Collectors.toList()); - maxPage = (int) Math.ceil(searching.size() / (double) max); - if (page > maxPage - 1) page = Math.max(0, maxPage - 1); - }); - addRenderableWidget(search); - Button add = new Button.Builder(AutoFish.getTranslatableComponent("gui.filterselection.save"), button -> { - List items = selected.stream().map(item -> BuiltInRegistries.ITEM.getKey(item).toString()).collect(Collectors.toList()); - Config.setFilter(items); - Minecraft.getInstance().setScreenAndShow(parent); - }).pos(this.width / 2 - 75, 60).size(72, 20).build(); - addRenderableWidget(add); - Button done = new Button.Builder(AutoFish.getTranslatableComponent("gui.filterselection.cancel"), button -> Minecraft.getInstance().setScreenAndShow(parent)).pos(this.width / 2 + 3, 60).size(72, 20).build(); - addRenderableWidget(done); - previous = new Button.Builder(AutoFish.getLiteralComponent("<"), button -> { if (page > 0) page--; }).pos(this.width / 2 - 100, 60).size(20, 20).build(); - previous.visible = false; - addRenderableWidget(previous); - next = new Button.Builder(AutoFish.getLiteralComponent(">"), button -> { if (page < maxPage - 1) page++; }).pos(this.width / 2 + 80, 60).size(20, 20).build(); - next.visible = false; - addRenderableWidget(next); - } - - @Override - //? if >=26.1 { - public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float partialTicks) { - super.extractRenderState(graphics, mouseX, mouseY, partialTicks); - graphics.centeredText(this.font, this.title, this.width / 2, 20, -1); - //?} else { - /*public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) { - super.render(graphics, mouseX, mouseY, partialTicks); - graphics.drawCenteredString(this.font, this.title, this.width / 2, 20, -1); - *///?} - Collection searchingCopy = Lists.newArrayList(); - Collection prioritized = searching.stream().filter(item -> { - Optional> opt = BuiltInRegistries.ITEM.getResourceKey(item); - if (opt.isEmpty()) return false; - Identifier rl = opt.get().identifier(); - boolean pri = Config.prioritize.contains(rl.toString()); - if (!pri) searchingCopy.add(item); - return pri; - }).toList(); - Item[] items = Stream.concat(prioritized.stream(), searchingCopy.stream()).toArray(Item[]::new); - if (items.length > 0 && page >= 0) { - for (int i = page * max; i < Math.min((page + 1) * max, searching.size()); i++) { - Item item = items[i]; - int h = (i % max) / (max / 30); - int k = (i % max) % (max / 30); - int x = getXPos(h, reducedWidth); - int y = getYPos(k, reducedHeight); - ItemStack stack = new ItemStack(item); - if (!stack.isEmpty()) { - //? if >=26.1 { - graphics.item(stack, x, y); - //? } else - //graphics.renderItem(stack, x, y); - if (!clickProcessed && isMouseInRange(clickX, clickY, x, y, x+16, y+16)) { - if (selected.contains(item)) selected.remove(item); - else selected.add(item); - clickProcessed = true; - } - if (selected.contains(item)) graphics.fillGradient(x - 2, y - 2, x + 18, y + 18, 0xFF00FF00, 0xFF00FF00); - else if (isMouseInRange(mouseX, mouseY, x, y,x + 16, y + 16)) graphics.fillGradient(x - 2, y - 2, x + 18, y + 18, 0xFFC0C0C0, 0xFFC0C0C0); - //if (isMouseInRange(mouseX, mouseY, x, y,x + 16, y + 16)) graphics.item(this.font, stack, mouseX, mouseY); - //? if >=26.1 { - graphics.item(stack, x, y); - //? } else - //graphics.renderItem(stack, x, y); - } - } - } - //? if >=26.1 { - search.extractRenderState(graphics, mouseX, mouseY, partialTicks); - //?} else - //search.render(graphics, mouseX, mouseY, partialTicks); - } - - private boolean isMouseInRange(double mouseX, double mouseY, int x1, int y1, int x2, int y2) { - return mouseX > x1 && mouseX < x2 && mouseY > y1 && mouseY < y2; - } - - private int getXPos(int h, int width) { - return (width * h / 30) + 15; - } - - private int getYPos(int k, int height) { - return ((height * k / (max / 30)) + 90); - } - - @Override - public boolean keyPressed(KeyEvent ev) { - if (ev.key() == GLFW.GLFW_KEY_ESCAPE) { - if (!search.isFocused()) Minecraft.getInstance().setScreenAndShow(parent); - else search.setFocused(false); - } - return super.keyPressed(ev); - } - - @Override - public boolean mouseClicked(MouseButtonEvent ev, boolean flag) { - clickX = ev.x(); - clickY = ev.y(); - clickProcessed = false; - return super.mouseClicked(ev, flag); - } - - @Override - public boolean shouldCloseOnEsc() { - return false; - } - - @Override - public void tick() { - //search.tick(); - super.tick(); - previous.visible = page >= 1; - next.visible = page < maxPage - 1; - } - - @Override - public boolean isPauseScreen() { - return false; - } -} diff --git a/src/main/java/in/northwestw/autofish/config/gui/LongSettingScreen.java b/src/main/java/in/northwestw/autofish/config/gui/LongSettingScreen.java deleted file mode 100644 index e95a923..0000000 --- a/src/main/java/in/northwestw/autofish/config/gui/LongSettingScreen.java +++ /dev/null @@ -1,105 +0,0 @@ -package in.northwestw.autofish.config.gui; - -import in.northwestw.autofish.AutoFish; -import net.minecraft.client.Minecraft; -//? if >=26.1 { -import net.minecraft.client.gui.GuiGraphicsExtractor; - //?} else -//import net.minecraft.client.gui.GuiGraphics; -import net.minecraft.client.gui.components.Button; -import net.minecraft.client.gui.components.EditBox; -import net.minecraft.client.gui.screens.Screen; -import net.minecraft.client.input.KeyEvent; -import net.minecraft.client.input.MouseButtonEvent; -import org.lwjgl.glfw.GLFW; - -import java.util.function.Consumer; -import java.util.function.Supplier; -import java.util.regex.Pattern; - -public class LongSettingScreen extends Screen { - private final Screen parent; - private final String middleTranslationKey; - private final Supplier supplier; - private final Consumer consumer; - private final long min, max; - private EditBox editBox; - - protected LongSettingScreen(Screen parent, String middleTranslationKey, Supplier supplier, Consumer consumer, long min, long max) { - super(AutoFish.getTranslatableComponent("gui." + middleTranslationKey)); - this.parent = parent; - this.middleTranslationKey = middleTranslationKey; - this.supplier = supplier; - this.consumer = consumer; - this.min = min; - this.max = max; - } - - @Override - protected void init() { - editBox = new EditBox(this.font, this.width / 2 - 75, this.height / 2 - 25, 150, 20, AutoFish.getTranslatableComponent("gui." + this.middleTranslationKey + ".throwdelay")) { - @Override - public boolean mouseClicked(MouseButtonEvent ev, boolean flag) { - if (ev.button() == GLFW.GLFW_MOUSE_BUTTON_2) this.setValue(""); - return super.mouseClicked(ev, flag); - } - }; - editBox.setValue(Long.toString(this.supplier.get())); - addRenderableWidget(editBox); - Button save = new Button.Builder(AutoFish.getTranslatableComponent("gui." + this.middleTranslationKey + ".save"), button -> { - if (!isNumeric(editBox.getValue())) editBox.setValue(Long.toString(this.supplier.get())); - else { - long delay = Long.parseLong(editBox.getValue()); - if (delay < this.min || delay > this.max) editBox.setValue(Long.toString(this.supplier.get())); - else { - this.consumer.accept(delay); - Minecraft.getInstance().setScreenAndShow(parent); - } - } - }).pos(this.width / 2 - 75, this.height / 2).size(150, 20).build(); - addRenderableWidget(save); - } - - @Override - public void tick() { - //throwDelay.tick(); - super.tick(); - } - - private static final Pattern pattern = Pattern.compile("-?\\d+(\\.\\d+)?"); - public static boolean isNumeric(String strNum) { - if (strNum == null) { - return false; - } - return pattern.matcher(strNum).matches(); - } - - @Override - //? if >=26.1 { - public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float partialTicks) { - super.extractRenderState(graphics, mouseX, mouseY, partialTicks); - graphics.centeredText(this.font, this.title, this.width / 2, 20, -1); - this.editBox.extractRenderState(graphics, mouseX, mouseY, partialTicks); - }//?} else { - /*public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) { - super.render(graphics, mouseX, mouseY, partialTicks); - graphics.drawCenteredString(this.font, this.title, this.width / 2, 20, -1); - this.editBox.render(graphics, mouseX, mouseY, partialTicks); - }*///?} - - @Override - public boolean shouldCloseOnEsc() { - return false; - } - - @Override - public boolean keyPressed(KeyEvent ev) { - if (ev.key() == GLFW.GLFW_KEY_ESCAPE) Minecraft.getInstance().setScreenAndShow(parent); - return super.keyPressed(ev); - } - - @Override - public boolean isPauseScreen() { - return false; - } -} diff --git a/src/main/java/in/northwestw/autofish/config/gui/SettingsScreen.java b/src/main/java/in/northwestw/autofish/config/gui/SettingsScreen.java deleted file mode 100644 index 1c1c3ba..0000000 --- a/src/main/java/in/northwestw/autofish/config/gui/SettingsScreen.java +++ /dev/null @@ -1,59 +0,0 @@ -package in.northwestw.autofish.config.gui; - -import in.northwestw.autofish.AutoFish; -import in.northwestw.autofish.config.Config; -import net.minecraft.client.Minecraft; -//? if >=26.1 { -import net.minecraft.client.gui.GuiGraphicsExtractor; - //?} else -//import net.minecraft.client.gui.GuiGraphics; -import net.minecraft.client.gui.components.Button; -import net.minecraft.client.gui.screens.Screen; - -public class SettingsScreen extends Screen { - private static final int WIDTH = 150, HEIGHT = 20, MARGIN = 5; - - public SettingsScreen() { - super(AutoFish.getTranslatableComponent("gui.autofish")); - } - - @Override - public boolean isPauseScreen() { - return false; - } - - @Override - protected void init() { - Button.Builder[] builders = { - new Button.Builder(AutoFish.getTranslatableComponent("gui.autofish.recastdelay"), button -> - Minecraft.getInstance().setScreenAndShow(new LongSettingScreen(this, "setrecastdelay", () -> Config.recastDelay, (newDelay) -> Config.recastDelay = newDelay, Config.RECAST_DELAY_RANGE[0], Config.RECAST_DELAY_RANGE[1]))), - new Button.Builder(AutoFish.getTranslatableComponent("gui.autofish.reelindelay"), button -> - Minecraft.getInstance().setScreenAndShow(new LongSettingScreen(this, "setreelindelay", () -> Config.reelInDelay, (newDelay) -> Config.reelInDelay = newDelay, Config.REEL_IN_DELAY_RANGE[0], Config.REEL_IN_DELAY_RANGE[1]))), - new Button.Builder(AutoFish.getTranslatableComponent("gui.autofish.throwdelay"), button -> - Minecraft.getInstance().setScreenAndShow(new LongSettingScreen(this, "setthrowdelay", () -> Config.throwDelay, (newDelay) -> Config.throwDelay = newDelay, Config.THROW_DELAY_RANGE[0], Config.THROW_DELAY_RANGE[1]))), - new Button.Builder(AutoFish.getTranslatableComponent("gui.autofish.checkinterval"), button -> - Minecraft.getInstance().setScreenAndShow(new LongSettingScreen(this, "setcheckinterval", () -> Config.checkInterval, (newInterval) -> Config.checkInterval = newInterval, Config.CHECK_INTERVAL_RANGE[0], Config.CHECK_INTERVAL_RANGE[1]))), - new Button.Builder(AutoFish.getTranslatableComponent("gui.autofish.filter"), button -> - Minecraft.getInstance().setScreenAndShow(new SuperFilterScreen(this))) - }; - - for (int ii = 0; ii < builders.length; ii++) { - Button button = builders[ii].pos(this.width / 2 - WIDTH / 2, this.height / 2 + (ii - builders.length / 2) * (HEIGHT + MARGIN)).size(WIDTH, HEIGHT).build(); - addRenderableWidget(button); - } - - Button done = new Button.Builder(AutoFish.getTranslatableComponent("gui.autofish.done"), button -> onClose()).pos(this.width / 2 - 75, this.height - 25).size(150, 20).build(); - addRenderableWidget(done); - } - - @Override - //? if >=26.1 { - public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float partialTicks) { - super.extractRenderState(graphics, mouseX, mouseY, partialTicks); - graphics.centeredText(this.font, this.title, this.width / 2, 20, -1); - }//?} else { - /*public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) { - super.render(graphics, mouseX, mouseY, partialTicks); - graphics.drawCenteredString(this.font, this.title, this.width / 2, 20, -1); - }*///?} -} diff --git a/src/main/java/in/northwestw/autofish/config/gui/SuperFilterScreen.java b/src/main/java/in/northwestw/autofish/config/gui/SuperFilterScreen.java deleted file mode 100644 index 067c672..0000000 --- a/src/main/java/in/northwestw/autofish/config/gui/SuperFilterScreen.java +++ /dev/null @@ -1,152 +0,0 @@ -package in.northwestw.autofish.config.gui; - -import com.google.common.collect.Lists; -import in.northwestw.autofish.AutoFish; -import in.northwestw.autofish.config.Config; -import net.minecraft.client.Minecraft; -//? if >=26.1 { -import net.minecraft.client.gui.GuiGraphicsExtractor; - //?} else -//import net.minecraft.client.gui.GuiGraphics; -import net.minecraft.client.gui.components.Button; -import net.minecraft.client.gui.components.EditBox; -import net.minecraft.client.gui.screens.Screen; -import net.minecraft.client.input.KeyEvent; -import net.minecraft.client.input.MouseButtonEvent; -import net.minecraft.core.HolderSet; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.resources.Identifier; -import net.minecraft.resources.ResourceKey; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; -import org.lwjgl.glfw.GLFW; - -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; - -public class SuperFilterScreen extends Screen { - private final Screen parent; - private EditBox search; - private Collection original; - private Collection searching; - private int page = 0, maxPage, max = 30; - private Button previous, next; - int reducedHeight; - int reducedWidth; - - protected SuperFilterScreen(Screen parent) { - super(AutoFish.getTranslatableComponent("gui.superfilterscreen")); - this.parent = parent; - } - - @Override - public void tick() { - //search.tick(); - previous.visible = page >= 1; - next.visible = page < maxPage - 1; - } - - @Override - protected void init() { - reducedHeight = this.height - 90; - reducedWidth = this.width - 30; - max = /* (int) Math.round(30 * (reducedWidth / 550.0 + reducedHeight / 330.0) / 2.0) */ 30; - original = Config.filter.stream().map(string -> BuiltInRegistries.ITEM.getOptional(Identifier.parse(string))).filter(Optional::isPresent).map(Optional::get).collect(Collectors.toList()); - maxPage = (int) Math.ceil(original.size() / (double) max); - searching = original; - search = new EditBox(this.font, this.width / 2 - 75, 35, 150, 20, AutoFish.getTranslatableComponent("gui.superfilterscreen.search")) { - @Override - public boolean mouseClicked(MouseButtonEvent ev, boolean flag) { - if (ev.button() == GLFW.GLFW_MOUSE_BUTTON_2) this.setValue(""); - return super.mouseClicked(ev, flag); - } - }; - search.setResponder(s -> { - String[] args = s.split("/ +/"); - List mods = Lists.newArrayList(), tags = Lists.newArrayList(), paths = Lists.newArrayList(); - for (String arg : args) { - if (arg.startsWith("@")) mods.add(arg.toLowerCase().substring(1)); - else if (arg.startsWith("#")) tags.add(arg.toLowerCase().substring(1)); - else paths.add(arg.toLowerCase()); - } - List> itemTags = BuiltInRegistries.ITEM.getTags().filter(tag -> tags.stream().anyMatch(t -> tag.key().location().getPath().contains(t))).toList(); - searching = original.stream().filter(item -> { - Optional> opt = BuiltInRegistries.ITEM.getResourceKey(item); - if (opt.isEmpty()) return false; - Identifier rl = opt.get().identifier(); - boolean matchmod = mods.isEmpty(), matchtag = tags.isEmpty(), matcharg = false; - for (String mod : mods) - matchmod = matchmod || rl.getNamespace().toLowerCase().contains(mod); - for (HolderSet.Named itemTag : itemTags) - matchtag = matchtag || itemTag.stream().anyMatch(tagItem -> tagItem.value() == item); - for (String arg : paths) - matcharg = matcharg || rl.getPath().contains(arg); - return matchmod && matchtag && matcharg; - }).collect(Collectors.toList()); - maxPage = (int) Math.ceil(original.size() / (double) max); - if (page > maxPage - 1) page = Math.max(0, maxPage - 1); - }); - addRenderableWidget(search); - Button add = new Button.Builder(AutoFish.getTranslatableComponent("gui.superfilterscreen.openfilter"), button -> Minecraft.getInstance().setScreenAndShow(new FilterSelectionScreen(this))).pos(this.width / 2 - 75, 60).size(72, 20).build(); - addRenderableWidget(add); - Button done = new Button.Builder(AutoFish.getTranslatableComponent("gui.superfilterscreen.done"), button -> Minecraft.getInstance().setScreenAndShow(parent)).pos(this.width / 2 + 3, 60).size(72, 20).build(); - addRenderableWidget(done); - previous = new Button.Builder(AutoFish.getLiteralComponent("<"), button -> { if (page > 0) page--; }).pos(this.width / 2 - 100, 60).size(20, 20).build(); - previous.visible = false; - addRenderableWidget(previous); - next = new Button.Builder(AutoFish.getLiteralComponent(">"), button -> { if (page < maxPage - 1) page++; }).pos(this.width / 2 + 80, 60).size(20, 20).build(); - next.visible = false; - addRenderableWidget(next); - } - - @Override - //? if >=26.1 { - public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float partialTicks) { - super.extractRenderState(graphics, mouseX, mouseY, partialTicks); - graphics.centeredText(this.font, this.title, this.width / 2, 20, -1); - //? } else { - /*public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) { - super.render(graphics, mouseX, mouseY, partialTicks); - graphics.drawCenteredString(this.font, this.title, this.width / 2, 20, -1); - *///? } - Item[] items = searching.toArray(new Item[0]); - for (int i = page * max; i < Math.min((page + 1) * max, searching.size()); i++) { - Item item = items[i]; - int h = (i % max) / (max / 3); - int k = (i % max) % (max / 3); - ItemStack stack = ItemStack.EMPTY; - if (item != null) stack = new ItemStack(item); - //? if >=26.1 { - if (!stack.isEmpty()) graphics.item(stack, (reducedWidth * h / 3) + 15, (reducedHeight * k / (max / 3)) + 90); - graphics.text(this.font, stack.getDisplayName().getString(), ((reducedWidth * h / 3) + 45), ((reducedHeight * k / (max / 3)) + 95), 0xFFFFFFFF); - //? } else { - /*if (!stack.isEmpty()) graphics.renderItem(stack, (reducedWidth * h / 3) + 15, (reducedHeight * k / (max / 3)) + 90); - graphics.drawString(this.font, stack.getDisplayName().getString(), ((reducedWidth * h / 3) + 45), ((reducedHeight * k / (max / 3)) + 95), 0xFFFFFFFF); - *///? } - //this.font.draw(graphics, stack.getDisplayName().getString(), (float) ((reducedWidth * h / 3) + 45), (float) ((reducedHeight * k / (max / 3)) + 95), Color.WHITE.getRGB()); - } - //? if >=26.1 { - search.extractRenderState(graphics, mouseX, mouseY, partialTicks); - //? } else - //search.render(graphics, mouseX, mouseY, partialTicks); - } - - @Override - public boolean shouldCloseOnEsc() { - return false; - } - - @Override - public boolean keyPressed(KeyEvent ev) { - if (ev.key() == GLFW.GLFW_KEY_ESCAPE) Minecraft.getInstance().setScreenAndShow(parent); - return super.keyPressed(ev); - } - - @Override - public boolean isPauseScreen() { - return false; - } -} diff --git a/src/main/java/in/northwestw/autofish/handler/AutoFishHandler.java b/src/main/java/in/northwestw/autofish/handler/AutoFishHandler.java deleted file mode 100644 index bda940b..0000000 --- a/src/main/java/in/northwestw/autofish/handler/AutoFishHandler.java +++ /dev/null @@ -1,227 +0,0 @@ -package in.northwestw.autofish.handler; - -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import in.northwestw.autofish.AutoFish; -import in.northwestw.autofish.config.Config; -import in.northwestw.autofish.config.gui.SettingsScreen; -import in.northwestw.autofish.keybind.KeyBinds; -import net.minecraft.ChatFormatting; -import net.minecraft.client.Minecraft; -import net.minecraft.client.multiplayer.MultiPlayerGameMode; -import net.minecraft.client.player.LocalPlayer; -import net.minecraft.core.Holder; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.network.chat.Component; -import net.minecraft.resources.Identifier; -import net.minecraft.world.InteractionHand; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.FishingRodItem; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.phys.Vec3; - -import java.util.List; -import java.util.Map; -import java.util.Optional; - -public class AutoFishHandler { - private static final List shouldDrop = Lists.newArrayList(); - private static boolean processingDrop, pendingReelIn, pendingRecast, lastTickFishing, afterDrop; - private static int dropCd, rodSlot; - private static long tick, checkTick; - private static final Map itemsBeforeFished = Maps.newHashMap(); - - public static void onKeyInput() { - Minecraft minecraft = Minecraft.getInstance(); - LocalPlayer player = minecraft.player; - if (KeyBinds.autofish.consumeClick()) { - Config.setAutoFish(!Config.autoFish); - if (player != null) sendOverlayMessage(player, "autofish", Config.autoFish); - } else if (KeyBinds.rodprotect.consumeClick()) { - Config.setRodProtect(!Config.rodProtect); - if (player != null) sendOverlayMessage(player, "rodprotect", Config.rodProtect); - } else if (KeyBinds.autoreplace.consumeClick()) { - Config.setAutoReplace(!Config.autoReplace); - if (player != null) sendOverlayMessage(player, "autoreplace", Config.autoReplace); - } else if (KeyBinds.itemfilter.consumeClick()) { - Config.enableFilter(!Config.allFilters); - if (player != null) sendOverlayMessage(player, "itemfilter", Config.allFilters); - } else if (KeyBinds.settings.consumeClick()) - minecraft.setScreenAndShow(new SettingsScreen()); - } - - public static void onPlayerTick(final Player player) { - if (Minecraft.getInstance().player == null) return; - if (!player.getUUID().equals(Minecraft.getInstance().player.getUUID())) return; - if (checkTick > 0) checkTick--; - else { - checkTick = Config.checkInterval; - if (!pendingRecast) { - if (player.fishing == null) recast(player); - else if (player.fishing.getDeltaMovement().lengthSqr() == 0) pendingReelIn = true; - } - } - if (afterDrop) { - if (tick == 0 && rodSlot != -1) { - player.getInventory().setSelectedSlot(rodSlot); - rodSlot = -1; - } - tick++; - if (tick > 2) { - afterDrop = false; - tick = 0; - } - return; - } - if (pendingReelIn) { - tick++; - if (tick >= Config.reelInDelay) { - reelIn(player); - tick = 0; - pendingReelIn = false; - } - return; - } - if (processingDrop) { - if (dropCd > 0) dropCd--; - dropItem(player); - if (shouldDrop.isEmpty()) { - processingDrop = false; - afterDrop = true; - } - return; - } - if (pendingRecast) { - tick++; - if (tick >= Config.recastDelay) { - checkItem(player); - if (processingDrop) { - tick = 0; - return; - } - recast(player); - tick = 0; - pendingRecast = false; - } - return; - } - if (!Config.autoFish || player.fishing == null) return; - Vec3 vector = player.fishing.getDeltaMovement(); - double x = vector.x(); - double y = vector.y(); - double z = vector.z(); - if (y < -0.075 && !player.level().getFluidState(player.fishing.blockPosition()).isEmpty() && x == 0 && z == 0) - pendingReelIn = true; - } - - private static void reelIn(Player player) { - if (!Config.autoFish) return; - InteractionHand hand = findHandOfRod(player); - if (hand == null) return; - player.getInventory().getNonEquipmentItems().forEach(stack -> { - Identifier rl = BuiltInRegistries.ITEM.getKey(stack.getItem()); - //? if >=26.1 { - itemsBeforeFished.put(rl, itemsBeforeFished.getOrDefault(rl, 0) + stack.count()); - //? } else - //itemsBeforeFished.put(rl, itemsBeforeFished.getOrDefault(rl, 0) + stack.getCount()); - }); - click(player, hand, Minecraft.getInstance().gameMode); - ItemStack fishingRod = player.getItemInHand(hand); - boolean needReplace = false; - if (fishingRod.getMaxDamage() - fishingRod.getDamageValue() < 2) - if (Config.autoReplace) needReplace = true; - else return; - else if (fishingRod.getMaxDamage() - fishingRod.getDamageValue() < 3 && !player.isCreative() && Config.rodProtect) - if (Config.autoReplace) needReplace = true; - else { - Config.autoFish = false; - sendOverlayMessage(player, "forgeautofish", Config.autoFish); - return; - } - if (needReplace) { - AutoFish.LOGGER.info("Fishing rod broke. Finding replacement..."); - boolean found = false; - for (int i = 0; i < 9; i++) { - if (i == player.getInventory().getSelectedSlot()) continue; - ItemStack stack = player.getInventory().getItem(i); - if (stack.getItem() instanceof FishingRodItem) { - if (Config.rodProtect && stack.getMaxDamage() - stack.getDamageValue() < 2) continue; - AutoFish.LOGGER.info("Found fishing rod for replacement"); - player.getInventory().setSelectedSlot(i); - found = true; - break; - } - } - if (!found) return; - } - pendingRecast = true; - } - - private static void recast(Player player) { - if (!Config.autoFish) return; - InteractionHand hand = findHandOfRod(player); - if (hand == null) return; - ItemStack fishingRod = player.getItemInHand(hand); - if (fishingRod.isEmpty()) return; - click(player, hand, Minecraft.getInstance().gameMode); - } - - private static void checkItem(Player player) { - if (!itemsBeforeFished.isEmpty()) { - List items = player.getInventory().getNonEquipmentItems(); - for (String name : Config.filter) { - Identifier rl = Identifier.parse(name); - Optional opt = BuiltInRegistries.ITEM.getOptional(rl); - if (opt.isEmpty()) continue; - Item item = opt.get(); - int newCount = items.stream().filter(stack -> stack.getItem().toString().equals(rl.toString())).mapToInt(ItemStack::getCount).reduce(Integer::sum).orElse(0); - int oldCount = itemsBeforeFished.getOrDefault(rl, 0); - int diff = newCount - oldCount; - for (int ii = 0; ii < diff; ii++) shouldDrop.add(item); - } - itemsBeforeFished.clear(); - if (!shouldDrop.isEmpty()) { - processingDrop = true; - rodSlot = player.getInventory().getSelectedSlot(); - } - } - } - - private static void dropItem(Player player) { - if (dropCd != 10 && dropCd != 0) return; - Item item = shouldDrop.getFirst(); - if (dropCd == 10) { - ((LocalPlayer) player).drop(false); - shouldDrop.remove(item); - return; - } - for (int ii = 0; ii < 9; ii++) { - if (!player.getInventory().getItem(ii).getItem().equals(item)) continue; - player.getInventory().setSelectedSlot(ii); - dropCd = 20; - return; - } - // if item cannot be found in hotbar, just ignore it - shouldDrop.remove(item); - } - - private static void click(Player player, InteractionHand hand, MultiPlayerGameMode controller) { - if (controller == null) return; - controller.useItem(player, hand); - } - - private static InteractionHand findHandOfRod(Player player) { - if (player.getMainHandItem().getItem() instanceof FishingRodItem) return InteractionHand.MAIN_HAND; - else if (player.getOffhandItem().getItem() instanceof FishingRodItem) return InteractionHand.OFF_HAND; - else return null; - } - - private static void sendOverlayMessage(Player player, String key, boolean state) { - Component component = AutoFish.getTranslatableComponent("toggle." + key, AutoFish.getTranslatableComponent("toggle.enable." + state).withStyle(state ? ChatFormatting.GREEN : ChatFormatting.RED)); - //? if >=26.1 { - player.sendOverlayMessage(component); - //? } else - //player.displayClientMessage(component, true); - } -} \ No newline at end of file diff --git a/src/main/java/in/northwestw/autofish/keybind/KeyBinds.java b/src/main/java/in/northwestw/autofish/keybind/KeyBinds.java deleted file mode 100644 index 3be0934..0000000 --- a/src/main/java/in/northwestw/autofish/keybind/KeyBinds.java +++ /dev/null @@ -1,20 +0,0 @@ -package in.northwestw.autofish.keybind; - -import in.northwestw.autofish.AutoFish; -import net.minecraft.client.KeyMapping; -import net.minecraft.resources.Identifier; -import org.lwjgl.glfw.GLFW; - -public class KeyBinds { - - public static KeyMapping autofish, rodprotect, autoreplace, settings, itemfilter; - - static { - KeyMapping.Category cat = KeyMapping.Category.register(Identifier.fromNamespaceAndPath(AutoFish.MOD_ID, "autofish")); - autofish = new KeyMapping(AutoFish.getTranslatableComponent("key.forgeautofish.autofish").getString(), GLFW.GLFW_KEY_MINUS, cat); - rodprotect = new KeyMapping(AutoFish.getTranslatableComponent("key.forgeautofish.rodprotect").getString(), GLFW.GLFW_KEY_BACKSLASH, cat); - autoreplace = new KeyMapping(AutoFish.getTranslatableComponent("key.forgeautofish.autoreplace").getString(), GLFW.GLFW_KEY_RIGHT_BRACKET, cat); - settings = new KeyMapping(AutoFish.getTranslatableComponent("key.forgeautofish.settings").getString(), GLFW.GLFW_KEY_K, cat); - itemfilter = new KeyMapping(AutoFish.getTranslatableComponent("key.forgeautofish.itemfilter").getString(), GLFW.GLFW_KEY_APOSTROPHE, cat); - } -} diff --git a/src/main/resources/assets/forgeautofish/lang/en_us.json b/src/main/resources/assets/forgeautofish/lang/en_us.json deleted file mode 100644 index de66ed5..0000000 --- a/src/main/resources/assets/forgeautofish/lang/en_us.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "key.autofish.autofish": "Toggle AutoFish", - "key.autofish.rodprotect": "Toggle Fishing Rod Protection", - "key.autofish.autoreplace": "Toggle Auto Replace", - "key.autofish.settings": "Open Settings", - "key.autofish.itemfilter": "Toggle Item Filter", - "key.categories.autofish": "AutoFish for Forge", - - "toggle.autofish": "%s AutoFish", - "toggle.rodprotect": "%s Fishing Rod Protection", - "toggle.autoreplace": "%s Auto Replace", - "toggle.itemfilter": "%s Item Filter", - - "warning.autoreplace": "Auto Replace Coming Soon", - - "gui.autofish": "AutoFish Configuration", - "gui.autofish.reelindelay": "Reel-In Delay", - "gui.autofish.recastdelay": "Recast Delay", - "gui.autofish.throwdelay": "Throw Delay", - "gui.autofish.checkinterval": "Check Interval", - "gui.autofish.filter": "Item Filter", - "gui.autofish.done": "Done", - - "gui.setreelindelay": "Set Reel-In Delay", - "gui.setreelindelay.reelindelay": "Reel-In Delay", - "gui.setreelindelay.save": "Save Reel-In Delay", - - "gui.setrecastdelay": "Set Recast Delay", - "gui.setrecastdelay.recastdelay": "Recast Delay", - "gui.setrecastdelay.save": "Save Recast Delay", - - "gui.setthrowdelay": "Set Throw Delay", - "gui.setthrowdelay.throwdelay": "Throw Delay", - "gui.setthrowdelay.save": "Save Throw Delay", - - "gui.setcheckinterval": "Set Check Interval", - "gui.setcheckinterval.checkinterval": "Check Interval", - "gui.setcheckinterval.save": "Save Check Interval", - - "gui.superfilterscreen": "Super Item Filter", - "gui.superfilterscreen.openfilter": "Config", - "gui.superfilterscreen.search": "Search", - "gui.superfilterscreen.done": "Done", - - "gui.filterselection": "Item Filter Configuration", - "gui.filterselection.save": "Save", - "gui.filterselection.cancel": "Cancel", - - "toggle.enable.true": "Enabled", - "toggle.enable.false": "Disabled" -} \ No newline at end of file diff --git a/src/main/resources/assets/forgeautofish/lang/zh_tw.json b/src/main/resources/assets/forgeautofish/lang/zh_tw.json deleted file mode 100644 index 4bd31f5..0000000 --- a/src/main/resources/assets/forgeautofish/lang/zh_tw.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "key.autofish.autofish": "切換 自動釣魚", - "key.autofish.rodprotect": "切換 釣竿保護", - "key.autofish.autoreplace": "切換 自動取代", - "key.autofish.settings": "開啟設定", - "key.autofish.itemfilter": "切換 物品過濾", - "key.categories.autofish": "自動釣魚", - - "toggle.autofish": "%s 自動釣魚", - "toggle.rodprotect": "%s 釣竿保護", - "toggle.autoreplace": "%s 自動取代", - "toggle.itemfilter": "%s 物品過濾", - - "warning.autoreplace": "自動過濾 即將來臨", - - "gui.autofish": "自動釣魚設定", - "gui.autofish.reelindelay": "收竿延遲", - "gui.autofish.recastdelay": "投竿延遲", - "gui.autofish.filter": "物品過濾", - "gui.autofish.done": "完成", - - "gui.setrecastdelay": "投竿延遲設定", - "gui.setrecastdelay.recastdelay": "投竿延遲", - "gui.setrecastdelay.save": "儲存", - - "gui.superfilterscreen": "超級物品過濾器", - "gui.superfilterscreen.openfilter": "設定", - "gui.superfilterscreen.search": "搜尋", - "gui.superfilterscreen.done": "完成", - - "gui.filterselection": "物品過濾設定", - "gui.filterselection.save": "儲存", - "gui.filterselection.cancel": "取消", - - "gui.setreelindelay": "收竿延遲設定", - "gui.setreelindelay.reelindelay": "收竿延遲", - "gui.setreelindelay.save": "儲存", - - "toggle.enable.true": "開啟", - "toggle.enable.false": "關閉" -} \ No newline at end of file diff --git a/src/main/resources/autofish.png b/src/main/resources/autofish.png deleted file mode 100644 index 3346eca73f20eb1da0bf60fbb3e2b167c9dadeb1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 92082 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 Ne0}_vUGNIu`#(x| Date: Tue, 30 Jun 2026 11:28:48 +0800 Subject: [PATCH 18/52] refactor: new repository location --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 88d38b0..1019469 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,7 +9,7 @@ mod.version=8.0.0 mod.author=NorthWestWind mod.description=I like playing survival, but fishing is a boring activity...\nTherefore, I made this mod!\nNow you can AFK Fish like no one else! mod.license=GPL-3.0 -mod.github=https://github.com/North-West-Wind/forge-autofish +mod.github=https://github.com/North-West-Wind/AutoFish # Stonecutter stonecutter_enabled_platforms=fabric, neoforge, forge From 70ec43b36aa56b661245adc14707ad2840e3ac1b Mon Sep 17 00:00:00 2001 From: North-West-Wind Date: Tue, 30 Jun 2026 11:32:22 +0800 Subject: [PATCH 19/52] build: CI settings --- settings.gradle.kts | 5 ----- stonecutter.gradle.kts | 8 ++++++-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/settings.gradle.kts b/settings.gradle.kts index 620cfc0..992d2c2 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,8 +1,3 @@ -val isCi = System.getenv("CI") == "true" -gradle.startParameter.isParallelProjectExecutionEnabled = !isCi -gradle.startParameter.isBuildCacheEnabled = !isCi -gradle.startParameter.isConfigureOnDemand = !isCi - pluginManagement { repositories { gradlePluginPortal() diff --git a/stonecutter.gradle.kts b/stonecutter.gradle.kts index 546c76c..f068b56 100644 --- a/stonecutter.gradle.kts +++ b/stonecutter.gradle.kts @@ -1,10 +1,11 @@ +val IS_CI = System.getenv("CI") == "true" + plugins { id("dev.kikugie.stonecutter") id("net.neoforged.moddev") version "2.0.141" apply false id("net.fabricmc.fabric-loom") version "1.17-SNAPSHOT" apply false id("net.fabricmc.fabric-loom-remap") version "1.17-SNAPSHOT" apply false } -stonecutter active "26.2" stonecutter { parameters { @@ -12,4 +13,7 @@ stonecutter { replace("Identifier", "ResourceLocation") } } -} \ No newline at end of file +} + +if (IS_CI) stonecutter active null +else stonecutter active "26.2" \ No newline at end of file From 8ac2292c77e0b3d0f1e569393cbd6e5d7adb9f42 Mon Sep 17 00:00:00 2001 From: NorthWestWind <45654842+North-West-Wind@users.noreply.github.com> Date: Tue, 30 Jun 2026 11:39:57 +0800 Subject: [PATCH 20/52] ci: build artifacts --- .github/workflows/main.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .github/workflows/main.yml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..cd80bc5 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,26 @@ +name: Build Artifacts + +on: + push: + pull_request: + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Compile with Java 25 + uses: xxanqw/compilation@v3 + with: + java-version: "25" + + - name: Upload Artifacts + uses: actions/upload-artifact@v4 + with: + name: mod-artifacts + path: ./**/versions/**/build/libs From b96ccf2a403fbd83748fa1f2fd397abcb655b856 Mon Sep 17 00:00:00 2001 From: North-West-Wind Date: Tue, 30 Jun 2026 11:54:56 +0800 Subject: [PATCH 21/52] fix: forge 1.21.1 --- .../main/java/in/northwestw/autofish/AutoFishForge.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/forge/src/main/java/in/northwestw/autofish/AutoFishForge.java b/forge/src/main/java/in/northwestw/autofish/AutoFishForge.java index b22a670..53010d3 100644 --- a/forge/src/main/java/in/northwestw/autofish/AutoFishForge.java +++ b/forge/src/main/java/in/northwestw/autofish/AutoFishForge.java @@ -5,7 +5,10 @@ import net.minecraftforge.client.event.InputEvent; import net.minecraftforge.client.event.RegisterKeyMappingsEvent; import net.minecraftforge.event.TickEvent; +//? if >=1.21.11 { import net.minecraftforge.eventbus.api.listener.SubscribeEvent; +//? } else +//import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.LogicalSide; import net.minecraftforge.fml.common.Mod; @@ -33,8 +36,13 @@ public static void inputKey(InputEvent.Key event) { @SubscribeEvent public static void playerTickPre(TickEvent.PlayerTickEvent.Pre event) { + //? if >=1.21.11 { if (event.side() != LogicalSide.CLIENT) return; AutoFishHandler.onPlayerTick(event.player()); + //? } else { + /*if (event.side != LogicalSide.CLIENT) return; + AutoFishHandler.onPlayerTick(event.player); + *///? } } } } \ No newline at end of file From fc836a8bccd59499cfca73ddfde26aad92f46398 Mon Sep 17 00:00:00 2001 From: North-West-Wind Date: Tue, 30 Jun 2026 11:55:06 +0800 Subject: [PATCH 22/52] refactor: use fabric mapping for common --- common/build.gradle.kts | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 04bd887..c9aeeca 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -1,27 +1,29 @@ plugins { id("multiloader-common") - id("net.neoforged.moddev") version "2.0.141" + id("fabric-loom-compat") kotlin("jvm") version "2.2.0" id("com.google.devtools.ksp") version "2.2.0-2.0.2" } -neoForge { - neoFormVersion = commonMod.dep("neoform") - // Automatically enable AccessTransformers if the file exists - val at = rootProject.file("src/main/resources/META-INF/accesstransformer.cfg") - if (at.exists()) { - accessTransformers.from(at.absolutePath) +loom { + if (stonecutter.eval(commonMod.mc, "<=1.21.11")) { + mixin { + useLegacyMixinAp = false + } } } dependencies { - // Fabric and NeoForge both bundle Fabric Mixin, so it is safe to use it in common - // If you need to update, check what version they are using to see what is compatible - // https://github.com/neoforged/NeoForge/blob/26.2.x/gradle.properties#L37 - // https://github.com/FabricMC/fabric-loader/blob/master/gradle.properties#L12 - compileOnly("net.fabricmc:sponge-mixin:0.17.3+mixin.0.8.7") - // Fabric and NeoForge both bundle MixinExtras, so it is safe to use it in common - annotationProcessor("io.github.llamalad7:mixinextras-common:0.5.3") + minecraft("com.mojang:minecraft:${commonMod.mc}") + + if (stonecutter.eval(commonMod.mc, "<=1.21.11")) { + mappings(loom.layered { + officialMojangMappings() + commonMod.depOrNull("parchment")?.let { parchmentVersion -> + parchment("org.parchmentmc.data:parchment-${commonMod.mc}:$parchmentVersion@zip") + } + }) + } } val commonJava: Configuration by configurations.creating { From 12284a63927e1586b29087133918fa62af04453f Mon Sep 17 00:00:00 2001 From: North-West-Wind Date: Tue, 30 Jun 2026 12:26:23 +0800 Subject: [PATCH 23/52] feat: support 1.20.1 --- common/build.gradle.kts | 2 ++ .../java/in/northwestw/autofish/AutoFish.java | 6 ++++++ .../config/gui/FilterSelectionScreen.java | 9 +++++++- .../config/gui/SuperFilterScreen.java | 9 +++++++- .../autofish/handler/AutoFishHandler.java | 5 ++++- fabric/build.gradle.kts | 4 ++++ .../in/northwestw/autofish/AutoFishForge.java | 5 +++++ gradle.properties | 6 +++--- versions/1.20.1/gradle.properties | 21 +++++++++++++++++++ 9 files changed, 61 insertions(+), 6 deletions(-) create mode 100644 versions/1.20.1/gradle.properties diff --git a/common/build.gradle.kts b/common/build.gradle.kts index c9aeeca..b9e8636 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -24,6 +24,8 @@ dependencies { } }) } + + modCompileOnly("net.fabricmc:fabric-loader:${commonMod.dep("fabric_loader")}") } val commonJava: Configuration by configurations.creating { diff --git a/common/src/main/java/in/northwestw/autofish/AutoFish.java b/common/src/main/java/in/northwestw/autofish/AutoFish.java index 6336c65..ad1d2a5 100644 --- a/common/src/main/java/in/northwestw/autofish/AutoFish.java +++ b/common/src/main/java/in/northwestw/autofish/AutoFish.java @@ -2,7 +2,10 @@ import in.northwestw.autofish.config.Config; import net.minecraft.network.chat.MutableComponent; +//? if >=1.21.1 { import net.minecraft.network.chat.contents.PlainTextContents; +//? } else +//import net.minecraft.network.chat.contents.LiteralContents; import net.minecraft.network.chat.contents.TranslatableContents; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -21,6 +24,9 @@ public static MutableComponent getTranslatableComponent(String key, Object... ar } public static MutableComponent getLiteralComponent(String str) { + //? if >=1.21.1 { return MutableComponent.create(new PlainTextContents.LiteralContents(str)); + //? } else + //return MutableComponent.create(new LiteralContents(str)); } } diff --git a/common/src/main/java/in/northwestw/autofish/config/gui/FilterSelectionScreen.java b/common/src/main/java/in/northwestw/autofish/config/gui/FilterSelectionScreen.java index 41247ec..cfcd3e6 100644 --- a/common/src/main/java/in/northwestw/autofish/config/gui/FilterSelectionScreen.java +++ b/common/src/main/java/in/northwestw/autofish/config/gui/FilterSelectionScreen.java @@ -34,7 +34,14 @@ public class FilterSelectionScreen extends Screen { private EditBox search; private final Collection original = BuiltInRegistries.ITEM.stream().toList(); private Collection searching; - private final Set selected = new HashSet<>(Config.filter.stream().map(string -> BuiltInRegistries.ITEM.getOptional(Identifier.parse(string))).filter(Optional::isPresent).map(Optional::get).collect(Collectors.toList())); + private final Set selected = new HashSet<>(Config.filter.stream().map(string -> + BuiltInRegistries.ITEM.getOptional( + //? if >=1.21.1 { + Identifier.parse(string) + //? } else + //new Identifier(string) + ) + ).filter(Optional::isPresent).map(Optional::get).collect(Collectors.toList())); private int page, maxPage = (int) Math.ceil(original.size() / 300.0), max = 300; private boolean clickProcessed = true; private double clickX, clickY; diff --git a/common/src/main/java/in/northwestw/autofish/config/gui/SuperFilterScreen.java b/common/src/main/java/in/northwestw/autofish/config/gui/SuperFilterScreen.java index ca5e04f..5270327 100644 --- a/common/src/main/java/in/northwestw/autofish/config/gui/SuperFilterScreen.java +++ b/common/src/main/java/in/northwestw/autofish/config/gui/SuperFilterScreen.java @@ -57,7 +57,14 @@ protected void init() { reducedHeight = this.height - 90; reducedWidth = this.width - 30; max = /* (int) Math.round(30 * (reducedWidth / 550.0 + reducedHeight / 330.0) / 2.0) */ 30; - original = Config.filter.stream().map(string -> BuiltInRegistries.ITEM.getOptional(Identifier.parse(string))).filter(Optional::isPresent).map(Optional::get).collect(Collectors.toList()); + original = Config.filter.stream().map(string -> + BuiltInRegistries.ITEM.getOptional( + //? if >=1.21.1 { + Identifier.parse(string) + //? } else + //new Identifier(string) + ) + ).filter(Optional::isPresent).map(Optional::get).collect(Collectors.toList()); maxPage = (int) Math.ceil(original.size() / (double) max); searching = original; search = new EditBox(this.font, this.width / 2 - 75, 35, 150, 20, AutoFish.getTranslatableComponent("gui.superfilterscreen.search")) { diff --git a/common/src/main/java/in/northwestw/autofish/handler/AutoFishHandler.java b/common/src/main/java/in/northwestw/autofish/handler/AutoFishHandler.java index 639224a..b95bf68 100644 --- a/common/src/main/java/in/northwestw/autofish/handler/AutoFishHandler.java +++ b/common/src/main/java/in/northwestw/autofish/handler/AutoFishHandler.java @@ -188,7 +188,10 @@ private static void checkItem(Player player) { //? } else //List items = player.getInventory().items; for (String name : Config.filter) { + //? if >=1.21.1 { Identifier rl = Identifier.parse(name); + //? } else + //Identifier rl = new Identifier(name); Optional opt = BuiltInRegistries.ITEM.getOptional(rl); if (opt.isEmpty()) continue; Item item = opt.get(); @@ -210,7 +213,7 @@ private static void checkItem(Player player) { private static void dropItem(Player player) { if (dropCd != 10 && dropCd != 0) return; - Item item = shouldDrop.getFirst(); + Item item = shouldDrop.get(0); if (dropCd == 10) { ((LocalPlayer) player).drop(false); shouldDrop.remove(item); diff --git a/fabric/build.gradle.kts b/fabric/build.gradle.kts index 3e119b6..1c9abf5 100644 --- a/fabric/build.gradle.kts +++ b/fabric/build.gradle.kts @@ -19,6 +19,10 @@ dependencies { modImplementation("net.fabricmc:fabric-loader:${commonMod.dep("fabric_loader")}") modApi("net.fabricmc.fabric-api:fabric-api:${commonMod.dep("fabric_api")}+${commonMod.mc}") + + commonMod.depOrNull("modmenu")?.let { modMenuVersion -> + modImplementation("com.terraformersmc:modmenu:${modMenuVersion}") + } } loom { diff --git a/forge/src/main/java/in/northwestw/autofish/AutoFishForge.java b/forge/src/main/java/in/northwestw/autofish/AutoFishForge.java index 53010d3..385467a 100644 --- a/forge/src/main/java/in/northwestw/autofish/AutoFishForge.java +++ b/forge/src/main/java/in/northwestw/autofish/AutoFishForge.java @@ -35,7 +35,12 @@ public static void inputKey(InputEvent.Key event) { } @SubscribeEvent + //? if >=1.21.1 { public static void playerTickPre(TickEvent.PlayerTickEvent.Pre event) { + //? } else { + /*public static void playerTickPre(TickEvent.PlayerTickEvent event) { + if (event.phase != TickEvent.Phase.START) return; + *///? } //? if >=1.21.11 { if (event.side() != LogicalSide.CLIENT) return; AutoFishHandler.onPlayerTick(event.player()); diff --git a/gradle.properties b/gradle.properties index 1019469..c541f04 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,9 +13,9 @@ mod.github=https://github.com/North-West-Wind/AutoFish # Stonecutter stonecutter_enabled_platforms=fabric, neoforge, forge -stonecutter_enabled_common_versions=26.2, 26.1.2, 1.21.11, 1.21.1 -stonecutter_enabled_fabric_versions=26.2, 26.1.2, 1.21.11, 1.21.1 -stonecutter_enabled_forge_versions=26.2, 26.1.2, 1.21.11, 1.21.1 +stonecutter_enabled_common_versions=26.2, 26.1.2, 1.21.11, 1.21.1, 1.20.1 +stonecutter_enabled_fabric_versions=26.2, 26.1.2, 1.21.11, 1.21.1, 1.20.1 +stonecutter_enabled_forge_versions=26.2, 26.1.2, 1.21.11, 1.21.1, 1.20.1 stonecutter_enabled_neoforge_versions=26.2, 26.1.2, 1.21.11, 1.21.1 # The below field are intentionally left blank, diff --git a/versions/1.20.1/gradle.properties b/versions/1.20.1/gradle.properties new file mode 100644 index 0000000..9f5fb81 --- /dev/null +++ b/versions/1.20.1/gradle.properties @@ -0,0 +1,21 @@ +# Stonecutter +stonecutter_enabled_platforms=fabric, forge + +# Java +java.version=17 + +# Minecraft +minecraft_version=1.20.1 +min_minecraft_version=1.20 + +# Mappings +deps.parchment=2023.09.03 + +# Fabric +deps.fabric_loader=0.19.3 +deps.fabric_api=0.92.9 + +deps.forge=47.4.0 + +# Dependencies +deps.modmenu= \ No newline at end of file From 8d5f5f886dea482cbf582ee9e0d07523928ab9e8 Mon Sep 17 00:00:00 2001 From: North-West-Wind Date: Tue, 30 Jun 2026 15:31:00 +0800 Subject: [PATCH 24/52] ci: publishing & renamed build --- .github/workflows/{main.yml => build.yml} | 2 +- .github/workflows/publish.yml | 96 ++++++++ update.json | 269 ---------------------- 3 files changed, 97 insertions(+), 270 deletions(-) rename .github/workflows/{main.yml => build.yml} (95%) create mode 100644 .github/workflows/publish.yml delete mode 100644 update.json diff --git a/.github/workflows/main.yml b/.github/workflows/build.yml similarity index 95% rename from .github/workflows/main.yml rename to .github/workflows/build.yml index cd80bc5..5edac6f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/build.yml @@ -1,4 +1,4 @@ -name: Build Artifacts +name: Build on: push: diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..8338447 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,96 @@ +name: Publish + +on: + release: + types: [published] + workflow_dispatch: + +concurrency: + group: publish-${{ github.ref }} + cancel-in-progress: false + +jobs: + generate-publish-matrix: + runs-on: ubuntu-latest + name: Generate Publish Matrix + outputs: + matrix: ${{ steps.set-matrix.outputs.matrix }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - id: set-matrix + run: | + platforms=$(awk -F= '/stonecutter_enabled_platforms/{print $2}' gradle.properties | tr -d ' ') + matrix_content="[" + for platform in $(echo $platforms | tr ',' ' '); do + versions=$(awk -F= '/stonecutter_enabled_'$platform'_versions/{print $2}' gradle.properties | tr -d ' ') + if [[ "$platform" == "fabric" ]]; then + supported_loaders="\"fabric\",\"quilt\"" + else + supported_loaders="\"$platform\"" + fi + for version in $(echo $versions | tr ',' ' '); do + matrix_content+="{\"loader\":\"$platform\",\"version\":\"$version\",\"supported_loaders\":\"$supported_loaders\"}," + done + done + echo "matrix=${matrix_content%,}]" >> "$GITHUB_OUTPUT" + + build: + runs-on: ubuntu-latest + name: Build Artifacts + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - uses: xxanqw/compilation@v3 + with: + java-version: "25" + + - uses: actions/upload-artifact@v4 + with: + name: mod-artifacts + path: ./**/versions/**/build/libs + if-no-files-found: error + + publish: + needs: [generate-publish-matrix, build] + runs-on: ubuntu-latest + name: Publish ${{ matrix.loader }} ${{ matrix.version }} + strategy: + fail-fast: false + matrix: ${{ fromJSON(needs.generate-publish-matrix.outputs.matrix) }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - uses: actions/download-artifact@v4 + with: + name: mod-artifacts + + - id: mod-info + run: | + echo "version=$(awk -F= '/mod.version/{print $2}' gradle.properties)" >> "$GITHUB_OUTPUT" + echo "name=$(awk -F= '/mod.name/{print $2}' gradle.properties)" >> "$GITHUB_OUTPUT" + + - uses: Kir-Antipov/mc-publish@v3.3 + with: + modrinth-id: ${{ vars.MODRINTH_ID }} + modrinth-token: ${{ secrets.MODRINTH_TOKEN }} + curseforge-id: ${{ vars.CURSEFORGE_ID }} + curseforge-token: ${{ secrets.CURSEFORGE_TOKEN }} + files: | + ${{ matrix.loader }}/versions/${{ matrix.version }}/build/libs/*.jar + loaders: | + ${{ matrix.supported_loaders }} + game-versions: | + ${{ matrix.version }} + version: ${{ steps.mod-info.outputs.version }} + name: ${{ steps.mod-info.outputs.name }} ${{ steps.mod-info.outputs.version }} + version-type: ${{ github.event.release.prerelease && 'beta' || 'release' }} + changelog: ${{ github.event.release.body || '' }} + retry-attempts: 6 + retry-delay: 30000 diff --git a/update.json b/update.json deleted file mode 100644 index 9f0e99b..0000000 --- a/update.json +++ /dev/null @@ -1,269 +0,0 @@ -{ - "homepage": "https://github.com/North-West-Wind/forge-autofish/", - "1.21.1": { - "7.1.0": "Check Interval also checks if bobber is stuck & Fixed blurry text in config" - }, - "1.21": { - "7.0.0": "1.21 Release", - "7.1.0": "Check Interval also checks if bobber is stuck & Fixed blurry text in config" - }, - "1.20.6": { - "6.0.0": "1.20.1 Release" - }, - "1.20.5": { - "6.0.0": "1.20.1 Release" - }, - "1.20.4": { - "6.0.0": "1.20.1 Release" - }, - "1.20.3": { - "6.0.0": "1.20.1 Release" - }, - "1.20.2": { - "6.0.0": "1.20.1 Release" - }, - "1.20.1": { - "6.0.0": "1.20.1 Release" - }, - "1.19.4": { - "5.0.4": "1.19.4 Release", - "5.1.0": "Allow reeling in from any fluid" - }, - "1.19.3": { - "5.0.3": "Fixed GUI crash in 1.19.3", - "5.1.0": "Allow reeling in from any fluid" - }, - "1.19.2": { - "5.0.2": "1.19 version works for 1.19.2", - "5.1.0": "Allow reeling in from any fluid" - }, - "1.19.1": { - "5.0.2": "1.19 version works for 1.19.1", - "5.1.0": "Allow reeling in from any fluid" - }, - "1.19": { - "5.0.0": "1.19 Release", - "5.0.1": "Ignore item filter outside hotbar", - "5.0.2": "Supports for Forge 41.0.64+", - "5.1.0": "Allow reeling in from any fluid" - }, - "1.18.2": { - "4.0.2": "1.18.2 Release", - "4.1.0": "Added rod cast checker", - "4.1.1": "Ignore item filter outside hotbar", - "4.2.0": "Allow reeling in from any fluid" - }, - "1.18.1": { - "4.0.0": "1.18 Release", - "4.0.1": "Fixed item filter (Single and Multi)", - "4.1.0": "Added rod recast checker" - }, - "1.18": { - "4.0.0": "1.18 Release", - "4.0.1": "Fixed item filter (Single and Multi)", - "4.1.0": "Added rod recast checker" - }, - "1.17.1": { - "3.0.0": "1.17.1 Release", - "3.0.1": "Fixed mod tracking multiple players on server", - "3.0.2": "Fixed search bar crash + Prioritize fishable items" - }, - "1.16.5": { - "2.0.1": "1.16.5 Release", - "2.0.2": "Delay Cap in GUI + Trad. Chinese Translation", - "2.0.3": "Supports Lava Fishing from Other Mods", - "2.1.0": "Auto Replace + Major Bug Fixes", - "2.1.1": "Fixed versions", - "2.1.2": "Fixed mod tracking multiple players on server", - "2.1.3": "Fixed search bar crash + Prioritize fishable items", - "2.2.0": "Allow reeling in from any fluid" - }, - "1.16.4": { - "1.0.6": "1.16.4 Release", - "1.1.0": "Customizable Recast Delay", - "1.1.1": "Fixed Loader Version for 1.16.4", - "1.1.2": "Fixed crash while fishing", - "1.1.3": "(Properly) Fixed Loader Version for 1.16.4", - "2.0.0": "The GUI Update", - "2.0.1": "1.16.5 Release", - "2.0.2": "Delay Cap in GUI + Trad. Chinese Translation", - "2.0.3": "Supports Lava Fishing from Other Mods", - "2.1.0": "Auto Replace + Major Bug Fixes", - "2.1.1": "Fixed versions", - "2.1.2": "Fixed mod tracking multiple players on server", - "2.1.3": "Fixed search bar crash + Prioritize fishable items", - "2.2.0": "Allow reeling in from any fluid" - }, - "1.16.3": { - "1.0.5": "1.16.3 Release", - "1.1.0": "Customizable Recast Delay", - "1.1.1": "Fixed Loader Version for 1.16.4", - "1.1.2": "Fixed crash while fishing", - "1.1.3": "(Properly) Fixed Loader Version for 1.16.4", - "2.0.0": "The GUI Update", - "2.0.1": "1.16.5 Release", - "2.0.2": "Delay Cap in GUI + Trad. Chinese Translation", - "2.0.3": "Supports Lava Fishing from Other Mods", - "2.1.0": "Auto Replace + Major Bug Fixes", - "2.1.1": "Fixed versions", - "2.1.2": "Fixed mod tracking multiple players on server", - "2.1.3": "Fixed search bar crash + Prioritize fishable items", - "2.2.0": "Allow reeling in from any fluid" - }, - "1.16.2": { - "1.0.5": "1.16.2 Release", - "1.1.0": "Customizable Recast Delay", - "1.1.1": "Fixed Loader Version for 1.16.4", - "1.1.2": "Fixed crash while fishing", - "1.1.3": "(Properly) Fixed Loader Version for 1.16.4", - "2.0.0": "The GUI Update", - "2.0.1": "1.16.5 Release", - "2.0.2": "Delay Cap in GUI + Trad. Chinese Translation", - "2.0.3": "Supports Lava Fishing from Other Mods", - "2.1.0": "Auto Replace + Major Bug Fixes", - "2.1.1": "Fixed versions", - "2.1.2": "Fixed mod tracking multiple players on server", - "2.1.3": "Fixed search bar crash + Prioritize fishable items", - "2.2.0": "Allow reeling in from any fluid" - }, - "1.16.1": { - "1.0.0": "Initial Release!", - "1.0.1": "Accuracy Update", - "1.0.2": "Optimization", - "1.0.3": "Homepage Link Update", - "1.0.4": "Accuracy Update #2 + Fixed Grammatical Mistake in Description", - "1.0.5": "Offhand Support + Fishing Rod Protection", - "1.1.0": "Customizable Recast Delay", - "1.1.1": "Fixed Loader Version for 1.16.4", - "1.1.2": "Fixed crash while fishing", - "1.1.3": "(Properly) Fixed Loader Version for 1.16.4", - "2.0.0": "The GUI Update", - "2.0.1": "1.16.5 Release", - "2.0.2": "Delay Cap in GUI + Trad. Chinese Translation", - "2.0.3": "Supports Lava Fishing from Other Mods", - "2.1.0": "Auto Replace + Major Bug Fixes", - "2.1.1": "Fixed versions", - "2.1.2": "Fixed mod tracking multiple players on server", - "2.1.3": "Fixed search bar crash + Prioritize fishable items", - "2.2.0": "Allow reeling in from any fluid" - }, - "1.15.2": { - "1.0.2": "Optimization + 1.15.2 Release", - "1.0.3": "Homepage Link Update", - "1.0.4": "Accuracy Update #2 + Fixed Grammatical Mistake in Description", - "1.1.3": "Catched Up with the 1.16.4 Version", - "2.0.0": "The GUI Update", - "2.0.1": "Delay Cap in GUI + Trad. Chinese Translation" - }, - "1.15.1": { - "1.1.3": "Catched Up with the 1.16.4 Version + 1.15.1 Release", - "2.0.0": "The GUI Update", - "2.0.1": "Delay Cap in GUI + Trad. Chinese Translation" - }, - "1.15": { - "1.1.3": "Catched Up with the 1.16.4 Version + 1.15.1 Release", - "2.0.0": "The GUI Update", - "2.0.1": "Delay Cap in GUI + Trad. Chinese Translation" - }, - "1.14.4": { - "1.0.3": "1.14.4 Release", - "1.0.4": "Accuracy Update #2 + Fixed Grammatical Mistake in Description", - "2.0.0": "The GUI Update" - }, - "1.14.3": { - "2.0.0": "The GUI Update" - }, - "1.14.2": { - "2.0.0": "The GUI Update" - }, - "1.13.2": { - "1.0.3": "1.13.2 Release", - "1.0.4": "Accuracy Update #2 + Fixed Grammatical Mistake in Description" - }, - "1.12.2": { - "1.0.3": "1.12.2 Release", - "1.0.4": "Accuracy Update #2 + Fixed Grammatical Mistake in Description" - }, - "1.11.2": { - "1.0.4": "1.11.2 Release" - }, - "1.10.2": { - "1.0.4": "1.10.2 Release" - }, - "1.9.4": { - "1.0.4": "1.9.4 Release" - }, - "1.8.9": { - "1.0.4": "1.8.9 Release" - }, - "promos": { - "1.21.1-latest": "7.1.0", - "1.21.1-recommended": "7.1.0", - "1.21-latest": "7.1.0", - "1.21-recommended": "7.1.0", - "1.20.6-latest": "6.0.0", - "1.20.6-recommended": "6.0.0", - "1.20.5-latest": "6.0.0", - "1.20.5-recommended": "6.0.0", - "1.20.4-latest": "6.0.0", - "1.20.4-recommended": "6.0.0", - "1.20.3-latest": "6.0.0", - "1.20.3-recommended": "6.0.0", - "1.20.2-latest": "6.0.0", - "1.20.2-recommended": "6.0.0", - "1.20.1-latest": "6.0.0", - "1.20.1-recommended": "6.0.0", - "1.19.4-latest": "5.1.0", - "1.19.4-recommended": "5.1.0", - "1.19.3-latest": "5.1.0", - "1.19.3-recommended": "5.1.0", - "1.19.2-latest": "5.1.0", - "1.19.2-recommended": "5.1.0", - "1.19.1-latest": "5.0.2", - "1.19.1-recommended": "5.0.2", - "1.19-latest": "5.0.2", - "1.19-recommended": "5.0.2", - "1.18.2-latest": "4.2.0", - "1.18.2-recommended": "4.2.0", - "1.18.1-latest": "4.1.0", - "1.18.1-recommended": "4.1.0", - "1.18-latest": "4.1.0", - "1.18-recommended": "4.1.0", - "1.17.1-latest": "3.0.2", - "1.17.1-recommended": "3.0.2", - "1.16.5-latest": "2.2.0", - "1.16.5-recommended": "2.2.0", - "1.16.4-latest": "2.2.0", - "1.16.4-recommended": "2.2.0", - "1.16.3-latest": "2.2.0", - "1.16.3-recommended": "2.2.0", - "1.16.2-latest": "2.2.0", - "1.16.2-recommended": "2.2.0", - "1.16.1-latest": "2.2.0", - "1.16.1-recommended": "2.2.0", - "1.15.2-latest": "2.0.0", - "1.15.2-recommended": "2.0.0", - "1.15.1-latest": "2.0.0", - "1.15.1-recommended": "2.0.0", - "1.15-latest": "2.0.0", - "1.15-recommended": "2.0.0", - "1.14.4-latest": "2.0.0", - "1.14.4-recommended": "2.0.0", - "1.14.3-latest": "2.0.0", - "1.14.3-recommended": "2.0.0", - "1.14.2-latest": "2.0.0", - "1.14.2-recommended": "2.0.0", - "1.13.2-latest": "1.0.4", - "1.13.2-recommended": "1.0.4", - "1.12.2-latest": "1.0.4", - "1.12.2-recommended": "1.0.4", - "1.11.2-latest": "1.0.4", - "1.11.2-recommended": "1.0.4", - "1.10.2-latest": "1.0.4", - "1.10.2-recommended": "1.0.4", - "1.9.4-latest": "1.0.4", - "1.9.4-recommended": "1.0.4", - "1.8.9-latest": "1.0.4", - "1.8.9-recommended": "1.0.4" - } -} From d26ca3ba832ecaf6af7a0e4b99a87cc66621ec14 Mon Sep 17 00:00:00 2001 From: North-West-Wind Date: Tue, 30 Jun 2026 16:02:29 +0800 Subject: [PATCH 25/52] ci: steal Faboslav's setup --- .github/scripts/generate-publish-matrix.sh | 24 ++++++++ .github/scripts/parse-gradle-properties.sh | 29 ++++++++++ .github/workflows/publish.yml | 66 ++++++++-------------- 3 files changed, 76 insertions(+), 43 deletions(-) create mode 100644 .github/scripts/generate-publish-matrix.sh create mode 100644 .github/scripts/parse-gradle-properties.sh diff --git a/.github/scripts/generate-publish-matrix.sh b/.github/scripts/generate-publish-matrix.sh new file mode 100644 index 0000000..691ec8d --- /dev/null +++ b/.github/scripts/generate-publish-matrix.sh @@ -0,0 +1,24 @@ +#!/bin/bash +# Stolen from Faboslav +# https://github.com/Faboslav/friends-and-foes/blob/master/.github/scripts/generate-publish-matrix.sh + +matrix_content="{\"include\":[" +enabled_platforms=$(awk -F= '/stonecutter_enabled_platforms/{print $2}' gradle.properties | tr -d ' ') + +for platform in $(echo $enabled_platforms | tr ',' ' '); do + versions=$(awk -F= '/stonecutter_enabled_'$platform'_versions/{print $2}' gradle.properties | tr -d ' ') + for version in $(echo $versions | tr ',' ' '); do + if [[ "$platform" == "fabric" ]]; then + supported_loaders="\"fabric\",\"quilt\"" + else + supported_loaders="\"$platform\"" + fi + + matrix_entry="{\"loader\":\"$platform\",\"version\":\"$version\",\"supported_loaders\":[$supported_loaders]}," + matrix_content+="$matrix_entry" + done +done + +matrix_content="${matrix_content%,}]}" +echo "Generated matrix: $matrix_content" +echo "matrix=$matrix_content" >> $GITHUB_OUTPUT \ No newline at end of file diff --git a/.github/scripts/parse-gradle-properties.sh b/.github/scripts/parse-gradle-properties.sh new file mode 100644 index 0000000..e94dec4 --- /dev/null +++ b/.github/scripts/parse-gradle-properties.sh @@ -0,0 +1,29 @@ +#!/bin/bash +# Stolen from Faboslav +# https://github.com/Faboslav/friends-and-foes/blob/master/.github/scripts/parse-gradle-properties.sh + +version=${1:-} + +parse_properties_file() { + local file=$1 + while IFS='=' read -r key value || [[ -n "$key" ]]; do + key=$(echo "$key" | awk '{$1=$1;print}') + value=$(echo "$value" | awk '{$1=$1;print}') + + if [[ -z "$key" || "$key" =~ ^# || "$key" == "org.gradle.jvmargs" ]]; then + continue + fi + + key=$(echo "$key" | tr '[:lower:]' '[:upper:]' | tr -c '[:alnum:]' '_') + key=$(echo "$key" | sed 's/_$//') + + echo "${key}=${value}" + echo "${key}=${value}" >> "$GITHUB_OUTPUT" + done < "$file" +} + +parse_properties_file gradle.properties + +if [[ -n "$version" ]]; then + parse_properties_file "versions/${version}/gradle.properties" +fi \ No newline at end of file diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 8338447..76cb2bd 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -21,45 +21,14 @@ jobs: fetch-depth: 0 - id: set-matrix - run: | - platforms=$(awk -F= '/stonecutter_enabled_platforms/{print $2}' gradle.properties | tr -d ' ') - matrix_content="[" - for platform in $(echo $platforms | tr ',' ' '); do - versions=$(awk -F= '/stonecutter_enabled_'$platform'_versions/{print $2}' gradle.properties | tr -d ' ') - if [[ "$platform" == "fabric" ]]; then - supported_loaders="\"fabric\",\"quilt\"" - else - supported_loaders="\"$platform\"" - fi - for version in $(echo $versions | tr ',' ' '); do - matrix_content+="{\"loader\":\"$platform\",\"version\":\"$version\",\"supported_loaders\":\"$supported_loaders\"}," - done - done - echo "matrix=${matrix_content%,}]" >> "$GITHUB_OUTPUT" - - build: - runs-on: ubuntu-latest - name: Build Artifacts - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - uses: xxanqw/compilation@v3 - with: - java-version: "25" - - - uses: actions/upload-artifact@v4 - with: - name: mod-artifacts - path: ./**/versions/**/build/libs - if-no-files-found: error + run: ./.github/scripts/generate-publish-matrix.sh publish: - needs: [generate-publish-matrix, build] + needs: generate-publish-matrix runs-on: ubuntu-latest name: Publish ${{ matrix.loader }} ${{ matrix.version }} strategy: + max-parallel: 3 fail-fast: false matrix: ${{ fromJSON(needs.generate-publish-matrix.outputs.matrix) }} steps: @@ -67,14 +36,25 @@ jobs: with: fetch-depth: 0 - - uses: actions/download-artifact@v4 + - name: "Set up JDK" + uses: actions/setup-java@v4 with: - name: mod-artifacts + java-version: 25 + distribution: "adopt" + + - name: "Setup Gradle" + uses: gradle/actions/setup-gradle@v4 + with: + cache-read-only: true + gradle-version: wrapper + add-job-summary: 'on-failure' + + - name: "Run build" + run: ./gradlew ${{ matrix.loader }}:${{ matrix.version }}:build - - id: mod-info - run: | - echo "version=$(awk -F= '/mod.version/{print $2}' gradle.properties)" >> "$GITHUB_OUTPUT" - echo "name=$(awk -F= '/mod.name/{print $2}' gradle.properties)" >> "$GITHUB_OUTPUT" + - name: "Parse gradle properties" + id: gradle-properties + run: ./.github/scripts/parse-gradle-properties.sh ${{ matrix.version }} - uses: Kir-Antipov/mc-publish@v3.3 with: @@ -87,9 +67,9 @@ jobs: loaders: | ${{ matrix.supported_loaders }} game-versions: | - ${{ matrix.version }} - version: ${{ steps.mod-info.outputs.version }} - name: ${{ steps.mod-info.outputs.name }} ${{ steps.mod-info.outputs.version }} + >=${{ steps.gradle-properties.outputs.MIN_MINECRAFT_VERSION }} <=${{ steps.gradle-properties.outputs.MINECRAFT_VERSION }} + version: ${{ steps.gradle-properties.outputs.MOD_VERSION }}-${{ matrix.version }}-${{ matrix.loader }} + name: ${{ steps.gradle-properties.outputs.MOD_NAME }} ${{ steps.gradle-properties.outputs.MOD_VERSION }} (${{ matrix.version }}-${{ matrix.loader }}) version-type: ${{ github.event.release.prerelease && 'beta' || 'release' }} changelog: ${{ github.event.release.body || '' }} retry-attempts: 6 From daf39b811957658182666b477e82d5df739425c9 Mon Sep 17 00:00:00 2001 From: North-West-Wind Date: Tue, 30 Jun 2026 17:03:24 +0800 Subject: [PATCH 26/52] feat: support 1.19.4 & 1.19.2 --- .../in/northwestw/autofish/config/Config.java | 3 +- .../config/gui/FilterSelectionScreen.java | 102 +++++++++++++----- .../config/gui/LongSettingScreen.java | 20 +++- .../autofish/config/gui/ScreenHelper.java | 9 ++ .../autofish/config/gui/SettingsScreen.java | 38 ++++--- .../config/gui/SuperFilterScreen.java | 68 ++++++++---- .../autofish/handler/AutoFishHandler.java | 17 ++- gradle.properties | 6 +- versions/1.19.2/gradle.properties | 21 ++++ versions/1.19.4/gradle.properties | 21 ++++ 10 files changed, 231 insertions(+), 74 deletions(-) create mode 100644 versions/1.19.2/gradle.properties create mode 100644 versions/1.19.4/gradle.properties diff --git a/common/src/main/java/in/northwestw/autofish/config/Config.java b/common/src/main/java/in/northwestw/autofish/config/Config.java index 90982f7..ca5d0d9 100644 --- a/common/src/main/java/in/northwestw/autofish/config/Config.java +++ b/common/src/main/java/in/northwestw/autofish/config/Config.java @@ -9,6 +9,7 @@ import java.io.IOException; import java.io.PrintWriter; import java.util.List; +import java.util.stream.Stream; public class Config { private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); @@ -72,7 +73,7 @@ public static void load() { if (json.has("all_filters")) allFilters = json.get("all_filters").getAsBoolean(); if (json.has("filter")) - filter = json.getAsJsonArray("filter").asList().stream().map(JsonElement::getAsString).toList(); + filter = Stream.of(json.getAsJsonArray("filter")).map(JsonElement::getAsString).toList(); // validate if (recastDelay < RECAST_DELAY_RANGE[0] || recastDelay > RECAST_DELAY_RANGE[1]) { diff --git a/common/src/main/java/in/northwestw/autofish/config/gui/FilterSelectionScreen.java b/common/src/main/java/in/northwestw/autofish/config/gui/FilterSelectionScreen.java index cfcd3e6..c9708a3 100644 --- a/common/src/main/java/in/northwestw/autofish/config/gui/FilterSelectionScreen.java +++ b/common/src/main/java/in/northwestw/autofish/config/gui/FilterSelectionScreen.java @@ -4,11 +4,12 @@ import com.mojang.datafixers.util.Pair; import in.northwestw.autofish.AutoFish; import in.northwestw.autofish.config.Config; -import net.minecraft.client.Minecraft; //? if >=26.1 { import net.minecraft.client.gui.GuiGraphicsExtractor; -//?} else +//?} elif >=1.20.1 { //import net.minecraft.client.gui.GuiGraphics; +//? } else +//import com.mojang.blaze3d.vertex.PoseStack; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.components.EditBox; import net.minecraft.client.gui.screens.Screen; @@ -17,7 +18,10 @@ import net.minecraft.client.input.MouseButtonEvent; //? } import net.minecraft.core.HolderSet; +//? if >=1.19.4 { import net.minecraft.core.registries.BuiltInRegistries; +//? } else +//import net.minecraft.core.Registry; import net.minecraft.resources.Identifier; import net.minecraft.resources.ResourceKey; import net.minecraft.world.item.Item; @@ -32,15 +36,18 @@ public class FilterSelectionScreen extends Screen { private final Screen parent; private EditBox search; + //? if >=1.19.4 { private final Collection original = BuiltInRegistries.ITEM.stream().toList(); + //? } else + //private final Collection original = Registry.ITEM.stream().toList(); private Collection searching; private final Set selected = new HashSet<>(Config.filter.stream().map(string -> - BuiltInRegistries.ITEM.getOptional( - //? if >=1.21.1 { - Identifier.parse(string) - //? } else - //new Identifier(string) - ) + //? if >=1.21.1 { + BuiltInRegistries.ITEM.getOptional(Identifier.parse(string)) + //? } elif >=1.19.4 { + //BuiltInRegistries.ITEM.getOptional(new Identifier(string)) + //? } else + //Optional.of(Registry.ITEM.get(new Identifier(string))) ).filter(Optional::isPresent).map(Optional::get).collect(Collectors.toList())); private int page, maxPage = (int) Math.ceil(original.size() / 300.0), max = 300; private boolean clickProcessed = true; @@ -85,15 +92,24 @@ public boolean mouseClicked(MouseButtonEvent ev, boolean p_430750_) { } //? if >=1.21.11 { List> itemTags = BuiltInRegistries.ITEM.getTags().filter(tag -> tags.stream().anyMatch(t -> tag.key().location().getPath().contains(t))).toList(); - //? } else + //? } elif >=1.19.4 { //List> itemTags = BuiltInRegistries.ITEM.getTags().map(Pair::getSecond).filter(tag -> tags.stream().anyMatch(t -> tag.key().location().getPath().contains(t))).toList(); + //? } else + //List> itemTags = Registry.ITEM.getTags().map(Pair::getSecond).filter(tag -> tags.stream().anyMatch(t -> tag.key().location().getPath().contains(t))).toList(); searching = original.stream().filter(item -> { + //? if >=1.21.11 { Optional> opt = BuiltInRegistries.ITEM.getResourceKey(item); if (opt.isEmpty()) return false; - //? if >=1.21.11 { Identifier rl = opt.get().identifier(); - //? } else - //Identifier rl = opt.get().location(); + //? } elif >=1.19.4 { + /*Optional> opt = BuiltInRegistries.ITEM.getResourceKey(item); + if (opt.isEmpty()) return false; + Identifier rl = opt.get().location(); + *///? } else { + /*Optional> opt = Registry.ITEM.getResourceKey(item); + if (opt.isEmpty()) return false; + Identifier rl = opt.get().location(); + *///? } boolean matchmod = mods.isEmpty(), matchtag = tags.isEmpty(), matcharg = false; for (String mod : mods) matchmod = matchmod || rl.getNamespace().toLowerCase().contains(mod); @@ -107,18 +123,21 @@ public boolean mouseClicked(MouseButtonEvent ev, boolean p_430750_) { if (page > maxPage - 1) page = Math.max(0, maxPage - 1); }); addRenderableWidget(search); - Button add = new Button.Builder(AutoFish.getTranslatableComponent("gui.filterselection.save"), button -> { + Button add = ScreenHelper.makeButton(this.width / 2 - 75, 60, 72, 20, AutoFish.getTranslatableComponent("gui.filterselection.save"), button -> { + //? if >=1.19.4 { List items = selected.stream().map(item -> BuiltInRegistries.ITEM.getKey(item).toString()).collect(Collectors.toList()); + //? } else + //List items = selected.stream().map(item -> Registry.ITEM.getKey(item).toString()).collect(Collectors.toList()); Config.setFilter(items); ScreenHelper.showScreen(parent); - }).pos(this.width / 2 - 75, 60).size(72, 20).build(); + }); addRenderableWidget(add); - Button done = new Button.Builder(AutoFish.getTranslatableComponent("gui.filterselection.cancel"), button -> ScreenHelper.showScreen(parent)).pos(this.width / 2 + 3, 60).size(72, 20).build(); + Button done = ScreenHelper.makeButton(this.width / 2 + 3, 60, 72, 20, AutoFish.getTranslatableComponent("gui.filterselection.cancel"), button -> ScreenHelper.showScreen(parent)); addRenderableWidget(done); - previous = new Button.Builder(AutoFish.getLiteralComponent("<"), button -> { if (page > 0) page--; }).pos(this.width / 2 - 100, 60).size(20, 20).build(); + previous = ScreenHelper.makeButton(this.width / 2 - 100, 60, 20, 20, AutoFish.getLiteralComponent("<"), button -> { if (page > 0) page--; }); previous.visible = false; addRenderableWidget(previous); - next = new Button.Builder(AutoFish.getLiteralComponent(">"), button -> { if (page < maxPage - 1) page++; }).pos(this.width / 2 + 80, 60).size(20, 20).build(); + next = ScreenHelper.makeButton(this.width / 2 + 80, 60, 20, 20, AutoFish.getLiteralComponent(">"), button -> { if (page < maxPage - 1) page++; }); next.visible = false; addRenderableWidget(next); } @@ -128,19 +147,31 @@ public boolean mouseClicked(MouseButtonEvent ev, boolean p_430750_) { public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float partialTicks) { super.extractRenderState(graphics, mouseX, mouseY, partialTicks); graphics.centeredText(this.font, this.title, this.width / 2, 20, -1); - //?} else { + //?} elif >=1.20.1 { /*public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) { super.render(graphics, mouseX, mouseY, partialTicks); graphics.drawCenteredString(this.font, this.title, this.width / 2, 20, -1); - *///?} + *///?} else { + /*public void render(PoseStack poseStack, int mouseX, int mouseY, float partialTicks) { + super.render(poseStack, mouseX, mouseY, partialTicks); + this.renderBackground(poseStack); + drawCenteredString(poseStack, this.font, this.title, this.width / 2, 20, -1); + *///? } Collection searchingCopy = Lists.newArrayList(); Collection prioritized = searching.stream().filter(item -> { + //? if >=1.21.11 { Optional> opt = BuiltInRegistries.ITEM.getResourceKey(item); if (opt.isEmpty()) return false; - //? if >=1.21.11 { Identifier rl = opt.get().identifier(); - //? } else - //Identifier rl = opt.get().location(); + //? } elif >=1.19.4 { + /*Optional> opt = BuiltInRegistries.ITEM.getResourceKey(item); + if (opt.isEmpty()) return false; + Identifier rl = opt.get().location(); + *///? } else { + /*Optional> opt = Registry.ITEM.getResourceKey(item); + if (opt.isEmpty()) return false; + Identifier rl = opt.get().location(); + *///? } boolean pri = Config.prioritize.contains(rl.toString()); if (!pri) searchingCopy.add(item); return pri; @@ -157,27 +188,42 @@ public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mo if (!stack.isEmpty()) { //? if >=26.1 { graphics.item(stack, x, y); - //? } else + //? } elif >=1.20.1 { //graphics.renderItem(stack, x, y); + //? } elif >=1.19.4 { + //itemRenderer.renderGuiItem(poseStack, stack, x, y); + //? } else + //itemRenderer.renderGuiItem(stack, x, y); if (!clickProcessed && isMouseInRange(clickX, clickY, x, y, x+16, y+16)) { if (selected.contains(item)) selected.remove(item); else selected.add(item); clickProcessed = true; } + //? if >=1.20.1 { if (selected.contains(item)) graphics.fillGradient(x - 2, y - 2, x + 18, y + 18, 0xFF00FF00, 0xFF00FF00); else if (isMouseInRange(mouseX, mouseY, x, y,x + 16, y + 16)) graphics.fillGradient(x - 2, y - 2, x + 18, y + 18, 0xFFC0C0C0, 0xFFC0C0C0); + //? } else { + /*if (selected.contains(item)) fillGradient(poseStack, x - 2, y - 2, x + 18, y + 18, 0xFF00FF00, 0xFF00FF00); + else if (isMouseInRange(mouseX, mouseY, x, y,x + 16, y + 16)) fillGradient(poseStack, x - 2, y - 2, x + 18, y + 18, 0xFFC0C0C0, 0xFFC0C0C0); + *///? } //if (isMouseInRange(mouseX, mouseY, x, y,x + 16, y + 16)) graphics.item(this.font, stack, mouseX, mouseY); //? if >=26.1 { graphics.item(stack, x, y); - //? } else + //? } elif >=1.20.1 { //graphics.renderItem(stack, x, y); + //? } elif >=1.19.4 { + //itemRenderer.renderGuiItem(poseStack, stack, x, y); + //? } else + //itemRenderer.renderGuiItem(stack, x, y); } } } //? if >=26.1 { search.extractRenderState(graphics, mouseX, mouseY, partialTicks); - //?} else + //?} elif >=1.20.1 { //search.render(graphics, mouseX, mouseY, partialTicks); + //? } else + //search.render(poseStack, mouseX, mouseY, partialTicks); } private boolean isMouseInRange(double mouseX, double mouseY, int x1, int y1, int x2, int y2) { @@ -205,7 +251,11 @@ public boolean keyPressed(KeyEvent ev) { /*public boolean keyPressed(int keyCode, int scanCode, int modifiers) { if (keyCode == GLFW.GLFW_KEY_ESCAPE) { if (!search.isFocused()) ScreenHelper.showScreen(parent); - else search.setFocused(false); + else + //? if >=1.19.4 { + search.setFocused(false); + //? } else + //search.setFocus(false); } return super.keyPressed(keyCode, scanCode, modifiers); } diff --git a/common/src/main/java/in/northwestw/autofish/config/gui/LongSettingScreen.java b/common/src/main/java/in/northwestw/autofish/config/gui/LongSettingScreen.java index 6e04d32..539aef0 100644 --- a/common/src/main/java/in/northwestw/autofish/config/gui/LongSettingScreen.java +++ b/common/src/main/java/in/northwestw/autofish/config/gui/LongSettingScreen.java @@ -4,8 +4,10 @@ import net.minecraft.client.Minecraft; //? if >=26.1 { import net.minecraft.client.gui.GuiGraphicsExtractor; - //?} else + //?} elif >=1.20.1 { //import net.minecraft.client.gui.GuiGraphics; +//? } else +//import com.mojang.blaze3d.vertex.PoseStack; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.components.EditBox; import net.minecraft.client.gui.screens.Screen; @@ -55,7 +57,7 @@ public boolean mouseClicked(MouseButtonEvent ev, boolean p_430750_) { }; editBox.setValue(Long.toString(this.supplier.get())); addRenderableWidget(editBox); - Button save = new Button.Builder(AutoFish.getTranslatableComponent("gui." + this.middleTranslationKey + ".save"), button -> { + Button save = ScreenHelper.makeButton(this.width / 2 - 75, this.height / 2, 150, 20, AutoFish.getTranslatableComponent("gui." + this.middleTranslationKey + ".save"), button -> { if (!isNumeric(editBox.getValue())) editBox.setValue(Long.toString(this.supplier.get())); else { long delay = Long.parseLong(editBox.getValue()); @@ -65,7 +67,7 @@ public boolean mouseClicked(MouseButtonEvent ev, boolean p_430750_) { ScreenHelper.showScreen(parent); } } - }).pos(this.width / 2 - 75, this.height / 2).size(150, 20).build(); + }); addRenderableWidget(save); } @@ -89,12 +91,20 @@ public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mo super.extractRenderState(graphics, mouseX, mouseY, partialTicks); graphics.centeredText(this.font, this.title, this.width / 2, 20, -1); this.editBox.extractRenderState(graphics, mouseX, mouseY, partialTicks); - }//?} else { + } + //? } elif >=1.20.1 { /*public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) { super.render(graphics, mouseX, mouseY, partialTicks); graphics.drawCenteredString(this.font, this.title, this.width / 2, 20, -1); this.editBox.render(graphics, mouseX, mouseY, partialTicks); - }*///?} + } + *///? } else { + /*public void render(PoseStack poseStack, int mouseX, int mouseY, float partialTicks) { + super.render(poseStack, mouseX, mouseY, partialTicks); + drawCenteredString(poseStack, this.font, this.title, this.width / 2, 20, -1); + this.editBox.render(poseStack, mouseX, mouseY, partialTicks); + } + *///? } @Override public boolean shouldCloseOnEsc() { diff --git a/common/src/main/java/in/northwestw/autofish/config/gui/ScreenHelper.java b/common/src/main/java/in/northwestw/autofish/config/gui/ScreenHelper.java index b85a015..f7d309c 100644 --- a/common/src/main/java/in/northwestw/autofish/config/gui/ScreenHelper.java +++ b/common/src/main/java/in/northwestw/autofish/config/gui/ScreenHelper.java @@ -1,7 +1,9 @@ package in.northwestw.autofish.config.gui; import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.Component; public class ScreenHelper { public static void showScreen(Screen screen) { @@ -10,4 +12,11 @@ public static void showScreen(Screen screen) { //? } else //Minecraft.getInstance().setScreen(screen); } + + public static Button makeButton(int x, int y, int width, int height, Component label, Button.OnPress onPress) { + //? if >=1.19.4 { + return new Button.Builder(label, onPress).pos(x, y).size(width, height).build(); + //? } else + //return new Button(x, y, width, height, label, onPress); + } } diff --git a/common/src/main/java/in/northwestw/autofish/config/gui/SettingsScreen.java b/common/src/main/java/in/northwestw/autofish/config/gui/SettingsScreen.java index 7bb7c6a..ec7ffb7 100644 --- a/common/src/main/java/in/northwestw/autofish/config/gui/SettingsScreen.java +++ b/common/src/main/java/in/northwestw/autofish/config/gui/SettingsScreen.java @@ -2,13 +2,18 @@ import in.northwestw.autofish.AutoFish; import in.northwestw.autofish.config.Config; -import net.minecraft.client.Minecraft; //? if >=26.1 { import net.minecraft.client.gui.GuiGraphicsExtractor; - //?} else + //?} elif >=1.20.1 { //import net.minecraft.client.gui.GuiGraphics; +//? } else +//import com.mojang.blaze3d.vertex.PoseStack; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.Component; +import org.apache.commons.lang3.tuple.Pair; + +import java.util.List; public class SettingsScreen extends Screen { private static final int WIDTH = 150, HEIGHT = 20, MARGIN = 5; @@ -24,36 +29,41 @@ public boolean isPauseScreen() { @Override protected void init() { - Button.Builder[] builders = { - new Button.Builder(AutoFish.getTranslatableComponent("gui.autofish.recastdelay"), button -> + List> pairs = List.of( + Pair.of(AutoFish.getTranslatableComponent("gui.autofish.recastdelay"), button -> ScreenHelper.showScreen(new LongSettingScreen(this, "setrecastdelay", () -> Config.recastDelay, (newDelay) -> Config.recastDelay = newDelay, Config.RECAST_DELAY_RANGE[0], Config.RECAST_DELAY_RANGE[1]))), - new Button.Builder(AutoFish.getTranslatableComponent("gui.autofish.reelindelay"), button -> + Pair.of(AutoFish.getTranslatableComponent("gui.autofish.reelindelay"), button -> ScreenHelper.showScreen(new LongSettingScreen(this, "setreelindelay", () -> Config.reelInDelay, (newDelay) -> Config.reelInDelay = newDelay, Config.REEL_IN_DELAY_RANGE[0], Config.REEL_IN_DELAY_RANGE[1]))), - new Button.Builder(AutoFish.getTranslatableComponent("gui.autofish.throwdelay"), button -> + Pair.of(AutoFish.getTranslatableComponent("gui.autofish.throwdelay"), button -> ScreenHelper.showScreen(new LongSettingScreen(this, "setthrowdelay", () -> Config.throwDelay, (newDelay) -> Config.throwDelay = newDelay, Config.THROW_DELAY_RANGE[0], Config.THROW_DELAY_RANGE[1]))), - new Button.Builder(AutoFish.getTranslatableComponent("gui.autofish.checkinterval"), button -> + Pair.of(AutoFish.getTranslatableComponent("gui.autofish.checkinterval"), button -> ScreenHelper.showScreen(new LongSettingScreen(this, "setcheckinterval", () -> Config.checkInterval, (newInterval) -> Config.checkInterval = newInterval, Config.CHECK_INTERVAL_RANGE[0], Config.CHECK_INTERVAL_RANGE[1]))), - new Button.Builder(AutoFish.getTranslatableComponent("gui.autofish.filter"), button -> + Pair.of(AutoFish.getTranslatableComponent("gui.autofish.filter"), button -> ScreenHelper.showScreen(new SuperFilterScreen(this))) - }; + ); - for (int ii = 0; ii < builders.length; ii++) { - Button button = builders[ii].pos(this.width / 2 - WIDTH / 2, this.height / 2 + (ii - builders.length / 2) * (HEIGHT + MARGIN)).size(WIDTH, HEIGHT).build(); + for (int ii = 0; ii < pairs.size(); ii++) { + Pair pair = pairs.get(ii); + Button button = ScreenHelper.makeButton(this.width / 2 - WIDTH / 2, this.height / 2 + (ii - pairs.size() / 2) * (HEIGHT + MARGIN), WIDTH, HEIGHT, pair.getLeft(), pair.getRight()); addRenderableWidget(button); } - Button done = new Button.Builder(AutoFish.getTranslatableComponent("gui.autofish.done"), button -> onClose()).pos(this.width / 2 - 75, this.height - 25).size(150, 20).build(); + Button done = ScreenHelper.makeButton(this.width / 2 - 75, this.height - 25, 150, 20, AutoFish.getTranslatableComponent("gui.autofish.done"), button -> onClose()); addRenderableWidget(done); } @Override - //? if >=26.1 { + //? if >=26.1 { public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float partialTicks) { super.extractRenderState(graphics, mouseX, mouseY, partialTicks); graphics.centeredText(this.font, this.title, this.width / 2, 20, -1); - }//?} else { + }//?} elif >=1.20.1 { /*public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) { super.render(graphics, mouseX, mouseY, partialTicks); graphics.drawCenteredString(this.font, this.title, this.width / 2, 20, -1); + }*///?} else { + /*public void render(PoseStack poseStack, int mouseX, int mouseY, float partialTicks) { + super.render(poseStack, mouseX, mouseY, partialTicks); + drawCenteredString(poseStack, this.font, this.title, this.width / 2, 20, -1); }*///?} } diff --git a/common/src/main/java/in/northwestw/autofish/config/gui/SuperFilterScreen.java b/common/src/main/java/in/northwestw/autofish/config/gui/SuperFilterScreen.java index 5270327..5cbb472 100644 --- a/common/src/main/java/in/northwestw/autofish/config/gui/SuperFilterScreen.java +++ b/common/src/main/java/in/northwestw/autofish/config/gui/SuperFilterScreen.java @@ -4,11 +4,12 @@ import com.mojang.datafixers.util.Pair; import in.northwestw.autofish.AutoFish; import in.northwestw.autofish.config.Config; -import net.minecraft.client.Minecraft; //? if >=26.1 { import net.minecraft.client.gui.GuiGraphicsExtractor; - //?} else +//?} elif >=1.20.1 { //import net.minecraft.client.gui.GuiGraphics; +//? } else +//import com.mojang.blaze3d.vertex.PoseStack; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.components.EditBox; import net.minecraft.client.gui.screens.Screen; @@ -17,17 +18,18 @@ import net.minecraft.client.input.MouseButtonEvent; //? } import net.minecraft.core.HolderSet; +//? if >=1.19.4 { import net.minecraft.core.registries.BuiltInRegistries; +//? } else +//import net.minecraft.core.Registry; import net.minecraft.resources.Identifier; import net.minecraft.resources.ResourceKey; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import org.lwjgl.glfw.GLFW; -import java.util.Arrays; -import java.util.Collection; import java.util.List; -import java.util.Optional; +import java.util.*; import java.util.stream.Collectors; public class SuperFilterScreen extends Screen { @@ -58,12 +60,12 @@ protected void init() { reducedWidth = this.width - 30; max = /* (int) Math.round(30 * (reducedWidth / 550.0 + reducedHeight / 330.0) / 2.0) */ 30; original = Config.filter.stream().map(string -> - BuiltInRegistries.ITEM.getOptional( - //? if >=1.21.1 { - Identifier.parse(string) - //? } else - //new Identifier(string) - ) + //? if >=1.21.1 { + BuiltInRegistries.ITEM.getOptional(Identifier.parse(string)) + //? } elif >=1.19.4 { + //BuiltInRegistries.ITEM.getOptional(new Identifier(string)) + //? } else + //Optional.of(Registry.ITEM.get(new Identifier(string))) ).filter(Optional::isPresent).map(Optional::get).collect(Collectors.toList()); maxPage = (int) Math.ceil(original.size() / (double) max); searching = original; @@ -91,15 +93,24 @@ public boolean mouseClicked(MouseButtonEvent ev, boolean p_430750_) { } //? if >=1.21.11 { List> itemTags = BuiltInRegistries.ITEM.getTags().filter(tag -> tags.stream().anyMatch(t -> tag.key().location().getPath().contains(t))).toList(); - //? } else + //? } elif >=1.19.4 { //List> itemTags = BuiltInRegistries.ITEM.getTags().map(Pair::getSecond).filter(tag -> tags.stream().anyMatch(t -> tag.key().location().getPath().contains(t))).toList(); + //? } else + //List> itemTags = Registry.ITEM.getTags().map(Pair::getSecond).filter(tag -> tags.stream().anyMatch(t -> tag.key().location().getPath().contains(t))).toList(); searching = original.stream().filter(item -> { + //? if >=1.21.11 { Optional> opt = BuiltInRegistries.ITEM.getResourceKey(item); if (opt.isEmpty()) return false; - //? if >=1.21.11 { Identifier rl = opt.get().identifier(); - //? } else - //Identifier rl = opt.get().location(); + //? } elif >=1.19.4 { + /*Optional> opt = BuiltInRegistries.ITEM.getResourceKey(item); + if (opt.isEmpty()) return false; + Identifier rl = opt.get().location(); + *///? } else { + /*Optional> opt = Registry.ITEM.getResourceKey(item); + if (opt.isEmpty()) return false; + Identifier rl = opt.get().location(); + *///? } boolean matchmod = mods.isEmpty(), matchtag = tags.isEmpty(), matcharg = false; for (String mod : mods) matchmod = matchmod || rl.getNamespace().toLowerCase().contains(mod); @@ -113,14 +124,14 @@ public boolean mouseClicked(MouseButtonEvent ev, boolean p_430750_) { if (page > maxPage - 1) page = Math.max(0, maxPage - 1); }); addRenderableWidget(search); - Button add = new Button.Builder(AutoFish.getTranslatableComponent("gui.superfilterscreen.openfilter"), button -> ScreenHelper.showScreen(new FilterSelectionScreen(this))).pos(this.width / 2 - 75, 60).size(72, 20).build(); + Button add = ScreenHelper.makeButton(this.width / 2 - 75, 60, 72, 20, AutoFish.getTranslatableComponent("gui.superfilterscreen.openfilter"), button -> ScreenHelper.showScreen(new FilterSelectionScreen(this))); addRenderableWidget(add); - Button done = new Button.Builder(AutoFish.getTranslatableComponent("gui.superfilterscreen.done"), button -> ScreenHelper.showScreen(parent)).pos(this.width / 2 + 3, 60).size(72, 20).build(); + Button done = ScreenHelper.makeButton(this.width / 2 + 3, 60, 72, 20, AutoFish.getTranslatableComponent("gui.superfilterscreen.done"), button -> ScreenHelper.showScreen(parent)); addRenderableWidget(done); - previous = new Button.Builder(AutoFish.getLiteralComponent("<"), button -> { if (page > 0) page--; }).pos(this.width / 2 - 100, 60).size(20, 20).build(); + previous = ScreenHelper.makeButton(this.width / 2 - 100, 60, 20, 20, AutoFish.getLiteralComponent("<"), button -> { if (page > 0) page--; }); previous.visible = false; addRenderableWidget(previous); - next = new Button.Builder(AutoFish.getLiteralComponent(">"), button -> { if (page < maxPage - 1) page++; }).pos(this.width / 2 + 80, 60).size(20, 20).build(); + next = ScreenHelper.makeButton(this.width / 2 + 80, 60, 20, 20, AutoFish.getLiteralComponent(">"), button -> { if (page < maxPage - 1) page++; }); next.visible = false; addRenderableWidget(next); } @@ -130,10 +141,14 @@ public boolean mouseClicked(MouseButtonEvent ev, boolean p_430750_) { public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float partialTicks) { super.extractRenderState(graphics, mouseX, mouseY, partialTicks); graphics.centeredText(this.font, this.title, this.width / 2, 20, -1); - //? } else { + //? } elif >=1.20.1 { /*public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) { super.render(graphics, mouseX, mouseY, partialTicks); graphics.drawCenteredString(this.font, this.title, this.width / 2, 20, -1); + *///? } else { + /*public void render(PoseStack poseStack, int mouseX, int mouseY, float partialTicks) { + super.render(poseStack, mouseX, mouseY, partialTicks); + drawCenteredString(poseStack, this.font, this.title, this.width / 2, 20, -1); *///? } Item[] items = searching.toArray(new Item[0]); for (int i = page * max; i < Math.min((page + 1) * max, searching.size()); i++) { @@ -145,16 +160,23 @@ public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mo //? if >=26.1 { if (!stack.isEmpty()) graphics.item(stack, (reducedWidth * h / 3) + 15, (reducedHeight * k / (max / 3)) + 90); graphics.text(this.font, stack.getDisplayName().getString(), ((reducedWidth * h / 3) + 45), ((reducedHeight * k / (max / 3)) + 95), 0xFFFFFFFF); - //? } else { + //? } elif >=1.20.1 { /*if (!stack.isEmpty()) graphics.renderItem(stack, (reducedWidth * h / 3) + 15, (reducedHeight * k / (max / 3)) + 90); graphics.drawString(this.font, stack.getDisplayName().getString(), ((reducedWidth * h / 3) + 45), ((reducedHeight * k / (max / 3)) + 95), 0xFFFFFFFF); + *///? } elif >=1.19.4 { + /*if (!stack.isEmpty()) itemRenderer.renderGuiItem(poseStack, stack, (reducedWidth * h / 3) + 15, (reducedHeight * k / (max / 3)) + 90); + drawString(poseStack, this.font, stack.getDisplayName().getString(), ((reducedWidth * h / 3) + 45), ((reducedHeight * k / (max / 3)) + 95), 0xFFFFFFFF); + *///? } else { + /*if (!stack.isEmpty()) itemRenderer.renderGuiItem(stack, (reducedWidth * h / 3) + 15, (reducedHeight * k / (max / 3)) + 90); + drawString(poseStack, this.font, stack.getDisplayName().getString(), ((reducedWidth * h / 3) + 45), ((reducedHeight * k / (max / 3)) + 95), 0xFFFFFFFF); *///? } - //this.font.draw(graphics, stack.getDisplayName().getString(), (float) ((reducedWidth * h / 3) + 45), (float) ((reducedHeight * k / (max / 3)) + 95), Color.WHITE.getRGB()); } //? if >=26.1 { search.extractRenderState(graphics, mouseX, mouseY, partialTicks); - //? } else + //? } elif >=1.20.1 { //search.render(graphics, mouseX, mouseY, partialTicks); + //? } else + //search.render(poseStack, mouseX, mouseY, partialTicks); } @Override diff --git a/common/src/main/java/in/northwestw/autofish/handler/AutoFishHandler.java b/common/src/main/java/in/northwestw/autofish/handler/AutoFishHandler.java index b95bf68..061b70f 100644 --- a/common/src/main/java/in/northwestw/autofish/handler/AutoFishHandler.java +++ b/common/src/main/java/in/northwestw/autofish/handler/AutoFishHandler.java @@ -11,8 +11,10 @@ import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.MultiPlayerGameMode; import net.minecraft.client.player.LocalPlayer; -import net.minecraft.core.Holder; +//? if >=1.19.4 { import net.minecraft.core.registries.BuiltInRegistries; +//? } else +//import net.minecraft.core.Registry; import net.minecraft.network.chat.Component; import net.minecraft.resources.Identifier; import net.minecraft.world.InteractionHand; @@ -20,6 +22,7 @@ import net.minecraft.world.item.FishingRodItem; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; import net.minecraft.world.phys.Vec3; import java.util.List; @@ -115,7 +118,11 @@ public static void onPlayerTick(final Player player) { double x = vector.x(); double y = vector.y(); double z = vector.z(); - if (y < -0.075 && !player.level().getFluidState(player.fishing.blockPosition()).isEmpty() && x == 0 && z == 0) + //? if >=1.20.1 { + Level level = player.level(); + //? } else + //Level level = player.level; + if (y < -0.075 && !level.getFluidState(player.fishing.blockPosition()).isEmpty() && x == 0 && z == 0) pendingReelIn = true; } @@ -128,7 +135,10 @@ private static void reelIn(Player player) { //? } else //List items = player.getInventory().items; items.forEach(stack -> { + //? if >=1.19.4 { Identifier rl = BuiltInRegistries.ITEM.getKey(stack.getItem()); + //? } else + //Identifier rl = Registry.ITEM.getKey(stack.getItem()); //? if >=26.1 { itemsBeforeFished.put(rl, itemsBeforeFished.getOrDefault(rl, 0) + stack.count()); //? } else @@ -192,7 +202,10 @@ private static void checkItem(Player player) { Identifier rl = Identifier.parse(name); //? } else //Identifier rl = new Identifier(name); + //? if >=1.19.4 { Optional opt = BuiltInRegistries.ITEM.getOptional(rl); + //? } else + //Optional opt = Registry.ITEM.getOptional(rl); if (opt.isEmpty()) continue; Item item = opt.get(); int newCount = items.stream().filter(stack -> stack.getItem().toString().equals(rl.toString())).mapToInt(ItemStack::getCount).reduce(Integer::sum).orElse(0); diff --git a/gradle.properties b/gradle.properties index c541f04..807febe 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,9 +13,9 @@ mod.github=https://github.com/North-West-Wind/AutoFish # Stonecutter stonecutter_enabled_platforms=fabric, neoforge, forge -stonecutter_enabled_common_versions=26.2, 26.1.2, 1.21.11, 1.21.1, 1.20.1 -stonecutter_enabled_fabric_versions=26.2, 26.1.2, 1.21.11, 1.21.1, 1.20.1 -stonecutter_enabled_forge_versions=26.2, 26.1.2, 1.21.11, 1.21.1, 1.20.1 +stonecutter_enabled_common_versions=26.2, 26.1.2, 1.21.11, 1.21.1, 1.20.1, 1.19.4, 1.19.2 +stonecutter_enabled_fabric_versions=26.2, 26.1.2, 1.21.11, 1.21.1, 1.20.1, 1.19.4, 1.19.2 +stonecutter_enabled_forge_versions=26.2, 26.1.2, 1.21.11, 1.21.1, 1.20.1, 1.19.4, 1.19.2 stonecutter_enabled_neoforge_versions=26.2, 26.1.2, 1.21.11, 1.21.1 # The below field are intentionally left blank, diff --git a/versions/1.19.2/gradle.properties b/versions/1.19.2/gradle.properties new file mode 100644 index 0000000..817d369 --- /dev/null +++ b/versions/1.19.2/gradle.properties @@ -0,0 +1,21 @@ +# Stonecutter +stonecutter_enabled_platforms=fabric, forge + +# Java +java.version=17 + +# Minecraft +minecraft_version=1.19.2 +min_minecraft_version=1.19 + +# Mappings +deps.parchment=2022.11.27 + +# Fabric +deps.fabric_loader=0.19.3 +deps.fabric_api=0.77.0 + +deps.forge=43.5.0 + +# Dependencies +deps.modmenu= \ No newline at end of file diff --git a/versions/1.19.4/gradle.properties b/versions/1.19.4/gradle.properties new file mode 100644 index 0000000..f06c170 --- /dev/null +++ b/versions/1.19.4/gradle.properties @@ -0,0 +1,21 @@ +# Stonecutter +stonecutter_enabled_platforms=fabric, forge + +# Java +java.version=17 + +# Minecraft +minecraft_version=1.19.4 +min_minecraft_version=1.19.3 + +# Mappings +deps.parchment=2023.06.26 + +# Fabric +deps.fabric_loader=0.19.3 +deps.fabric_api=0.87.2 + +deps.forge=45.4.0 + +# Dependencies +deps.modmenu= \ No newline at end of file From b46448140fdcad33161e5cd7d6e95341006e9733 Mon Sep 17 00:00:00 2001 From: North-West-Wind Date: Tue, 30 Jun 2026 18:09:38 +0800 Subject: [PATCH 27/52] feat: support 1.18.2 --- .../java/in/northwestw/autofish/AutoFish.java | 16 +++++++++--- .../config/gui/FilterSelectionScreen.java | 9 +++---- .../config/gui/LongSettingScreen.java | 10 +++----- .../autofish/config/gui/ScreenHelper.java | 11 ++++++++ .../config/gui/SuperFilterScreen.java | 9 +++---- .../autofish/handler/AutoFishHandler.java | 3 +++ .../northwestw/autofish/keybind/KeyBinds.java | 25 +++++++++++++++---- .../in/northwestw/autofish/AutoFishForge.java | 18 +++++++++++++ gradle.properties | 6 ++--- versions/1.18.2/gradle.properties | 21 ++++++++++++++++ 10 files changed, 101 insertions(+), 27 deletions(-) create mode 100644 versions/1.18.2/gradle.properties diff --git a/common/src/main/java/in/northwestw/autofish/AutoFish.java b/common/src/main/java/in/northwestw/autofish/AutoFish.java index ad1d2a5..cdf511c 100644 --- a/common/src/main/java/in/northwestw/autofish/AutoFish.java +++ b/common/src/main/java/in/northwestw/autofish/AutoFish.java @@ -4,9 +4,14 @@ import net.minecraft.network.chat.MutableComponent; //? if >=1.21.1 { import net.minecraft.network.chat.contents.PlainTextContents; -//? } else -//import net.minecraft.network.chat.contents.LiteralContents; import net.minecraft.network.chat.contents.TranslatableContents; +//? } elif >=1.19.2 { +/*import net.minecraft.network.chat.contents.LiteralContents; +import net.minecraft.network.chat.contents.TranslatableContents; +*///? } else { +/*import net.minecraft.network.chat.TextComponent; +import net.minecraft.network.chat.TranslatableComponent; +*///? } import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -20,13 +25,18 @@ public class AutoFish } public static MutableComponent getTranslatableComponent(String key, Object... args) { + //? if >=1.19.2 { return MutableComponent.create(new TranslatableContents(key, null, args)); + //? } else + //return new TranslatableComponent(key, args); } public static MutableComponent getLiteralComponent(String str) { //? if >=1.21.1 { return MutableComponent.create(new PlainTextContents.LiteralContents(str)); - //? } else + //? } elif >=1.19.2 { //return MutableComponent.create(new LiteralContents(str)); + //? } else + //return new TextComponent(str); } } diff --git a/common/src/main/java/in/northwestw/autofish/config/gui/FilterSelectionScreen.java b/common/src/main/java/in/northwestw/autofish/config/gui/FilterSelectionScreen.java index c9708a3..d734eb5 100644 --- a/common/src/main/java/in/northwestw/autofish/config/gui/FilterSelectionScreen.java +++ b/common/src/main/java/in/northwestw/autofish/config/gui/FilterSelectionScreen.java @@ -26,7 +26,6 @@ import net.minecraft.resources.ResourceKey; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; -import org.lwjgl.glfw.GLFW; import java.util.List; import java.util.*; @@ -72,12 +71,12 @@ protected void init() { @Override //? if >=1.21.11 { public boolean mouseClicked(MouseButtonEvent ev, boolean p_430750_) { - if (ev.button() == GLFW.GLFW_MOUSE_BUTTON_2) this.setValue(""); + if (ev.button() == ScreenHelper.MOUSE_BUTTON_LEFT) this.setValue(""); return super.mouseClicked(ev, p_430750_); } //? } else { /*public boolean mouseClicked(double mouseX, double mouseY, int button) { - if (button == GLFW.GLFW_MOUSE_BUTTON_2) this.setValue(""); + if (button == ScreenHelper.MOUSE_BUTTON_LEFT) this.setValue(""); return super.mouseClicked(mouseX, mouseY, button); } *///? } @@ -241,7 +240,7 @@ private int getYPos(int k, int height) { @Override //? if >=1.21.11 { public boolean keyPressed(KeyEvent ev) { - if (ev.key() == GLFW.GLFW_KEY_ESCAPE) { + if (ev.key() == ScreenHelper.KEY_ESCAPE) { if (!search.isFocused()) ScreenHelper.showScreen(parent); else search.setFocused(false); } @@ -249,7 +248,7 @@ public boolean keyPressed(KeyEvent ev) { } //? } else { /*public boolean keyPressed(int keyCode, int scanCode, int modifiers) { - if (keyCode == GLFW.GLFW_KEY_ESCAPE) { + if (keyCode == ScreenHelper.KEY_ESCAPE) { if (!search.isFocused()) ScreenHelper.showScreen(parent); else //? if >=1.19.4 { diff --git a/common/src/main/java/in/northwestw/autofish/config/gui/LongSettingScreen.java b/common/src/main/java/in/northwestw/autofish/config/gui/LongSettingScreen.java index 539aef0..c41f4c8 100644 --- a/common/src/main/java/in/northwestw/autofish/config/gui/LongSettingScreen.java +++ b/common/src/main/java/in/northwestw/autofish/config/gui/LongSettingScreen.java @@ -1,7 +1,6 @@ package in.northwestw.autofish.config.gui; import in.northwestw.autofish.AutoFish; -import net.minecraft.client.Minecraft; //? if >=26.1 { import net.minecraft.client.gui.GuiGraphicsExtractor; //?} elif >=1.20.1 { @@ -15,7 +14,6 @@ import net.minecraft.client.input.KeyEvent; import net.minecraft.client.input.MouseButtonEvent; //? } -import org.lwjgl.glfw.GLFW; import java.util.function.Consumer; import java.util.function.Supplier; @@ -45,12 +43,12 @@ protected void init() { @Override //? if >=1.21.11 { public boolean mouseClicked(MouseButtonEvent ev, boolean p_430750_) { - if (ev.button() == GLFW.GLFW_MOUSE_BUTTON_2) this.setValue(""); + if (ev.button() == ScreenHelper.MOUSE_BUTTON_LEFT) this.setValue(""); return super.mouseClicked(ev, p_430750_); } //? } else { /*public boolean mouseClicked(double mouseX, double mouseY, int button) { - if (button == GLFW.GLFW_MOUSE_BUTTON_2) this.setValue(""); + if (button == ScreenHelper.MOUSE_BUTTON_LEFT) this.setValue(""); return super.mouseClicked(mouseX, mouseY, button); } *///? } @@ -114,12 +112,12 @@ public boolean shouldCloseOnEsc() { @Override //? if >=1.21.11 { public boolean keyPressed(KeyEvent ev) { - if (ev.key() == GLFW.GLFW_KEY_ESCAPE) ScreenHelper.showScreen(parent); + if (ev.key() == ScreenHelper.KEY_ESCAPE) ScreenHelper.showScreen(parent); return super.keyPressed(ev); } //? } else { /*public boolean keyPressed(int keyCode, int scanCode, int modifiers) { - if (keyCode == GLFW.GLFW_KEY_ESCAPE) ScreenHelper.showScreen(parent); + if (keyCode == ScreenHelper.KEY_ESCAPE) ScreenHelper.showScreen(parent); return super.keyPressed(keyCode, scanCode, modifiers); } *///? } diff --git a/common/src/main/java/in/northwestw/autofish/config/gui/ScreenHelper.java b/common/src/main/java/in/northwestw/autofish/config/gui/ScreenHelper.java index f7d309c..186777b 100644 --- a/common/src/main/java/in/northwestw/autofish/config/gui/ScreenHelper.java +++ b/common/src/main/java/in/northwestw/autofish/config/gui/ScreenHelper.java @@ -4,8 +4,19 @@ import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.screens.Screen; import net.minecraft.network.chat.Component; +//? if >=1.19.2 { +import org.lwjgl.glfw.GLFW; +//? } public class ScreenHelper { + //? if >=1.19.2 { + public static final int MOUSE_BUTTON_LEFT = GLFW.GLFW_MOUSE_BUTTON_2; + public static final int KEY_ESCAPE = GLFW.GLFW_KEY_ESCAPE; + //? } else { + /*public static final int MOUSE_BUTTON_LEFT = 1; + public static final int KEY_ESCAPE = 256; + *///? } + public static void showScreen(Screen screen) { //? if >=1.21.11 { Minecraft.getInstance().setScreenAndShow(screen); diff --git a/common/src/main/java/in/northwestw/autofish/config/gui/SuperFilterScreen.java b/common/src/main/java/in/northwestw/autofish/config/gui/SuperFilterScreen.java index 5cbb472..4ba72d8 100644 --- a/common/src/main/java/in/northwestw/autofish/config/gui/SuperFilterScreen.java +++ b/common/src/main/java/in/northwestw/autofish/config/gui/SuperFilterScreen.java @@ -26,7 +26,6 @@ import net.minecraft.resources.ResourceKey; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; -import org.lwjgl.glfw.GLFW; import java.util.List; import java.util.*; @@ -73,12 +72,12 @@ protected void init() { @Override //? if >=1.21.11 { public boolean mouseClicked(MouseButtonEvent ev, boolean p_430750_) { - if (ev.button() == GLFW.GLFW_MOUSE_BUTTON_2) this.setValue(""); + if (ev.button() == ScreenHelper.MOUSE_BUTTON_LEFT) this.setValue(""); return super.mouseClicked(ev, p_430750_); } //? } else { /*public boolean mouseClicked(double mouseX, double mouseY, int button) { - if (button == GLFW.GLFW_MOUSE_BUTTON_2) this.setValue(""); + if (button == ScreenHelper.MOUSE_BUTTON_LEFT) this.setValue(""); return super.mouseClicked(mouseX, mouseY, button); } *///? } @@ -187,12 +186,12 @@ public boolean shouldCloseOnEsc() { @Override //? if >=1.21.11 { public boolean keyPressed(KeyEvent ev) { - if (ev.key() == GLFW.GLFW_KEY_ESCAPE) ScreenHelper.showScreen(parent); + if (ev.key() == ScreenHelper.KEY_ESCAPE) ScreenHelper.showScreen(parent); return super.keyPressed(ev); } //? } else { /*public boolean keyPressed(int keyCode, int scanCode, int modifiers) { - if (keyCode == GLFW.GLFW_KEY_ESCAPE) ScreenHelper.showScreen(parent); + if (keyCode == ScreenHelper.KEY_ESCAPE) ScreenHelper.showScreen(parent); return super.keyPressed(keyCode, scanCode, modifiers); } *///? } diff --git a/common/src/main/java/in/northwestw/autofish/handler/AutoFishHandler.java b/common/src/main/java/in/northwestw/autofish/handler/AutoFishHandler.java index 061b70f..18cff1c 100644 --- a/common/src/main/java/in/northwestw/autofish/handler/AutoFishHandler.java +++ b/common/src/main/java/in/northwestw/autofish/handler/AutoFishHandler.java @@ -247,7 +247,10 @@ private static void dropItem(Player player) { private static void click(Player player, InteractionHand hand, MultiPlayerGameMode controller) { if (controller == null) return; + //? if >=1.19.2 { controller.useItem(player, hand); + //? } else + //controller.useItem(player, player.level, hand); } private static InteractionHand findHandOfRod(Player player) { diff --git a/common/src/main/java/in/northwestw/autofish/keybind/KeyBinds.java b/common/src/main/java/in/northwestw/autofish/keybind/KeyBinds.java index 08c7eb7..86ab88d 100644 --- a/common/src/main/java/in/northwestw/autofish/keybind/KeyBinds.java +++ b/common/src/main/java/in/northwestw/autofish/keybind/KeyBinds.java @@ -5,9 +5,24 @@ //? if >=1.21.11 { import net.minecraft.resources.Identifier; //? } +//? if >=1.19.2 { import org.lwjgl.glfw.GLFW; +//? } public class KeyBinds { + //? if >=1.19.2 { + private static final int KEY_MINUS = GLFW.GLFW_KEY_MINUS; + private static final int KEY_BACKSLASH = GLFW.GLFW_KEY_BACKSLASH; + private static final int KEY_RIGHT_BRACKET = GLFW.GLFW_KEY_RIGHT_BRACKET; + private static final int KEY_K = GLFW.GLFW_KEY_K; + private static final int KEY_APOSTROPHE = GLFW.GLFW_KEY_APOSTROPHE; + //? } else { + /*private static final int KEY_MINUS = 45; + private static final int KEY_BACKSLASH = 92; + private static final int KEY_RIGHT_BRACKET = 93; + private static final int KEY_K = 75; + private static final int KEY_APOSTROPHE = 39; + *///? } public static KeyMapping autofish, rodprotect, autoreplace, settings, itemfilter; @@ -16,10 +31,10 @@ public class KeyBinds { KeyMapping.Category cat = KeyMapping.Category.register(Identifier.fromNamespaceAndPath(AutoFish.MOD_ID, "autofish")); //? } else //String cat = "key.categories.autofish"; - autofish = new KeyMapping(AutoFish.getTranslatableComponent("key.forgeautofish.autofish").getString(), GLFW.GLFW_KEY_MINUS, cat); - rodprotect = new KeyMapping(AutoFish.getTranslatableComponent("key.forgeautofish.rodprotect").getString(), GLFW.GLFW_KEY_BACKSLASH, cat); - autoreplace = new KeyMapping(AutoFish.getTranslatableComponent("key.forgeautofish.autoreplace").getString(), GLFW.GLFW_KEY_RIGHT_BRACKET, cat); - settings = new KeyMapping(AutoFish.getTranslatableComponent("key.forgeautofish.settings").getString(), GLFW.GLFW_KEY_K, cat); - itemfilter = new KeyMapping(AutoFish.getTranslatableComponent("key.forgeautofish.itemfilter").getString(), GLFW.GLFW_KEY_APOSTROPHE, cat); + autofish = new KeyMapping(AutoFish.getTranslatableComponent("key.forgeautofish.autofish").getString(), KEY_MINUS, cat); + rodprotect = new KeyMapping(AutoFish.getTranslatableComponent("key.forgeautofish.rodprotect").getString(), KEY_BACKSLASH, cat); + autoreplace = new KeyMapping(AutoFish.getTranslatableComponent("key.forgeautofish.autoreplace").getString(), KEY_RIGHT_BRACKET, cat); + settings = new KeyMapping(AutoFish.getTranslatableComponent("key.forgeautofish.settings").getString(), KEY_K, cat); + itemfilter = new KeyMapping(AutoFish.getTranslatableComponent("key.forgeautofish.itemfilter").getString(), KEY_APOSTROPHE, cat); } } diff --git a/forge/src/main/java/in/northwestw/autofish/AutoFishForge.java b/forge/src/main/java/in/northwestw/autofish/AutoFishForge.java index 385467a..350fd0f 100644 --- a/forge/src/main/java/in/northwestw/autofish/AutoFishForge.java +++ b/forge/src/main/java/in/northwestw/autofish/AutoFishForge.java @@ -3,7 +3,12 @@ import in.northwestw.autofish.handler.AutoFishHandler; import in.northwestw.autofish.keybind.KeyBinds; import net.minecraftforge.client.event.InputEvent; +//? if >=1.19.2 { import net.minecraftforge.client.event.RegisterKeyMappingsEvent; +//? } else { +/*import net.minecraftforge.client.ClientRegistry; +import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; +*///? } import net.minecraftforge.event.TickEvent; //? if >=1.21.11 { import net.minecraftforge.eventbus.api.listener.SubscribeEvent; @@ -21,6 +26,7 @@ public AutoFishForge() { @Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD) public static class ModEvents { @SubscribeEvent + //? if >=1.19.2 { public static void registerKeyMappings(RegisterKeyMappingsEvent event) { event.register(KeyBinds.autofish); event.register(KeyBinds.rodprotect); @@ -28,9 +34,21 @@ public static void registerKeyMappings(RegisterKeyMappingsEvent event) { event.register(KeyBinds.settings); event.register(KeyBinds.itemfilter); } + //? } else { + /*public static void setupClient(FMLClientSetupEvent event) { + ClientRegistry.registerKeyBinding(KeyBinds.autofish); + ClientRegistry.registerKeyBinding(KeyBinds.rodprotect); + ClientRegistry.registerKeyBinding(KeyBinds.autoreplace); + ClientRegistry.registerKeyBinding(KeyBinds.settings); + ClientRegistry.registerKeyBinding(KeyBinds.itemfilter); + } + *///? } @SubscribeEvent + //? if >=1.19.2 { public static void inputKey(InputEvent.Key event) { + //? } else + //public static void inputKey(InputEvent.KeyInputEvent event) { AutoFishHandler.onKeyInput(); } diff --git a/gradle.properties b/gradle.properties index 807febe..cc8c4ec 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,9 +13,9 @@ mod.github=https://github.com/North-West-Wind/AutoFish # Stonecutter stonecutter_enabled_platforms=fabric, neoforge, forge -stonecutter_enabled_common_versions=26.2, 26.1.2, 1.21.11, 1.21.1, 1.20.1, 1.19.4, 1.19.2 -stonecutter_enabled_fabric_versions=26.2, 26.1.2, 1.21.11, 1.21.1, 1.20.1, 1.19.4, 1.19.2 -stonecutter_enabled_forge_versions=26.2, 26.1.2, 1.21.11, 1.21.1, 1.20.1, 1.19.4, 1.19.2 +stonecutter_enabled_common_versions=26.2, 26.1.2, 1.21.11, 1.21.1, 1.20.1, 1.19.4, 1.19.2, 1.18.2 +stonecutter_enabled_fabric_versions=26.2, 26.1.2, 1.21.11, 1.21.1, 1.20.1, 1.19.4, 1.19.2, 1.18.2 +stonecutter_enabled_forge_versions=26.2, 26.1.2, 1.21.11, 1.21.1, 1.20.1, 1.19.4, 1.19.2, 1.18.2 stonecutter_enabled_neoforge_versions=26.2, 26.1.2, 1.21.11, 1.21.1 # The below field are intentionally left blank, diff --git a/versions/1.18.2/gradle.properties b/versions/1.18.2/gradle.properties new file mode 100644 index 0000000..7e9a227 --- /dev/null +++ b/versions/1.18.2/gradle.properties @@ -0,0 +1,21 @@ +# Stonecutter +stonecutter_enabled_platforms=fabric, forge + +# Java +java.version=17 + +# Minecraft +minecraft_version=1.18.2 +min_minecraft_version=1.18 + +# Mappings +deps.parchment=2022.11.06 + +# Fabric +deps.fabric_loader=0.19.3 +deps.fabric_api=0.77.0 + +deps.forge=40.3.0 + +# Dependencies +deps.modmenu= \ No newline at end of file From c7a52443984a78c15f1d61a25fe2a9f0f9c3a644 Mon Sep 17 00:00:00 2001 From: North-West-Wind Date: Tue, 30 Jun 2026 19:07:31 +0800 Subject: [PATCH 28/52] feat: support 1.17.1 --- .../config/gui/FilterSelectionScreen.java | 15 ++++++++++++- .../config/gui/SuperFilterScreen.java | 15 ++++++++++++- fabric/build.gradle.kts | 6 +++++- .../in/northwestw/autofish/AutoFishForge.java | 5 ++++- gradle.properties | 6 +++--- versions/1.17.1/gradle.properties | 21 +++++++++++++++++++ 6 files changed, 61 insertions(+), 7 deletions(-) create mode 100644 versions/1.17.1/gradle.properties diff --git a/common/src/main/java/in/northwestw/autofish/config/gui/FilterSelectionScreen.java b/common/src/main/java/in/northwestw/autofish/config/gui/FilterSelectionScreen.java index d734eb5..2cdfab3 100644 --- a/common/src/main/java/in/northwestw/autofish/config/gui/FilterSelectionScreen.java +++ b/common/src/main/java/in/northwestw/autofish/config/gui/FilterSelectionScreen.java @@ -17,7 +17,12 @@ import net.minecraft.client.input.KeyEvent; import net.minecraft.client.input.MouseButtonEvent; //? } +//? if >=1.18.2 { import net.minecraft.core.HolderSet; +//? } else { +/*import net.minecraft.tags.ItemTags; +import net.minecraft.tags.Tag; +*///? } //? if >=1.19.4 { import net.minecraft.core.registries.BuiltInRegistries; //? } else @@ -93,8 +98,13 @@ public boolean mouseClicked(MouseButtonEvent ev, boolean p_430750_) { List> itemTags = BuiltInRegistries.ITEM.getTags().filter(tag -> tags.stream().anyMatch(t -> tag.key().location().getPath().contains(t))).toList(); //? } elif >=1.19.4 { //List> itemTags = BuiltInRegistries.ITEM.getTags().map(Pair::getSecond).filter(tag -> tags.stream().anyMatch(t -> tag.key().location().getPath().contains(t))).toList(); - //? } else + //? } elif >=1.18.2 { //List> itemTags = Registry.ITEM.getTags().map(Pair::getSecond).filter(tag -> tags.stream().anyMatch(t -> tag.key().location().getPath().contains(t))).toList(); + //? } else { + /*List> itemTags = ItemTags.getAllTags().getAllTags().entrySet().stream() + .filter(entry -> tags.stream().anyMatch(t -> entry.getKey().toString().contains(t))) + .map(Map.Entry::getValue).toList(); + *///? } searching = original.stream().filter(item -> { //? if >=1.21.11 { Optional> opt = BuiltInRegistries.ITEM.getResourceKey(item); @@ -112,8 +122,11 @@ public boolean mouseClicked(MouseButtonEvent ev, boolean p_430750_) { boolean matchmod = mods.isEmpty(), matchtag = tags.isEmpty(), matcharg = false; for (String mod : mods) matchmod = matchmod || rl.getNamespace().toLowerCase().contains(mod); + //? if >=1.18.2 { for (HolderSet.Named itemTag : itemTags) matchtag = matchtag || itemTag.stream().anyMatch(tagItem -> tagItem.value() == item); + //? } else + //matchtag = matchtag || itemTags.stream().anyMatch(tag -> tag.contains(item)); for (String arg : paths) matcharg = matcharg || rl.getPath().contains(arg); return matchmod && matchtag && matcharg; diff --git a/common/src/main/java/in/northwestw/autofish/config/gui/SuperFilterScreen.java b/common/src/main/java/in/northwestw/autofish/config/gui/SuperFilterScreen.java index 4ba72d8..dea735c 100644 --- a/common/src/main/java/in/northwestw/autofish/config/gui/SuperFilterScreen.java +++ b/common/src/main/java/in/northwestw/autofish/config/gui/SuperFilterScreen.java @@ -17,7 +17,12 @@ import net.minecraft.client.input.KeyEvent; import net.minecraft.client.input.MouseButtonEvent; //? } +//? if >=1.18.2 { import net.minecraft.core.HolderSet; +//? } else { +/*import net.minecraft.tags.ItemTags; +import net.minecraft.tags.Tag; +*///? } //? if >=1.19.4 { import net.minecraft.core.registries.BuiltInRegistries; //? } else @@ -94,8 +99,13 @@ public boolean mouseClicked(MouseButtonEvent ev, boolean p_430750_) { List> itemTags = BuiltInRegistries.ITEM.getTags().filter(tag -> tags.stream().anyMatch(t -> tag.key().location().getPath().contains(t))).toList(); //? } elif >=1.19.4 { //List> itemTags = BuiltInRegistries.ITEM.getTags().map(Pair::getSecond).filter(tag -> tags.stream().anyMatch(t -> tag.key().location().getPath().contains(t))).toList(); - //? } else + //? } elif >=1.18.2 { //List> itemTags = Registry.ITEM.getTags().map(Pair::getSecond).filter(tag -> tags.stream().anyMatch(t -> tag.key().location().getPath().contains(t))).toList(); + //? } else { + /*List> itemTags = ItemTags.getAllTags().getAllTags().entrySet().stream() + .filter(entry -> tags.stream().anyMatch(t -> entry.getKey().toString().contains(t))) + .map(Map.Entry::getValue).toList(); + *///? } searching = original.stream().filter(item -> { //? if >=1.21.11 { Optional> opt = BuiltInRegistries.ITEM.getResourceKey(item); @@ -113,8 +123,11 @@ public boolean mouseClicked(MouseButtonEvent ev, boolean p_430750_) { boolean matchmod = mods.isEmpty(), matchtag = tags.isEmpty(), matcharg = false; for (String mod : mods) matchmod = matchmod || rl.getNamespace().toLowerCase().contains(mod); + //? if >=1.18.2 { for (HolderSet.Named itemTag : itemTags) matchtag = matchtag || itemTag.stream().anyMatch(tagItem -> tagItem.value() == item); + //? } else + //matchtag = matchtag || itemTags.stream().anyMatch(tag -> tag.contains(item)); for (String arg : paths) matcharg = matcharg || rl.getPath().contains(arg); return matchmod && matchtag && matcharg; diff --git a/fabric/build.gradle.kts b/fabric/build.gradle.kts index 1c9abf5..c80ec72 100644 --- a/fabric/build.gradle.kts +++ b/fabric/build.gradle.kts @@ -18,7 +18,11 @@ dependencies { } modImplementation("net.fabricmc:fabric-loader:${commonMod.dep("fabric_loader")}") - modApi("net.fabricmc.fabric-api:fabric-api:${commonMod.dep("fabric_api")}+${commonMod.mc}") + if (commonMod.dep("fabric_api").contains("+")) { + modApi("net.fabricmc.fabric-api:fabric-api:${commonMod.dep("fabric_api")}") + } else { + modApi("net.fabricmc.fabric-api:fabric-api:${commonMod.dep("fabric_api")}+${commonMod.mc}") + } commonMod.depOrNull("modmenu")?.let { modMenuVersion -> modImplementation("com.terraformersmc:modmenu:${modMenuVersion}") diff --git a/forge/src/main/java/in/northwestw/autofish/AutoFishForge.java b/forge/src/main/java/in/northwestw/autofish/AutoFishForge.java index 350fd0f..481f693 100644 --- a/forge/src/main/java/in/northwestw/autofish/AutoFishForge.java +++ b/forge/src/main/java/in/northwestw/autofish/AutoFishForge.java @@ -5,9 +5,12 @@ import net.minecraftforge.client.event.InputEvent; //? if >=1.19.2 { import net.minecraftforge.client.event.RegisterKeyMappingsEvent; -//? } else { +//? } elif >=1.18.2 { /*import net.minecraftforge.client.ClientRegistry; import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; +*///? } else { +/*import net.minecraftforge.fmlclient.registry.ClientRegistry; +import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; *///? } import net.minecraftforge.event.TickEvent; //? if >=1.21.11 { diff --git a/gradle.properties b/gradle.properties index cc8c4ec..4db032f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,9 +13,9 @@ mod.github=https://github.com/North-West-Wind/AutoFish # Stonecutter stonecutter_enabled_platforms=fabric, neoforge, forge -stonecutter_enabled_common_versions=26.2, 26.1.2, 1.21.11, 1.21.1, 1.20.1, 1.19.4, 1.19.2, 1.18.2 -stonecutter_enabled_fabric_versions=26.2, 26.1.2, 1.21.11, 1.21.1, 1.20.1, 1.19.4, 1.19.2, 1.18.2 -stonecutter_enabled_forge_versions=26.2, 26.1.2, 1.21.11, 1.21.1, 1.20.1, 1.19.4, 1.19.2, 1.18.2 +stonecutter_enabled_common_versions=26.2, 26.1.2, 1.21.11, 1.21.1, 1.20.1, 1.19.4, 1.19.2, 1.18.2, 1.17.1 +stonecutter_enabled_fabric_versions=26.2, 26.1.2, 1.21.11, 1.21.1, 1.20.1, 1.19.4, 1.19.2, 1.18.2, 1.17.1 +stonecutter_enabled_forge_versions=26.2, 26.1.2, 1.21.11, 1.21.1, 1.20.1, 1.19.4, 1.19.2, 1.18.2, 1.17.1 stonecutter_enabled_neoforge_versions=26.2, 26.1.2, 1.21.11, 1.21.1 # The below field are intentionally left blank, diff --git a/versions/1.17.1/gradle.properties b/versions/1.17.1/gradle.properties new file mode 100644 index 0000000..4b06303 --- /dev/null +++ b/versions/1.17.1/gradle.properties @@ -0,0 +1,21 @@ +# Stonecutter +stonecutter_enabled_platforms=fabric, forge + +# Java +java.version=16 + +# Minecraft +minecraft_version=1.17.1 +min_minecraft_version=1.17 + +# Mappings +deps.parchment=2021.12.12 + +# Fabric +deps.fabric_loader=0.19.3 +deps.fabric_api=0.46.1+1.17 + +deps.forge=37.1.1 + +# Dependencies +deps.modmenu= \ No newline at end of file From 2e4958fd694703a0600f7a13ef3a3e40c9d626b0 Mon Sep 17 00:00:00 2001 From: North-West-Wind Date: Tue, 30 Jun 2026 20:30:59 +0800 Subject: [PATCH 29/52] feat: support 1.16.5 (except forge) --- .../in/northwestw/autofish/config/Config.java | 3 +- .../config/gui/FilterSelectionScreen.java | 28 ++++++---- .../config/gui/LongSettingScreen.java | 7 ++- .../autofish/config/gui/SettingsScreen.java | 10 +++- .../config/gui/SuperFilterScreen.java | 20 ++++--- .../autofish/handler/AutoFishHandler.java | 52 +++++++++++++------ fabric/build.gradle.kts | 9 ++-- .../in/northwestw/autofish/AutoFishForge.java | 5 +- gradle.properties | 4 +- versions/1.16.5/gradle.properties | 21 ++++++++ 10 files changed, 116 insertions(+), 43 deletions(-) create mode 100644 versions/1.16.5/gradle.properties diff --git a/common/src/main/java/in/northwestw/autofish/config/Config.java b/common/src/main/java/in/northwestw/autofish/config/Config.java index ca5d0d9..2d5f5d4 100644 --- a/common/src/main/java/in/northwestw/autofish/config/Config.java +++ b/common/src/main/java/in/northwestw/autofish/config/Config.java @@ -9,6 +9,7 @@ import java.io.IOException; import java.io.PrintWriter; import java.util.List; +import java.util.stream.Collectors; import java.util.stream.Stream; public class Config { @@ -73,7 +74,7 @@ public static void load() { if (json.has("all_filters")) allFilters = json.get("all_filters").getAsBoolean(); if (json.has("filter")) - filter = Stream.of(json.getAsJsonArray("filter")).map(JsonElement::getAsString).toList(); + filter = Stream.of(json.getAsJsonArray("filter")).map(JsonElement::getAsString).collect(Collectors.toList()); // validate if (recastDelay < RECAST_DELAY_RANGE[0] || recastDelay > RECAST_DELAY_RANGE[1]) { diff --git a/common/src/main/java/in/northwestw/autofish/config/gui/FilterSelectionScreen.java b/common/src/main/java/in/northwestw/autofish/config/gui/FilterSelectionScreen.java index 2cdfab3..cf8a79f 100644 --- a/common/src/main/java/in/northwestw/autofish/config/gui/FilterSelectionScreen.java +++ b/common/src/main/java/in/northwestw/autofish/config/gui/FilterSelectionScreen.java @@ -1,7 +1,6 @@ package in.northwestw.autofish.config.gui; import com.google.common.collect.Lists; -import com.mojang.datafixers.util.Pair; import in.northwestw.autofish.AutoFish; import in.northwestw.autofish.config.Config; //? if >=26.1 { @@ -18,6 +17,7 @@ import net.minecraft.client.input.MouseButtonEvent; //? } //? if >=1.18.2 { +import com.mojang.datafixers.util.Pair; import net.minecraft.core.HolderSet; //? } else { /*import net.minecraft.tags.ItemTags; @@ -43,7 +43,7 @@ public class FilterSelectionScreen extends Screen { //? if >=1.19.4 { private final Collection original = BuiltInRegistries.ITEM.stream().toList(); //? } else - //private final Collection original = Registry.ITEM.stream().toList(); + //private final Collection original = Registry.ITEM.stream().collect(Collectors.toList()); private Collection searching; private final Set selected = new HashSet<>(Config.filter.stream().map(string -> //? if >=1.21.1 { @@ -103,7 +103,7 @@ public boolean mouseClicked(MouseButtonEvent ev, boolean p_430750_) { //? } else { /*List> itemTags = ItemTags.getAllTags().getAllTags().entrySet().stream() .filter(entry -> tags.stream().anyMatch(t -> entry.getKey().toString().contains(t))) - .map(Map.Entry::getValue).toList(); + .map(Map.Entry::getValue).collect(Collectors.toList()); *///? } searching = original.stream().filter(item -> { //? if >=1.21.11 { @@ -116,7 +116,7 @@ public boolean mouseClicked(MouseButtonEvent ev, boolean p_430750_) { Identifier rl = opt.get().location(); *///? } else { /*Optional> opt = Registry.ITEM.getResourceKey(item); - if (opt.isEmpty()) return false; + if (!opt.isPresent()) return false; Identifier rl = opt.get().location(); *///? } boolean matchmod = mods.isEmpty(), matchtag = tags.isEmpty(), matcharg = false; @@ -134,7 +134,6 @@ public boolean mouseClicked(MouseButtonEvent ev, boolean p_430750_) { maxPage = (int) Math.ceil(searching.size() / (double) max); if (page > maxPage - 1) page = Math.max(0, maxPage - 1); }); - addRenderableWidget(search); Button add = ScreenHelper.makeButton(this.width / 2 - 75, 60, 72, 20, AutoFish.getTranslatableComponent("gui.filterselection.save"), button -> { //? if >=1.19.4 { List items = selected.stream().map(item -> BuiltInRegistries.ITEM.getKey(item).toString()).collect(Collectors.toList()); @@ -143,15 +142,24 @@ public boolean mouseClicked(MouseButtonEvent ev, boolean p_430750_) { Config.setFilter(items); ScreenHelper.showScreen(parent); }); - addRenderableWidget(add); Button done = ScreenHelper.makeButton(this.width / 2 + 3, 60, 72, 20, AutoFish.getTranslatableComponent("gui.filterselection.cancel"), button -> ScreenHelper.showScreen(parent)); - addRenderableWidget(done); previous = ScreenHelper.makeButton(this.width / 2 - 100, 60, 20, 20, AutoFish.getLiteralComponent("<"), button -> { if (page > 0) page--; }); previous.visible = false; - addRenderableWidget(previous); next = ScreenHelper.makeButton(this.width / 2 + 80, 60, 20, 20, AutoFish.getLiteralComponent(">"), button -> { if (page < maxPage - 1) page++; }); next.visible = false; + //? if <=1.16.5 { + /*this.children.add(search); + addButton(add); + addButton(done); + addButton(previous); + addButton(next); + *///? } else { + addRenderableWidget(search); + addRenderableWidget(add); + addRenderableWidget(done); + addRenderableWidget(previous); addRenderableWidget(next); + //? } } @Override @@ -181,13 +189,13 @@ public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mo Identifier rl = opt.get().location(); *///? } else { /*Optional> opt = Registry.ITEM.getResourceKey(item); - if (opt.isEmpty()) return false; + if (!opt.isPresent()) return false; Identifier rl = opt.get().location(); *///? } boolean pri = Config.prioritize.contains(rl.toString()); if (!pri) searchingCopy.add(item); return pri; - }).toList(); + }).collect(Collectors.toList()); Item[] items = Stream.concat(prioritized.stream(), searchingCopy.stream()).toArray(Item[]::new); if (items.length > 0 && page >= 0) { for (int i = page * max; i < Math.min((page + 1) * max, searching.size()); i++) { diff --git a/common/src/main/java/in/northwestw/autofish/config/gui/LongSettingScreen.java b/common/src/main/java/in/northwestw/autofish/config/gui/LongSettingScreen.java index c41f4c8..6ce1dff 100644 --- a/common/src/main/java/in/northwestw/autofish/config/gui/LongSettingScreen.java +++ b/common/src/main/java/in/northwestw/autofish/config/gui/LongSettingScreen.java @@ -54,7 +54,6 @@ public boolean mouseClicked(MouseButtonEvent ev, boolean p_430750_) { *///? } }; editBox.setValue(Long.toString(this.supplier.get())); - addRenderableWidget(editBox); Button save = ScreenHelper.makeButton(this.width / 2 - 75, this.height / 2, 150, 20, AutoFish.getTranslatableComponent("gui." + this.middleTranslationKey + ".save"), button -> { if (!isNumeric(editBox.getValue())) editBox.setValue(Long.toString(this.supplier.get())); else { @@ -66,7 +65,13 @@ public boolean mouseClicked(MouseButtonEvent ev, boolean p_430750_) { } } }); + //? if <=1.16.5 { + /*this.children.add(editBox); + addButton(save); + *///? } else { + addRenderableWidget(editBox); addRenderableWidget(save); + //? } } @Override diff --git a/common/src/main/java/in/northwestw/autofish/config/gui/SettingsScreen.java b/common/src/main/java/in/northwestw/autofish/config/gui/SettingsScreen.java index ec7ffb7..eebe6ac 100644 --- a/common/src/main/java/in/northwestw/autofish/config/gui/SettingsScreen.java +++ b/common/src/main/java/in/northwestw/autofish/config/gui/SettingsScreen.java @@ -1,5 +1,7 @@ package in.northwestw.autofish.config.gui; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; import in.northwestw.autofish.AutoFish; import in.northwestw.autofish.config.Config; //? if >=26.1 { @@ -29,7 +31,7 @@ public boolean isPauseScreen() { @Override protected void init() { - List> pairs = List.of( + List> pairs = ImmutableList.of( Pair.of(AutoFish.getTranslatableComponent("gui.autofish.recastdelay"), button -> ScreenHelper.showScreen(new LongSettingScreen(this, "setrecastdelay", () -> Config.recastDelay, (newDelay) -> Config.recastDelay = newDelay, Config.RECAST_DELAY_RANGE[0], Config.RECAST_DELAY_RANGE[1]))), Pair.of(AutoFish.getTranslatableComponent("gui.autofish.reelindelay"), button -> @@ -45,10 +47,16 @@ protected void init() { for (int ii = 0; ii < pairs.size(); ii++) { Pair pair = pairs.get(ii); Button button = ScreenHelper.makeButton(this.width / 2 - WIDTH / 2, this.height / 2 + (ii - pairs.size() / 2) * (HEIGHT + MARGIN), WIDTH, HEIGHT, pair.getLeft(), pair.getRight()); + //? if <=1.16.5 { + /*addButton(button); + *///? } else addRenderableWidget(button); } Button done = ScreenHelper.makeButton(this.width / 2 - 75, this.height - 25, 150, 20, AutoFish.getTranslatableComponent("gui.autofish.done"), button -> onClose()); + //? if <=1.16.5 { + /*addButton(done); + *///? } else addRenderableWidget(done); } diff --git a/common/src/main/java/in/northwestw/autofish/config/gui/SuperFilterScreen.java b/common/src/main/java/in/northwestw/autofish/config/gui/SuperFilterScreen.java index dea735c..dd0d309 100644 --- a/common/src/main/java/in/northwestw/autofish/config/gui/SuperFilterScreen.java +++ b/common/src/main/java/in/northwestw/autofish/config/gui/SuperFilterScreen.java @@ -104,7 +104,7 @@ public boolean mouseClicked(MouseButtonEvent ev, boolean p_430750_) { //? } else { /*List> itemTags = ItemTags.getAllTags().getAllTags().entrySet().stream() .filter(entry -> tags.stream().anyMatch(t -> entry.getKey().toString().contains(t))) - .map(Map.Entry::getValue).toList(); + .map(Map.Entry::getValue).collect(Collectors.toList()); *///? } searching = original.stream().filter(item -> { //? if >=1.21.11 { @@ -117,7 +117,7 @@ public boolean mouseClicked(MouseButtonEvent ev, boolean p_430750_) { Identifier rl = opt.get().location(); *///? } else { /*Optional> opt = Registry.ITEM.getResourceKey(item); - if (opt.isEmpty()) return false; + if (!opt.isPresent()) return false; Identifier rl = opt.get().location(); *///? } boolean matchmod = mods.isEmpty(), matchtag = tags.isEmpty(), matcharg = false; @@ -135,17 +135,25 @@ public boolean mouseClicked(MouseButtonEvent ev, boolean p_430750_) { maxPage = (int) Math.ceil(original.size() / (double) max); if (page > maxPage - 1) page = Math.max(0, maxPage - 1); }); - addRenderableWidget(search); Button add = ScreenHelper.makeButton(this.width / 2 - 75, 60, 72, 20, AutoFish.getTranslatableComponent("gui.superfilterscreen.openfilter"), button -> ScreenHelper.showScreen(new FilterSelectionScreen(this))); - addRenderableWidget(add); Button done = ScreenHelper.makeButton(this.width / 2 + 3, 60, 72, 20, AutoFish.getTranslatableComponent("gui.superfilterscreen.done"), button -> ScreenHelper.showScreen(parent)); - addRenderableWidget(done); previous = ScreenHelper.makeButton(this.width / 2 - 100, 60, 20, 20, AutoFish.getLiteralComponent("<"), button -> { if (page > 0) page--; }); previous.visible = false; - addRenderableWidget(previous); next = ScreenHelper.makeButton(this.width / 2 + 80, 60, 20, 20, AutoFish.getLiteralComponent(">"), button -> { if (page < maxPage - 1) page++; }); next.visible = false; + //? if <=1.16.5 { + /*this.children.add(search); + addButton(add); + addButton(done); + addButton(previous); + addButton(next); + *///? } else { + addRenderableWidget(search); + addRenderableWidget(add); + addRenderableWidget(done); + addRenderableWidget(previous); addRenderableWidget(next); + //? } } @Override diff --git a/common/src/main/java/in/northwestw/autofish/handler/AutoFishHandler.java b/common/src/main/java/in/northwestw/autofish/handler/AutoFishHandler.java index 18cff1c..02c2283 100644 --- a/common/src/main/java/in/northwestw/autofish/handler/AutoFishHandler.java +++ b/common/src/main/java/in/northwestw/autofish/handler/AutoFishHandler.java @@ -70,8 +70,10 @@ public static void onPlayerTick(final Player player) { if (tick == 0 && rodSlot != -1) { //? if >=1.21.11 { player.getInventory().setSelectedSlot(rodSlot); - //? } else - //player.getInventory().selected = rodSlot; + //? } elif >=1.17.1 { + /*player.getInventory().selected = rodSlot; + *///? } else + //player.inventory.selected = rodSlot; rodSlot = -1; } tick++; @@ -132,8 +134,10 @@ private static void reelIn(Player player) { if (hand == null) return; //? if >=1.21.11 { List items = player.getInventory().getNonEquipmentItems(); - //? } else - //List items = player.getInventory().items; + //? } elif >=1.17.1 { + /*List items = player.getInventory().items; + *///? } else + //List items = player.inventory.items; items.forEach(stack -> { //? if >=1.19.4 { Identifier rl = BuiltInRegistries.ITEM.getKey(stack.getItem()); @@ -163,16 +167,23 @@ else if (fishingRod.getMaxDamage() - fishingRod.getDamageValue() < 3 && !player. for (int i = 0; i < 9; i++) { //? if >=1.21.11 { if (i == player.getInventory().getSelectedSlot()) continue; - //? } else - //if (i == player.getInventory().selected) continue; ItemStack stack = player.getInventory().getItem(i); + //? } elif >=1.17.1 { + /*if (i == player.getInventory().selected) continue; + ItemStack stack = player.getInventory().getItem(i); + *///? } else { + /*if (i == player.inventory.selected) continue; + ItemStack stack = player.inventory.getItem(i); + *///? } if (stack.getItem() instanceof FishingRodItem) { if (Config.rodProtect && stack.getMaxDamage() - stack.getDamageValue() < 2) continue; AutoFish.LOGGER.info("Found fishing rod for replacement"); //? if >=1.21.11 { player.getInventory().setSelectedSlot(i); - //? } else - //player.getInventory().selected = i; + //? } elif >=1.17.1 { + /*player.getInventory().selected = i; + *///? } else + //player.inventory.selected = i; found = true; break; } @@ -195,8 +206,10 @@ private static void checkItem(Player player) { if (!itemsBeforeFished.isEmpty()) { //? if >=1.21.11 { List items = player.getInventory().getNonEquipmentItems(); - //? } else - //List items = player.getInventory().items; + //? } elif >=1.17.1 { + /*List items = player.getInventory().items; + *///? } else + //List items = player.inventory.items; for (String name : Config.filter) { //? if >=1.21.1 { Identifier rl = Identifier.parse(name); @@ -206,7 +219,7 @@ private static void checkItem(Player player) { Optional opt = BuiltInRegistries.ITEM.getOptional(rl); //? } else //Optional opt = Registry.ITEM.getOptional(rl); - if (opt.isEmpty()) continue; + if (!opt.isPresent()) continue; Item item = opt.get(); int newCount = items.stream().filter(stack -> stack.getItem().toString().equals(rl.toString())).mapToInt(ItemStack::getCount).reduce(Integer::sum).orElse(0); int oldCount = itemsBeforeFished.getOrDefault(rl, 0); @@ -218,8 +231,10 @@ private static void checkItem(Player player) { processingDrop = true; //? if >=1.21.11 { rodSlot = player.getInventory().getSelectedSlot(); - //? } else - //rodSlot = player.getInventory().selected; + //? } elif >=1.17.1 { + /*rodSlot = player.getInventory().selected; + *///? } else + //rodSlot = player.inventory.selected; } } } @@ -233,11 +248,16 @@ private static void dropItem(Player player) { return; } for (int ii = 0; ii < 9; ii++) { - if (!player.getInventory().getItem(ii).getItem().equals(item)) continue; //? if >=1.21.11 { + if (!player.getInventory().getItem(ii).getItem().equals(item)) continue; player.getInventory().setSelectedSlot(ii); - //? } else - //player.getInventory().selected = ii; + //? } elif >=1.17.1 { + /*if (!player.getInventory().getItem(ii).getItem().equals(item)) continue; + player.getInventory().selected = ii; + *///? } else { + /*if (!player.inventory.getItem(ii).getItem().equals(item)) continue; + player.inventory.selected = ii; + *///? } dropCd = 20; return; } diff --git a/fabric/build.gradle.kts b/fabric/build.gradle.kts index c80ec72..9148e42 100644 --- a/fabric/build.gradle.kts +++ b/fabric/build.gradle.kts @@ -18,11 +18,10 @@ dependencies { } modImplementation("net.fabricmc:fabric-loader:${commonMod.dep("fabric_loader")}") - if (commonMod.dep("fabric_api").contains("+")) { - modApi("net.fabricmc.fabric-api:fabric-api:${commonMod.dep("fabric_api")}") - } else { - modApi("net.fabricmc.fabric-api:fabric-api:${commonMod.dep("fabric_api")}+${commonMod.mc}") - } + + // In older versions, Fabric uses the base Minecraft version for Fabric API. Specify by using {api-ver}+{mc-ver} + if (commonMod.dep("fabric_api").contains("+")) modApi("net.fabricmc.fabric-api:fabric-api:${commonMod.dep("fabric_api")}") + else modApi("net.fabricmc.fabric-api:fabric-api:${commonMod.dep("fabric_api")}+${commonMod.mc}") commonMod.depOrNull("modmenu")?.let { modMenuVersion -> modImplementation("com.terraformersmc:modmenu:${modMenuVersion}") diff --git a/forge/src/main/java/in/northwestw/autofish/AutoFishForge.java b/forge/src/main/java/in/northwestw/autofish/AutoFishForge.java index 481f693..c4f6583 100644 --- a/forge/src/main/java/in/northwestw/autofish/AutoFishForge.java +++ b/forge/src/main/java/in/northwestw/autofish/AutoFishForge.java @@ -8,9 +8,12 @@ //? } elif >=1.18.2 { /*import net.minecraftforge.client.ClientRegistry; import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; -*///? } else { +*///? } elif >=1.17.1 { /*import net.minecraftforge.fmlclient.registry.ClientRegistry; import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; +*///? } else { +/*import net.minecraftforge.fml.client.registry.ClientRegistry; +import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; *///? } import net.minecraftforge.event.TickEvent; //? if >=1.21.11 { diff --git a/gradle.properties b/gradle.properties index 4db032f..0d8c40e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,8 +13,8 @@ mod.github=https://github.com/North-West-Wind/AutoFish # Stonecutter stonecutter_enabled_platforms=fabric, neoforge, forge -stonecutter_enabled_common_versions=26.2, 26.1.2, 1.21.11, 1.21.1, 1.20.1, 1.19.4, 1.19.2, 1.18.2, 1.17.1 -stonecutter_enabled_fabric_versions=26.2, 26.1.2, 1.21.11, 1.21.1, 1.20.1, 1.19.4, 1.19.2, 1.18.2, 1.17.1 +stonecutter_enabled_common_versions=26.2, 26.1.2, 1.21.11, 1.21.1, 1.20.1, 1.19.4, 1.19.2, 1.18.2, 1.17.1, 1.16.5 +stonecutter_enabled_fabric_versions=26.2, 26.1.2, 1.21.11, 1.21.1, 1.20.1, 1.19.4, 1.19.2, 1.18.2, 1.17.1, 1.16.5 stonecutter_enabled_forge_versions=26.2, 26.1.2, 1.21.11, 1.21.1, 1.20.1, 1.19.4, 1.19.2, 1.18.2, 1.17.1 stonecutter_enabled_neoforge_versions=26.2, 26.1.2, 1.21.11, 1.21.1 diff --git a/versions/1.16.5/gradle.properties b/versions/1.16.5/gradle.properties new file mode 100644 index 0000000..419e893 --- /dev/null +++ b/versions/1.16.5/gradle.properties @@ -0,0 +1,21 @@ +# Stonecutter +stonecutter_enabled_platforms=fabric + +# Java +java.version=8 + +# Minecraft +minecraft_version=1.16.5 +min_minecraft_version=1.16 + +# Mappings +deps.parchment=2022.03.06 + +# Fabric +deps.fabric_loader=0.19.3 +deps.fabric_api=0.42.0+1.16 + +deps.forge=36.2.34 + +# Dependencies +deps.modmenu= \ No newline at end of file From fa623b6dbc3757145ad126e588b7c63a368c2599 Mon Sep 17 00:00:00 2001 From: North-West-Wind Date: Tue, 30 Jun 2026 21:44:12 +0800 Subject: [PATCH 30/52] fix: mark executable --- .github/scripts/generate-publish-matrix.sh | 0 .github/scripts/parse-gradle-properties.sh | 0 2 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 .github/scripts/generate-publish-matrix.sh mode change 100644 => 100755 .github/scripts/parse-gradle-properties.sh diff --git a/.github/scripts/generate-publish-matrix.sh b/.github/scripts/generate-publish-matrix.sh old mode 100644 new mode 100755 diff --git a/.github/scripts/parse-gradle-properties.sh b/.github/scripts/parse-gradle-properties.sh old mode 100644 new mode 100755 From ab3d2a750e629b3612c96286f10c73fdba205940 Mon Sep 17 00:00:00 2001 From: North-West-Wind Date: Tue, 30 Jun 2026 21:50:07 +0800 Subject: [PATCH 31/52] fix: git update-index for scripts From f80c63015db11fd75fce9ba9ac67bfe4e5f7a9de Mon Sep 17 00:00:00 2001 From: North-West-Wind Date: Tue, 30 Jun 2026 22:02:24 +0800 Subject: [PATCH 32/52] ci: bashing my scripts --- .github/workflows/publish.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 76cb2bd..6949682 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -21,7 +21,7 @@ jobs: fetch-depth: 0 - id: set-matrix - run: ./.github/scripts/generate-publish-matrix.sh + run: bash ./.github/scripts/generate-publish-matrix.sh publish: needs: generate-publish-matrix @@ -54,7 +54,7 @@ jobs: - name: "Parse gradle properties" id: gradle-properties - run: ./.github/scripts/parse-gradle-properties.sh ${{ matrix.version }} + run: bash ./.github/scripts/parse-gradle-properties.sh ${{ matrix.version }} - uses: Kir-Antipov/mc-publish@v3.3 with: @@ -64,8 +64,7 @@ jobs: curseforge-token: ${{ secrets.CURSEFORGE_TOKEN }} files: | ${{ matrix.loader }}/versions/${{ matrix.version }}/build/libs/*.jar - loaders: | - ${{ matrix.supported_loaders }} + loaders: ${{ join(matrix.supported_loaders, ' ') }} game-versions: | >=${{ steps.gradle-properties.outputs.MIN_MINECRAFT_VERSION }} <=${{ steps.gradle-properties.outputs.MINECRAFT_VERSION }} version: ${{ steps.gradle-properties.outputs.MOD_VERSION }}-${{ matrix.version }}-${{ matrix.loader }} From acd2cd460b6088bdcddc05224e21d14574124627 Mon Sep 17 00:00:00 2001 From: North-West-Wind Date: Tue, 30 Jun 2026 22:05:58 +0800 Subject: [PATCH 33/52] ci: add changelog inputs --- .github/workflows/publish.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 6949682..b7fbf57 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -4,6 +4,10 @@ on: release: types: [published] workflow_dispatch: + inputs: + changelog: + description: 'Changelog' + required: false concurrency: group: publish-${{ github.ref }} @@ -70,6 +74,6 @@ jobs: version: ${{ steps.gradle-properties.outputs.MOD_VERSION }}-${{ matrix.version }}-${{ matrix.loader }} name: ${{ steps.gradle-properties.outputs.MOD_NAME }} ${{ steps.gradle-properties.outputs.MOD_VERSION }} (${{ matrix.version }}-${{ matrix.loader }}) version-type: ${{ github.event.release.prerelease && 'beta' || 'release' }} - changelog: ${{ github.event.release.body || '' }} + changelog: ${{ inputs.changelog || github.event.release.body || '' }} retry-attempts: 6 retry-delay: 30000 From 4682032c130dd115cb85237d149482bcba473861 Mon Sep 17 00:00:00 2001 From: North-West-Wind Date: Tue, 30 Jun 2026 22:09:59 +0800 Subject: [PATCH 34/52] ci: use changelog from release --- .github/workflows/publish.yml | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index b7fbf57..45675b9 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -4,10 +4,6 @@ on: release: types: [published] workflow_dispatch: - inputs: - changelog: - description: 'Changelog' - required: false concurrency: group: publish-${{ github.ref }} @@ -60,6 +56,15 @@ jobs: id: gradle-properties run: bash ./.github/scripts/parse-gradle-properties.sh ${{ matrix.version }} + - name: "Fetch release changelog" + id: release + run: | + gh release view "${{ github.ref_name }}" --json body,isPrerelease > /tmp/release.json + echo "prerelease=$(jq -r '.isPrerelease' /tmp/release.json)" >> "$GITHUB_OUTPUT" + jq -r '.body' /tmp/release.json > /tmp/changelog.md + env: + GH_TOKEN: ${{ github.token }} + - uses: Kir-Antipov/mc-publish@v3.3 with: modrinth-id: ${{ vars.MODRINTH_ID }} @@ -73,7 +78,7 @@ jobs: >=${{ steps.gradle-properties.outputs.MIN_MINECRAFT_VERSION }} <=${{ steps.gradle-properties.outputs.MINECRAFT_VERSION }} version: ${{ steps.gradle-properties.outputs.MOD_VERSION }}-${{ matrix.version }}-${{ matrix.loader }} name: ${{ steps.gradle-properties.outputs.MOD_NAME }} ${{ steps.gradle-properties.outputs.MOD_VERSION }} (${{ matrix.version }}-${{ matrix.loader }}) - version-type: ${{ github.event.release.prerelease && 'beta' || 'release' }} - changelog: ${{ inputs.changelog || github.event.release.body || '' }} + version-type: ${{ steps.release.outputs.prerelease == 'true' && 'beta' || 'release' }} + changelog-file: /tmp/changelog.md retry-attempts: 6 retry-delay: 30000 From 318ce828b19dc4527a4a07a1400bac58a1572230 Mon Sep 17 00:00:00 2001 From: North-West-Wind Date: Tue, 30 Jun 2026 22:40:00 +0800 Subject: [PATCH 35/52] ci: only trigger publish on release --- .github/workflows/publish.yml | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 45675b9..78fc82f 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -3,7 +3,6 @@ name: Publish on: release: types: [published] - workflow_dispatch: concurrency: group: publish-${{ github.ref }} @@ -56,15 +55,6 @@ jobs: id: gradle-properties run: bash ./.github/scripts/parse-gradle-properties.sh ${{ matrix.version }} - - name: "Fetch release changelog" - id: release - run: | - gh release view "${{ github.ref_name }}" --json body,isPrerelease > /tmp/release.json - echo "prerelease=$(jq -r '.isPrerelease' /tmp/release.json)" >> "$GITHUB_OUTPUT" - jq -r '.body' /tmp/release.json > /tmp/changelog.md - env: - GH_TOKEN: ${{ github.token }} - - uses: Kir-Antipov/mc-publish@v3.3 with: modrinth-id: ${{ vars.MODRINTH_ID }} @@ -78,7 +68,7 @@ jobs: >=${{ steps.gradle-properties.outputs.MIN_MINECRAFT_VERSION }} <=${{ steps.gradle-properties.outputs.MINECRAFT_VERSION }} version: ${{ steps.gradle-properties.outputs.MOD_VERSION }}-${{ matrix.version }}-${{ matrix.loader }} name: ${{ steps.gradle-properties.outputs.MOD_NAME }} ${{ steps.gradle-properties.outputs.MOD_VERSION }} (${{ matrix.version }}-${{ matrix.loader }}) - version-type: ${{ steps.release.outputs.prerelease == 'true' && 'beta' || 'release' }} - changelog-file: /tmp/changelog.md + version-type: ${{ github.event.release.prerelease && 'beta' || 'release' }} + changelog: ${{ github.event.release.body || '' }} retry-attempts: 6 retry-delay: 30000 From 66906dba240c4631b4e1a9b8ed954ed765beec14 Mon Sep 17 00:00:00 2001 From: North-West-Wind Date: Tue, 30 Jun 2026 22:52:31 +0800 Subject: [PATCH 36/52] ci: centralize building during publish --- .github/workflows/publish.yml | 36 ++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 78fc82f..a910a60 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -22,8 +22,26 @@ jobs: - id: set-matrix run: bash ./.github/scripts/generate-publish-matrix.sh + build: + runs-on: ubuntu-latest + name: Build Everything + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - uses: xxanqw/compilation@v3 + with: + java-version: "25" + + - uses: actions/upload-artifact@v4 + with: + name: mod-artifacts + path: ./**/versions/**/build/libs + if-no-files-found: error + publish: - needs: generate-publish-matrix + needs: [generate-publish-matrix, build] runs-on: ubuntu-latest name: Publish ${{ matrix.loader }} ${{ matrix.version }} strategy: @@ -35,21 +53,9 @@ jobs: with: fetch-depth: 0 - - name: "Set up JDK" - uses: actions/setup-java@v4 + - uses: actions/download-artifact@v4 with: - java-version: 25 - distribution: "adopt" - - - name: "Setup Gradle" - uses: gradle/actions/setup-gradle@v4 - with: - cache-read-only: true - gradle-version: wrapper - add-job-summary: 'on-failure' - - - name: "Run build" - run: ./gradlew ${{ matrix.loader }}:${{ matrix.version }}:build + name: mod-artifacts - name: "Parse gradle properties" id: gradle-properties From c98d7d59c5db882ab59f175f9abb2ff036f91104 Mon Sep 17 00:00:00 2001 From: North-West-Wind Date: Tue, 30 Jun 2026 22:53:36 +0800 Subject: [PATCH 37/52] ci: new push cancel old build --- .github/workflows/build.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5edac6f..4686341 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,6 +4,10 @@ on: push: pull_request: +concurrency: + group: build-${{ github.ref }} + cancel-in-progress: true + jobs: build: runs-on: ubuntu-latest From 1677bf579f5244864c1b998f80fc30e97a37a9eb Mon Sep 17 00:00:00 2001 From: North-West-Wind Date: Wed, 1 Jul 2026 23:06:47 +0800 Subject: [PATCH 38/52] refactor: remove old shared.gradle --- gradle/shared.gradle | 115 ------------------------------------------- 1 file changed, 115 deletions(-) delete mode 100644 gradle/shared.gradle diff --git a/gradle/shared.gradle b/gradle/shared.gradle deleted file mode 100644 index 3493114..0000000 --- a/gradle/shared.gradle +++ /dev/null @@ -1,115 +0,0 @@ -def versionPropsFile = rootProject.file("versions/${sc.current.version}/gradle.properties") -if (versionPropsFile.exists()) { - def props = new Properties() - versionPropsFile.withInputStream { props.load(it) } - props.each { key, value -> project.ext."${key}" = value } -} - -base { - archivesName = "${mod_id}-${loader}-${minecraft_version}" -} - -java { - toolchain.languageVersion = JavaLanguageVersion.of(java_version) - withSourcesJar() - withJavadocJar() -} - -sourceSets { - main { - java { - srcDir rootProject.file("src/main/java") - srcDir rootProject.file("src/${loader}/java") - } - resources { - srcDir rootProject.file("src/main/resources") - srcDir rootProject.file("src/${loader}/resources") - } - } -} - -tasks.withType(Jar).configureEach { - from(rootProject.file('LICENSE')) { - rename { "${it}_${mod_name}" } - } -} - -jar { - manifest { - attributes([ - 'Specification-Title' : mod_name, - 'Specification-Vendor' : mod_author, - 'Specification-Version' : project.jar.archiveVersion, - 'Implementation-Title' : project.name, - 'Implementation-Version': project.jar.archiveVersion, - 'Implementation-Vendor' : mod_author, - 'Built-On-Minecraft' : minecraft_version - ]) - } -} - -dependencies { - compileOnly('net.fabricmc:sponge-mixin:0.17.3+mixin.0.8.7') - compileOnly(annotationProcessor('io.github.llamalad7:mixinextras-common:0.5.3')) - if (loader == "fabric") { - minecraft("com.mojang:minecraft:${minecraft_version}") - implementation("net.fabricmc:fabric-loader:${fabric_loader_version}") - implementation("net.fabricmc.fabric-api:fabric-api:${fabric_version}") - - if (sc.current.parsed <= "1.21.11") { - mappings loom.layered { - officialMojangMappings() - } - } - } -} - -processResources { - var expandProps = [ - 'version' : version, - 'group' : project.group, - 'minecraft_version' : minecraft_version, - 'minecraft_version_range' : minecraft_version_range, - 'fabric_version' : fabric_version, - 'fabric_loader_version' : fabric_loader_version, - 'mod_name' : mod_name, - 'mod_author' : mod_author, - 'mod_id' : mod_id, - 'license' : license, - 'description' : project.description, - 'forge_version' : forge_version, - 'forge_loader_version_range' : forge_loader_version_range, - 'neoforge_version' : neoforge_version, - 'neoforge_loader_version_range': neoforge_loader_version_range, - 'credits' : credits, - 'java_version' : java_version - ] - - var jsonExpandProps = expandProps.collectEntries { - key, value -> [(key): value instanceof String ? value.replace('\n', '\\\\n') : value] - } - - filesMatching(['META-INF/mods.toml', 'META-INF/neoforge.mods.toml']) { - expand expandProps - } - - filesMatching(['pack.mcmeta', 'fabric.mod.json', '*.mixins.json']) { - expand jsonExpandProps - } - - inputs.properties(expandProps) -} - -publishing { - publications { - register('mavenJava', MavenPublication) { - artifactId base.archivesName.get() - from components.java - } - } - repositories { - maven { - url System.getenv('local_maven_url') - } - } -} From 0466011b7ed2d57f5215d4cc55e653e4726a5a94 Mon Sep 17 00:00:00 2001 From: North-West-Wind Date: Thu, 2 Jul 2026 10:13:32 +0800 Subject: [PATCH 39/52] fix: forge mod loading --- .../in/northwestw/autofish/config/Config.java | 8 +++-- .../config/gui/FilterSelectionScreen.java | 6 ++-- .../config/gui/SuperFilterScreen.java | 4 +-- forge/build.gradle.kts | 19 +++++++++++ .../in/northwestw/autofish/AutoFishForge.java | 34 +++++++++++-------- 5 files changed, 50 insertions(+), 21 deletions(-) diff --git a/common/src/main/java/in/northwestw/autofish/config/Config.java b/common/src/main/java/in/northwestw/autofish/config/Config.java index 2d5f5d4..7c25cc6 100644 --- a/common/src/main/java/in/northwestw/autofish/config/Config.java +++ b/common/src/main/java/in/northwestw/autofish/config/Config.java @@ -73,8 +73,12 @@ public static void load() { autoReplace = json.get("auto_replace").getAsBoolean(); if (json.has("all_filters")) allFilters = json.get("all_filters").getAsBoolean(); - if (json.has("filter")) - filter = Stream.of(json.getAsJsonArray("filter")).map(JsonElement::getAsString).collect(Collectors.toList()); + if (json.has("filter")) { + filter = Lists.newArrayList(); + JsonArray arr = json.getAsJsonArray("filter"); + for (int ii = 0; ii < arr.size(); ii++) + filter.add(arr.get(ii).getAsString()); + } // validate if (recastDelay < RECAST_DELAY_RANGE[0] || recastDelay > RECAST_DELAY_RANGE[1]) { diff --git a/common/src/main/java/in/northwestw/autofish/config/gui/FilterSelectionScreen.java b/common/src/main/java/in/northwestw/autofish/config/gui/FilterSelectionScreen.java index cf8a79f..21cf7c3 100644 --- a/common/src/main/java/in/northwestw/autofish/config/gui/FilterSelectionScreen.java +++ b/common/src/main/java/in/northwestw/autofish/config/gui/FilterSelectionScreen.java @@ -99,8 +99,8 @@ public boolean mouseClicked(MouseButtonEvent ev, boolean p_430750_) { //? } elif >=1.19.4 { //List> itemTags = BuiltInRegistries.ITEM.getTags().map(Pair::getSecond).filter(tag -> tags.stream().anyMatch(t -> tag.key().location().getPath().contains(t))).toList(); //? } elif >=1.18.2 { - //List> itemTags = Registry.ITEM.getTags().map(Pair::getSecond).filter(tag -> tags.stream().anyMatch(t -> tag.key().location().getPath().contains(t))).toList(); - //? } else { + /*List> itemTags = Registry.ITEM.getTags().map(Pair::getSecond).filter(tag -> tags.stream().anyMatch(t -> tag.key().location().getPath().contains(t))).toList(); + *///? } else { /*List> itemTags = ItemTags.getAllTags().getAllTags().entrySet().stream() .filter(entry -> tags.stream().anyMatch(t -> entry.getKey().toString().contains(t))) .map(Map.Entry::getValue).collect(Collectors.toList()); @@ -173,8 +173,8 @@ public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mo graphics.drawCenteredString(this.font, this.title, this.width / 2, 20, -1); *///?} else { /*public void render(PoseStack poseStack, int mouseX, int mouseY, float partialTicks) { - super.render(poseStack, mouseX, mouseY, partialTicks); this.renderBackground(poseStack); + super.render(poseStack, mouseX, mouseY, partialTicks); drawCenteredString(poseStack, this.font, this.title, this.width / 2, 20, -1); *///? } Collection searchingCopy = Lists.newArrayList(); diff --git a/common/src/main/java/in/northwestw/autofish/config/gui/SuperFilterScreen.java b/common/src/main/java/in/northwestw/autofish/config/gui/SuperFilterScreen.java index dd0d309..172ca51 100644 --- a/common/src/main/java/in/northwestw/autofish/config/gui/SuperFilterScreen.java +++ b/common/src/main/java/in/northwestw/autofish/config/gui/SuperFilterScreen.java @@ -100,8 +100,8 @@ public boolean mouseClicked(MouseButtonEvent ev, boolean p_430750_) { //? } elif >=1.19.4 { //List> itemTags = BuiltInRegistries.ITEM.getTags().map(Pair::getSecond).filter(tag -> tags.stream().anyMatch(t -> tag.key().location().getPath().contains(t))).toList(); //? } elif >=1.18.2 { - //List> itemTags = Registry.ITEM.getTags().map(Pair::getSecond).filter(tag -> tags.stream().anyMatch(t -> tag.key().location().getPath().contains(t))).toList(); - //? } else { + /*List> itemTags = Registry.ITEM.getTags().map(Pair::getSecond).filter(tag -> tags.stream().anyMatch(t -> tag.key().location().getPath().contains(t))).toList(); + *///? } else { /*List> itemTags = ItemTags.getAllTags().getAllTags().entrySet().stream() .filter(entry -> tags.stream().anyMatch(t -> entry.getKey().toString().contains(t))) .map(Map.Entry::getValue).collect(Collectors.toList()); diff --git a/forge/build.gradle.kts b/forge/build.gradle.kts index c2c9768..5f77e5a 100644 --- a/forge/build.gradle.kts +++ b/forge/build.gradle.kts @@ -1,11 +1,16 @@ plugins { id("multiloader-loader") id("net.minecraftforge.gradle") version "[7.0.17,8)" + id("net.minecraftforge.renamer") version "1.1.2" kotlin("jvm") version "2.2.0" id("com.google.devtools.ksp") version "2.2.0-2.0.2" } +// Version will be added after renaming +if (stonecutter.eval(commonMod.mc, "<=1.20.4")) version = "${commonMod.version}-${stonecutterBuild.current.version}" + minecraft { + if (stonecutter.eval(commonMod.mc, "<1.17")) mappings("parchment", "${commonMod.mc}-${commonMod.dep("parchment")}") mappings("official", commonMod.mc) val at = rootProject.file("src/${loader}/resources/META-INF/accesstransformer.cfg") @@ -51,4 +56,18 @@ repositories { dependencies { implementation(minecraft.dependency("net.minecraftforge:forge:${commonMod.mc}-${commonMod.dep("forge")}")) +} + +// The renamer plugin is required for Forge <= 1.20.4 +if (stonecutter.eval(commonMod.mc, "<=1.20.4")) { + tasks.register("deleteJar") { + description = "Deletes the JAR before renaming" + delete(layout.buildDirectory.file("libs/${commonMod.id}-${version}.jar")) + } + + renamer.classes("renameJar", tasks.named("jar")) { + map.from(minecraft.dependency.toSrgFile) + archiveClassifier = loader + finalizedBy("deleteJar") + } } \ No newline at end of file diff --git a/forge/src/main/java/in/northwestw/autofish/AutoFishForge.java b/forge/src/main/java/in/northwestw/autofish/AutoFishForge.java index c4f6583..09942e2 100644 --- a/forge/src/main/java/in/northwestw/autofish/AutoFishForge.java +++ b/forge/src/main/java/in/northwestw/autofish/AutoFishForge.java @@ -29,10 +29,10 @@ public class AutoFishForge { public AutoFishForge() { } - @Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD) - public static class ModEvents { - @SubscribeEvent + @Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.FORGE) + public static class ForgeEvents { //? if >=1.19.2 { + @SubscribeEvent public static void registerKeyMappings(RegisterKeyMappingsEvent event) { event.register(KeyBinds.autofish); event.register(KeyBinds.rodprotect); @@ -40,20 +40,12 @@ public static void registerKeyMappings(RegisterKeyMappingsEvent event) { event.register(KeyBinds.settings); event.register(KeyBinds.itemfilter); } - //? } else { - /*public static void setupClient(FMLClientSetupEvent event) { - ClientRegistry.registerKeyBinding(KeyBinds.autofish); - ClientRegistry.registerKeyBinding(KeyBinds.rodprotect); - ClientRegistry.registerKeyBinding(KeyBinds.autoreplace); - ClientRegistry.registerKeyBinding(KeyBinds.settings); - ClientRegistry.registerKeyBinding(KeyBinds.itemfilter); - } - *///? } + //? } @SubscribeEvent //? if >=1.19.2 { public static void inputKey(InputEvent.Key event) { - //? } else + //? } else //public static void inputKey(InputEvent.KeyInputEvent event) { AutoFishHandler.onKeyInput(); } @@ -61,7 +53,7 @@ public static void inputKey(InputEvent.Key event) { @SubscribeEvent //? if >=1.21.1 { public static void playerTickPre(TickEvent.PlayerTickEvent.Pre event) { - //? } else { + //? } else { /*public static void playerTickPre(TickEvent.PlayerTickEvent event) { if (event.phase != TickEvent.Phase.START) return; *///? } @@ -74,4 +66,18 @@ public static void playerTickPre(TickEvent.PlayerTickEvent.Pre event) { *///? } } } + + //? if <1.19 { + /*@Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD) + public static class ModEvents { + @SubscribeEvent + public static void setupClient(FMLClientSetupEvent event) { + ClientRegistry.registerKeyBinding(KeyBinds.autofish); + ClientRegistry.registerKeyBinding(KeyBinds.rodprotect); + ClientRegistry.registerKeyBinding(KeyBinds.autoreplace); + ClientRegistry.registerKeyBinding(KeyBinds.settings); + ClientRegistry.registerKeyBinding(KeyBinds.itemfilter); + } + } + *///? } } \ No newline at end of file From 462d2b6919fc79750641acccbf07571126c9c636 Mon Sep 17 00:00:00 2001 From: North-West-Wind Date: Thu, 2 Jul 2026 10:37:41 +0800 Subject: [PATCH 40/52] fix: translation keys --- .../java/in/northwestw/autofish/keybind/KeyBinds.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/common/src/main/java/in/northwestw/autofish/keybind/KeyBinds.java b/common/src/main/java/in/northwestw/autofish/keybind/KeyBinds.java index 86ab88d..55113e4 100644 --- a/common/src/main/java/in/northwestw/autofish/keybind/KeyBinds.java +++ b/common/src/main/java/in/northwestw/autofish/keybind/KeyBinds.java @@ -31,10 +31,10 @@ public class KeyBinds { KeyMapping.Category cat = KeyMapping.Category.register(Identifier.fromNamespaceAndPath(AutoFish.MOD_ID, "autofish")); //? } else //String cat = "key.categories.autofish"; - autofish = new KeyMapping(AutoFish.getTranslatableComponent("key.forgeautofish.autofish").getString(), KEY_MINUS, cat); - rodprotect = new KeyMapping(AutoFish.getTranslatableComponent("key.forgeautofish.rodprotect").getString(), KEY_BACKSLASH, cat); - autoreplace = new KeyMapping(AutoFish.getTranslatableComponent("key.forgeautofish.autoreplace").getString(), KEY_RIGHT_BRACKET, cat); - settings = new KeyMapping(AutoFish.getTranslatableComponent("key.forgeautofish.settings").getString(), KEY_K, cat); - itemfilter = new KeyMapping(AutoFish.getTranslatableComponent("key.forgeautofish.itemfilter").getString(), KEY_APOSTROPHE, cat); + autofish = new KeyMapping(AutoFish.getTranslatableComponent("key.autofish.autofish").getString(), KEY_MINUS, cat); + rodprotect = new KeyMapping(AutoFish.getTranslatableComponent("key.autofish.rodprotect").getString(), KEY_BACKSLASH, cat); + autoreplace = new KeyMapping(AutoFish.getTranslatableComponent("key.autofish.autoreplace").getString(), KEY_RIGHT_BRACKET, cat); + settings = new KeyMapping(AutoFish.getTranslatableComponent("key.autofish.settings").getString(), KEY_K, cat); + itemfilter = new KeyMapping(AutoFish.getTranslatableComponent("key.autofish.itemfilter").getString(), KEY_APOSTROPHE, cat); } } From 31ecc34835a970d87e3d61fbac589123799bfdac Mon Sep 17 00:00:00 2001 From: North-West-Wind Date: Thu, 2 Jul 2026 10:38:10 +0800 Subject: [PATCH 41/52] fix: background rendering --- .../in/northwestw/autofish/config/gui/SuperFilterScreen.java | 1 + 1 file changed, 1 insertion(+) diff --git a/common/src/main/java/in/northwestw/autofish/config/gui/SuperFilterScreen.java b/common/src/main/java/in/northwestw/autofish/config/gui/SuperFilterScreen.java index 172ca51..d738034 100644 --- a/common/src/main/java/in/northwestw/autofish/config/gui/SuperFilterScreen.java +++ b/common/src/main/java/in/northwestw/autofish/config/gui/SuperFilterScreen.java @@ -167,6 +167,7 @@ public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mo graphics.drawCenteredString(this.font, this.title, this.width / 2, 20, -1); *///? } else { /*public void render(PoseStack poseStack, int mouseX, int mouseY, float partialTicks) { + this.renderBackground(poseStack); super.render(poseStack, mouseX, mouseY, partialTicks); drawCenteredString(poseStack, this.font, this.title, this.width / 2, 20, -1); *///? } From eb7503ab4786e708bedbce45f18df079d1b72a5d Mon Sep 17 00:00:00 2001 From: North-West-Wind Date: Thu, 2 Jul 2026 10:38:39 +0800 Subject: [PATCH 42/52] fix: item filter for 1.19.2- --- .../main/java/in/northwestw/autofish/AutoFish.java | 4 ++-- .../northwestw/autofish/handler/AutoFishHandler.java | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/common/src/main/java/in/northwestw/autofish/AutoFish.java b/common/src/main/java/in/northwestw/autofish/AutoFish.java index cdf511c..6b98eb0 100644 --- a/common/src/main/java/in/northwestw/autofish/AutoFish.java +++ b/common/src/main/java/in/northwestw/autofish/AutoFish.java @@ -35,8 +35,8 @@ public static MutableComponent getLiteralComponent(String str) { //? if >=1.21.1 { return MutableComponent.create(new PlainTextContents.LiteralContents(str)); //? } elif >=1.19.2 { - //return MutableComponent.create(new LiteralContents(str)); - //? } else + /*return MutableComponent.create(new LiteralContents(str)); + *///? } else //return new TextComponent(str); } } diff --git a/common/src/main/java/in/northwestw/autofish/handler/AutoFishHandler.java b/common/src/main/java/in/northwestw/autofish/handler/AutoFishHandler.java index 02c2283..1818cd7 100644 --- a/common/src/main/java/in/northwestw/autofish/handler/AutoFishHandler.java +++ b/common/src/main/java/in/northwestw/autofish/handler/AutoFishHandler.java @@ -34,7 +34,7 @@ public class AutoFishHandler { private static boolean processingDrop, pendingReelIn, pendingRecast, lastTickFishing, afterDrop; private static int dropCd, rodSlot; private static long tick, checkTick; - private static final Map itemsBeforeFished = Maps.newHashMap(); + private static final Map itemsBeforeFished = Maps.newHashMap(); public static void onKeyInput() { Minecraft minecraft = Minecraft.getInstance(); @@ -144,9 +144,9 @@ private static void reelIn(Player player) { //? } else //Identifier rl = Registry.ITEM.getKey(stack.getItem()); //? if >=26.1 { - itemsBeforeFished.put(rl, itemsBeforeFished.getOrDefault(rl, 0) + stack.count()); + itemsBeforeFished.put(rl.toString(), itemsBeforeFished.getOrDefault(rl, 0) + stack.count()); //? } else - //itemsBeforeFished.put(rl, itemsBeforeFished.getOrDefault(rl, 0) + stack.getCount()); + //itemsBeforeFished.put(rl.toString(), itemsBeforeFished.getOrDefault(rl.toString(), 0) + stack.getCount()); }); click(player, hand, Minecraft.getInstance().gameMode); ItemStack fishingRod = player.getItemInHand(hand); @@ -158,7 +158,7 @@ else if (fishingRod.getMaxDamage() - fishingRod.getDamageValue() < 3 && !player. if (Config.autoReplace) needReplace = true; else { Config.autoFish = false; - sendOverlayMessage(player, "forgeautofish", Config.autoFish); + sendOverlayMessage(player, "autofish", Config.autoFish); return; } if (needReplace) { @@ -221,8 +221,8 @@ private static void checkItem(Player player) { //Optional opt = Registry.ITEM.getOptional(rl); if (!opt.isPresent()) continue; Item item = opt.get(); - int newCount = items.stream().filter(stack -> stack.getItem().toString().equals(rl.toString())).mapToInt(ItemStack::getCount).reduce(Integer::sum).orElse(0); - int oldCount = itemsBeforeFished.getOrDefault(rl, 0); + int newCount = items.stream().filter(stack -> stack.getItem().equals(item)).mapToInt(ItemStack::getCount).reduce(Integer::sum).orElse(0); + int oldCount = itemsBeforeFished.getOrDefault(rl.toString(), 0); int diff = newCount - oldCount; for (int ii = 0; ii < diff; ii++) shouldDrop.add(item); } From 0531555fb6ee27882ea8af0ec77ce1516124280d Mon Sep 17 00:00:00 2001 From: North-West-Wind Date: Thu, 2 Jul 2026 10:41:24 +0800 Subject: [PATCH 43/52] ci: togglable publish --- .github/workflows/publish.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index a910a60..f662dad 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -63,10 +63,10 @@ jobs: - uses: Kir-Antipov/mc-publish@v3.3 with: - modrinth-id: ${{ vars.MODRINTH_ID }} - modrinth-token: ${{ secrets.MODRINTH_TOKEN }} - curseforge-id: ${{ vars.CURSEFORGE_ID }} - curseforge-token: ${{ secrets.CURSEFORGE_TOKEN }} + modrinth-id: ${{ vars.MODRINTH_PUB && vars.MODRINTH_ID }} + modrinth-token: ${{ vars.MODRINTH_PUB && secrets.MODRINTH_TOKEN }} + curseforge-id: ${{ vars.CURSEFORGE_PUB && vars.CURSEFORGE_ID }} + curseforge-token: ${{ vars.CURSEFORGE_PUB && secrets.CURSEFORGE_TOKEN }} files: | ${{ matrix.loader }}/versions/${{ matrix.version }}/build/libs/*.jar loaders: ${{ join(matrix.supported_loaders, ' ') }} From 6983e9e4ae44633a9708eed40daaaf356ab6c785 Mon Sep 17 00:00:00 2001 From: North-West-Wind Date: Thu, 2 Jul 2026 10:55:53 +0800 Subject: [PATCH 44/52] ci: cache build & reusable build for publish --- .github/workflows/build.yml | 23 ++++++++++++++++++++--- .github/workflows/publish.yml | 17 +---------------- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4686341..e08e560 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -3,6 +3,7 @@ name: Build on: push: pull_request: + workflow_call: concurrency: group: build-${{ github.ref }} @@ -10,7 +11,10 @@ concurrency: jobs: build: + name: Build Everything runs-on: ubuntu-latest + outputs: + artifact-name: ${{ steps.upload.outputs.artifact-name }} steps: - name: Checkout @@ -18,10 +22,23 @@ jobs: with: fetch-depth: 0 - - name: Compile with Java 25 - uses: xxanqw/compilation@v3 + - name: Set up JDK + uses: actions/setup-java@v3 with: - java-version: "25" + java-version: 25 + distribution: temurin + cache: gradle + + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 + + - name: Make gradlew executable + run: chmod +x ./gradlew + shell: bash + + - name: Build with Gradle + run: ./gradlew build + shell: bash - name: Upload Artifacts uses: actions/upload-artifact@v4 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index f662dad..7125780 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -23,22 +23,7 @@ jobs: run: bash ./.github/scripts/generate-publish-matrix.sh build: - runs-on: ubuntu-latest - name: Build Everything - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - uses: xxanqw/compilation@v3 - with: - java-version: "25" - - - uses: actions/upload-artifact@v4 - with: - name: mod-artifacts - path: ./**/versions/**/build/libs - if-no-files-found: error + uses: ./.github/workflows/build.yml publish: needs: [generate-publish-matrix, build] From 2730754d0af0d0ca14c388c845fcdd408039a9a3 Mon Sep 17 00:00:00 2001 From: North-West-Wind Date: Thu, 2 Jul 2026 12:00:34 +0800 Subject: [PATCH 45/52] chore: revert license to GPLv3 --- LICENSE | 795 +++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 674 insertions(+), 121 deletions(-) diff --git a/LICENSE b/LICENSE index 0e259d4..e72bfdd 100644 --- a/LICENSE +++ b/LICENSE @@ -1,121 +1,674 @@ -Creative Commons Legal Code - -CC0 1.0 Universal - - CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE - LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN - ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS - INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES - REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS - PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM - THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED - HEREUNDER. - -Statement of Purpose - -The laws of most jurisdictions throughout the world automatically confer -exclusive Copyright and Related Rights (defined below) upon the creator -and subsequent owner(s) (each and all, an "owner") of an original work of -authorship and/or a database (each, a "Work"). - -Certain owners wish to permanently relinquish those rights to a Work for -the purpose of contributing to a commons of creative, cultural and -scientific works ("Commons") that the public can reliably and without fear -of later claims of infringement build upon, modify, incorporate in other -works, reuse and redistribute as freely as possible in any form whatsoever -and for any purposes, including without limitation commercial purposes. -These owners may contribute to the Commons to promote the ideal of a free -culture and the further production of creative, cultural and scientific -works, or to gain reputation or greater distribution for their Work in -part through the use and efforts of others. - -For these and/or other purposes and motivations, and without any -expectation of additional consideration or compensation, the person -associating CC0 with a Work (the "Affirmer"), to the extent that he or she -is an owner of Copyright and Related Rights in the Work, voluntarily -elects to apply CC0 to the Work and publicly distribute the Work under its -terms, with knowledge of his or her Copyright and Related Rights in the -Work and the meaning and intended legal effect of CC0 on those rights. - -1. Copyright and Related Rights. A Work made available under CC0 may be -protected by copyright and related or neighboring rights ("Copyright and -Related Rights"). Copyright and Related Rights include, but are not -limited to, the following: - - i. the right to reproduce, adapt, distribute, perform, display, - communicate, and translate a Work; - ii. moral rights retained by the original author(s) and/or performer(s); -iii. publicity and privacy rights pertaining to a person's image or - likeness depicted in a Work; - iv. rights protecting against unfair competition in regards to a Work, - subject to the limitations in paragraph 4(a), below; - v. rights protecting the extraction, dissemination, use and reuse of data - in a Work; - vi. database rights (such as those arising under Directive 96/9/EC of the - European Parliament and of the Council of 11 March 1996 on the legal - protection of databases, and under any national implementation - thereof, including any amended or successor version of such - directive); and -vii. other similar, equivalent or corresponding rights throughout the - world based on applicable law or treaty, and any national - implementations thereof. - -2. Waiver. To the greatest extent permitted by, but not in contravention -of, applicable law, Affirmer hereby overtly, fully, permanently, -irrevocably and unconditionally waives, abandons, and surrenders all of -Affirmer's Copyright and Related Rights and associated claims and causes -of action, whether now known or unknown (including existing as well as -future claims and causes of action), in the Work (i) in all territories -worldwide, (ii) for the maximum duration provided by applicable law or -treaty (including future time extensions), (iii) in any current or future -medium and for any number of copies, and (iv) for any purpose whatsoever, -including without limitation commercial, advertising or promotional -purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each -member of the public at large and to the detriment of Affirmer's heirs and -successors, fully intending that such Waiver shall not be subject to -revocation, rescission, cancellation, termination, or any other legal or -equitable action to disrupt the quiet enjoyment of the Work by the public -as contemplated by Affirmer's express Statement of Purpose. - -3. Public License Fallback. Should any part of the Waiver for any reason -be judged legally invalid or ineffective under applicable law, then the -Waiver shall be preserved to the maximum extent permitted taking into -account Affirmer's express Statement of Purpose. In addition, to the -extent the Waiver is so judged Affirmer hereby grants to each affected -person a royalty-free, non transferable, non sublicensable, non exclusive, -irrevocable and unconditional license to exercise Affirmer's Copyright and -Related Rights in the Work (i) in all territories worldwide, (ii) for the -maximum duration provided by applicable law or treaty (including future -time extensions), (iii) in any current or future medium and for any number -of copies, and (iv) for any purpose whatsoever, including without -limitation commercial, advertising or promotional purposes (the -"License"). The License shall be deemed effective as of the date CC0 was -applied by Affirmer to the Work. Should any part of the License for any -reason be judged legally invalid or ineffective under applicable law, such -partial invalidity or ineffectiveness shall not invalidate the remainder -of the License, and in such case Affirmer hereby affirms that he or she -will not (i) exercise any of his or her remaining Copyright and Related -Rights in the Work or (ii) assert any associated claims and causes of -action with respect to the Work, in either case contrary to Affirmer's -express Statement of Purpose. - -4. Limitations and Disclaimers. - - a. No trademark or patent rights held by Affirmer are waived, abandoned, - surrendered, licensed or otherwise affected by this document. - b. Affirmer offers the Work as-is and makes no representations or - warranties of any kind concerning the Work, express, implied, - statutory or otherwise, including without limitation warranties of - title, merchantability, fitness for a particular purpose, non - infringement, or the absence of latent or other defects, accuracy, or - the present or absence of errors, whether or not discoverable, all to - the greatest extent permissible under applicable law. - c. Affirmer disclaims responsibility for clearing rights of other persons - that may apply to the Work or any use thereof, including without - limitation any person's Copyright and Related Rights in the Work. - Further, Affirmer disclaims responsibility for obtaining any necessary - consents, permissions or other rights required for any use of the - Work. - d. Affirmer understands and acknowledges that Creative Commons is not a - party to this document and has no duty or obligation with respect to - this CC0 or use of the Work. + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. \ No newline at end of file From 0a7cdf304e35da6fe9f2211b68adb75a299c45dc Mon Sep 17 00:00:00 2001 From: North-West-Wind Date: Thu, 2 Jul 2026 12:10:09 +0800 Subject: [PATCH 46/52] chore: update README.md --- README.md | 46 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 6c6a23e..1d0cb44 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,42 @@ -# Multiloader - Stonecutter -A gradle project template allows you to make a multi-loaders+versions mod using [Multi-loader Template](https://github.com/jaredlll08/MultiLoader-Template/) and [Stonecutter](https://stonecutter.kikugie.dev/). +# AutoFish for Everyone +Finally! An AFK fishing mod for everyone! -This template is based on [Faboslav](https://github.com/Faboslav) mods, extracted to be used as template. +## Download +- [CurseForge](https://www.curseforge.com/minecraft/mc-mods/autofish-for-everyone) +- [Modrinth](https://modrinth.com/mod/autofish-for-everyone) -# How-to Setup -// TODO +Thanks for using the mod! ---- -Note: This template has not carefully tested so there might be bugs, feel free to report or PR to make this template better! \ No newline at end of file +## Supported Minecraft Versions and Mod Loaders +Versions not listed here are not supported + +| Minecraft Version | Fabric | Forge | NeoForge | +|-------------------|--------|-------|----------| +| 26.2 | ✅ | ✅ | ✅ | +| 26.1.2 | ✅ | ✅ | ✅ | +| 1.21.11 | ✅ | ✅ | ✅ | +| 1.21.1 | ✅ | ✅ | ✅ | +| 1.20.1 | ✅ | ✅ | ❌ | +| 1.19.4 | ✅ | ✅ | ❌ | +| 1.19.2 | ✅ | ✅ | ❌ | +| 1.18.2 | ✅ | ✅ | ❌ | +| 1.17.1 | ✅ | ✅ | ❌ | +| 1.16.5 | ✅ | ❌ | ❌ | + +## What does it do? +This mod allows you to AFK fish (as long as the server allows AFK) anywhere. Can I use it in my singleplayer world? Yes! Can I use it on servers? Yes! The mod is completely client-side! You just need a Forge client on your computer, put this mod into the "mods" folder and you finished the setup! How easy it is! + +Note: Putting the mod into the "mods" folder of a server will NOT do anything. +There is also NO Fabric version of this mod, as there are other fishing mods for Fabric already. + +## Why did I make this? +To answer that, we need to talk about ~~parallel universe~~ the 1.16 update of Minecraft. If you read the changelogs, there is 1 particular part that nerfed the entire AFK fishing farm. Basically, it still allows players to fish with it, but you will not get any treasure (e.g. Enchanted Books, Saddles, etc.). On the other hand, you can still get fish from it. Since the farm is nerfed, players started to create other designs of the fishing farm. However, those are not as good as the old ones. That's what causes me to make this mod, which I think quite a lot of players needed it. + +After I made the very first version of the mod, why not make it for more versions of Minecraft? And that's the reason I made the mod for more versions. I know some other Forge fishing mods exist in older versions, but they are not accurate. + +## How does it work? +Programmers have probably looked at the source code already, but allow me to explain that for non-coders. + +When the mod is enabled, it will look for the bobber of the player. You may think that I look for the state of the bobber but no. The state of the bobber is not public, and there is no public methods that returns the state, so it is impossible to listen for change of state. + +However, I found a way simplier method to know if is catch a fish...\*drumroll\* Motion. Since, the bobber is an entity, we can track its motion. As we all know, the bobber sinks into the water when it catches a fish. By tracking the vertical motion of the bobber, we can know when it catches a fish. It is simple as that! \ No newline at end of file From 5ded20f9d50b84ca1700fffa2a8aea92e03a298f Mon Sep 17 00:00:00 2001 From: North-West-Wind Date: Thu, 2 Jul 2026 12:10:31 +0800 Subject: [PATCH 47/52] fix: bump patch version --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 0d8c40e..511f63c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,7 +5,7 @@ org.gradle.jvmargs=-Xmx2G mod.name=AutoFish for Everyone mod.id=autofish mod.group=in.northwestw.in -mod.version=8.0.0 +mod.version=8.0.1 mod.author=NorthWestWind mod.description=I like playing survival, but fishing is a boring activity...\nTherefore, I made this mod!\nNow you can AFK Fish like no one else! mod.license=GPL-3.0 From 11d3f5428c96dabf04f39d15ff841543e5056f20 Mon Sep 17 00:00:00 2001 From: North-West-Wind Date: Thu, 2 Jul 2026 12:27:50 +0800 Subject: [PATCH 48/52] ci: parallel building --- ...e-publish-matrix.sh => generate-matrix.sh} | 0 .github/workflows/build.yml | 35 ++++++++++++++----- .github/workflows/publish.yml | 22 ++++++------ 3 files changed, 38 insertions(+), 19 deletions(-) rename .github/scripts/{generate-publish-matrix.sh => generate-matrix.sh} (100%) diff --git a/.github/scripts/generate-publish-matrix.sh b/.github/scripts/generate-matrix.sh similarity index 100% rename from .github/scripts/generate-publish-matrix.sh rename to .github/scripts/generate-matrix.sh diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e08e560..295709b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,17 +10,32 @@ concurrency: cancel-in-progress: true jobs: + generate-matrix: + runs-on: ubuntu-latest + name: Generate Matrix + outputs: + matrix: ${{ steps.set-matrix.outputs.matrix }} + steps: + - uses: actions/checkout@v6 + with: + fetch-depth: 1 + + - id: set-matrix + run: bash ./.github/scripts/generate-matrix.sh + build: + needs: generate-matrix name: Build Everything runs-on: ubuntu-latest - outputs: - artifact-name: ${{ steps.upload.outputs.artifact-name }} + strategy: + max-parallel: 3 + matrix: ${{ fromJSON(needs.generate-matrix.outputs.matrix) }} steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: - fetch-depth: 0 + fetch-depth: 1 - name: Set up JDK uses: actions/setup-java@v3 @@ -30,18 +45,22 @@ jobs: cache: gradle - name: Setup Gradle - uses: gradle/gradle-build-action@v2 + uses: gradle/actions/setup-gradle@v6 + with: + cache-read-only: false + gradle-version: wrapper + add-job-summary: 'on-failure' - name: Make gradlew executable run: chmod +x ./gradlew shell: bash - name: Build with Gradle - run: ./gradlew build + run: ./gradlew ${{ matrix.loader }}:${{ matrix.version }}:build shell: bash - name: Upload Artifacts uses: actions/upload-artifact@v4 with: - name: mod-artifacts - path: ./**/versions/**/build/libs + name: artifact-${{ matrix.loader }}-${{ matrix.version }} + path: ./${{ matrix.loader }}/versions/${{ matrix.version }}/build/libs diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 7125780..ff77aa6 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -9,38 +9,38 @@ concurrency: cancel-in-progress: false jobs: - generate-publish-matrix: + generate-matrix: runs-on: ubuntu-latest - name: Generate Publish Matrix + name: Generate Matrix outputs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: - fetch-depth: 0 + fetch-depth: 1 - id: set-matrix - run: bash ./.github/scripts/generate-publish-matrix.sh + run: bash ./.github/scripts/generate-matrix.sh build: uses: ./.github/workflows/build.yml publish: - needs: [generate-publish-matrix, build] + needs: [generate-matrix, build] runs-on: ubuntu-latest name: Publish ${{ matrix.loader }} ${{ matrix.version }} strategy: max-parallel: 3 fail-fast: false - matrix: ${{ fromJSON(needs.generate-publish-matrix.outputs.matrix) }} + matrix: ${{ fromJSON(needs.generate-matrix.outputs.matrix) }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: - fetch-depth: 0 + fetch-depth: 1 - uses: actions/download-artifact@v4 with: - name: mod-artifacts + name: artifact-${{ matrix.loader }}-${{ matrix.version }} - name: "Parse gradle properties" id: gradle-properties @@ -53,7 +53,7 @@ jobs: curseforge-id: ${{ vars.CURSEFORGE_PUB && vars.CURSEFORGE_ID }} curseforge-token: ${{ vars.CURSEFORGE_PUB && secrets.CURSEFORGE_TOKEN }} files: | - ${{ matrix.loader }}/versions/${{ matrix.version }}/build/libs/*.jar + ${{ steps.gradle-properties.outputs.MOD_ID }}-${{ steps.gradle-properties.outputs.MOD_VERSION }}-${{ matrix.version }}-${{ matrix.loader }}.jar loaders: ${{ join(matrix.supported_loaders, ' ') }} game-versions: | >=${{ steps.gradle-properties.outputs.MIN_MINECRAFT_VERSION }} <=${{ steps.gradle-properties.outputs.MINECRAFT_VERSION }} From 6a5b10f0688207780c33e412fd8eb806b1f013f1 Mon Sep 17 00:00:00 2001 From: North-West-Wind Date: Thu, 2 Jul 2026 12:40:43 +0800 Subject: [PATCH 49/52] ci: YOUR TAKING TOO LONG (revert parallel) --- ...e-matrix.sh => generate-publish-matrix.sh} | 0 .github/workflows/build.yml | 23 +++---------------- .github/workflows/publish.yml | 14 +++++------ 3 files changed, 10 insertions(+), 27 deletions(-) rename .github/scripts/{generate-matrix.sh => generate-publish-matrix.sh} (100%) diff --git a/.github/scripts/generate-matrix.sh b/.github/scripts/generate-publish-matrix.sh similarity index 100% rename from .github/scripts/generate-matrix.sh rename to .github/scripts/generate-publish-matrix.sh diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 295709b..0290da5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,26 +10,9 @@ concurrency: cancel-in-progress: true jobs: - generate-matrix: - runs-on: ubuntu-latest - name: Generate Matrix - outputs: - matrix: ${{ steps.set-matrix.outputs.matrix }} - steps: - - uses: actions/checkout@v6 - with: - fetch-depth: 1 - - - id: set-matrix - run: bash ./.github/scripts/generate-matrix.sh - build: - needs: generate-matrix name: Build Everything runs-on: ubuntu-latest - strategy: - max-parallel: 3 - matrix: ${{ fromJSON(needs.generate-matrix.outputs.matrix) }} steps: - name: Checkout @@ -56,11 +39,11 @@ jobs: shell: bash - name: Build with Gradle - run: ./gradlew ${{ matrix.loader }}:${{ matrix.version }}:build + run: ./gradlew build shell: bash - name: Upload Artifacts uses: actions/upload-artifact@v4 with: - name: artifact-${{ matrix.loader }}-${{ matrix.version }} - path: ./${{ matrix.loader }}/versions/${{ matrix.version }}/build/libs + name: mod-artifacts + path: ./**/versions/**/build/libs diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index ff77aa6..0a765a5 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -9,9 +9,9 @@ concurrency: cancel-in-progress: false jobs: - generate-matrix: + generate-publish-matrix: runs-on: ubuntu-latest - name: Generate Matrix + name: Generate Publish Matrix outputs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: @@ -20,19 +20,19 @@ jobs: fetch-depth: 1 - id: set-matrix - run: bash ./.github/scripts/generate-matrix.sh + run: bash ./.github/scripts/generate-publish-matrix.sh build: uses: ./.github/workflows/build.yml publish: - needs: [generate-matrix, build] + needs: [generate-publish-matrix, build] runs-on: ubuntu-latest name: Publish ${{ matrix.loader }} ${{ matrix.version }} strategy: max-parallel: 3 fail-fast: false - matrix: ${{ fromJSON(needs.generate-matrix.outputs.matrix) }} + matrix: ${{ fromJSON(needs.generate-publish-matrix.outputs.matrix) }} steps: - uses: actions/checkout@v6 with: @@ -40,7 +40,7 @@ jobs: - uses: actions/download-artifact@v4 with: - name: artifact-${{ matrix.loader }}-${{ matrix.version }} + name: mod-artifacts - name: "Parse gradle properties" id: gradle-properties @@ -53,7 +53,7 @@ jobs: curseforge-id: ${{ vars.CURSEFORGE_PUB && vars.CURSEFORGE_ID }} curseforge-token: ${{ vars.CURSEFORGE_PUB && secrets.CURSEFORGE_TOKEN }} files: | - ${{ steps.gradle-properties.outputs.MOD_ID }}-${{ steps.gradle-properties.outputs.MOD_VERSION }}-${{ matrix.version }}-${{ matrix.loader }}.jar + ${{ matrix.loader }}/versions/${{ matrix.version }}/build/libs/*.jar loaders: ${{ join(matrix.supported_loaders, ' ') }} game-versions: | >=${{ steps.gradle-properties.outputs.MIN_MINECRAFT_VERSION }} <=${{ steps.gradle-properties.outputs.MINECRAFT_VERSION }} From b0a7a7e8e149ada2fe727530f56022c615fd9dc1 Mon Sep 17 00:00:00 2001 From: North-West-Wind Date: Thu, 2 Jul 2026 14:49:50 +0800 Subject: [PATCH 50/52] ci: build only on branch push --- .github/workflows/build.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0290da5..649e7f9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,6 +2,8 @@ name: Build on: push: + branches: + - multiloader-stonecutter pull_request: workflow_call: From 401c5d640a473d49b1ca8d53eccaa611bd47f64f Mon Sep 17 00:00:00 2001 From: North-West-Wind Date: Thu, 2 Jul 2026 16:08:33 +0800 Subject: [PATCH 51/52] fix: properly apply parchment --- forge/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/forge/build.gradle.kts b/forge/build.gradle.kts index 5f77e5a..743185c 100644 --- a/forge/build.gradle.kts +++ b/forge/build.gradle.kts @@ -10,8 +10,8 @@ plugins { if (stonecutter.eval(commonMod.mc, "<=1.20.4")) version = "${commonMod.version}-${stonecutterBuild.current.version}" minecraft { - if (stonecutter.eval(commonMod.mc, "<1.17")) mappings("parchment", "${commonMod.mc}-${commonMod.dep("parchment")}") - mappings("official", commonMod.mc) + if (commonMod.depOrNull("parchment") != null) mappings("parchment", "${commonMod.mc}-${commonMod.dep("parchment")}") + else mappings("official", commonMod.mc) val at = rootProject.file("src/${loader}/resources/META-INF/accesstransformer.cfg") if (at.exists()) { From 9b2bfa410169d03e478a39ff77e28246df70688f Mon Sep 17 00:00:00 2001 From: North-West-Wind Date: Mon, 6 Jul 2026 18:29:25 +0800 Subject: [PATCH 52/52] test: use common run directory --- fabric/build.gradle.kts | 2 ++ neoforge/build.gradle.kts | 2 ++ 2 files changed, 4 insertions(+) diff --git a/fabric/build.gradle.kts b/fabric/build.gradle.kts index 9148e42..78dcce7 100644 --- a/fabric/build.gradle.kts +++ b/fabric/build.gradle.kts @@ -34,11 +34,13 @@ loom { client() configName = "Fabric Client" ideConfigGenerated(true) + runDirectory = rootProject.layout.projectDirectory.dir("runs/client") } getByName("server") { server() configName = "Fabric Server" ideConfigGenerated(true) + runDirectory = rootProject.layout.projectDirectory.dir("runs/server") } } diff --git a/neoforge/build.gradle.kts b/neoforge/build.gradle.kts index 867d9a4..02a0901 100644 --- a/neoforge/build.gradle.kts +++ b/neoforge/build.gradle.kts @@ -12,10 +12,12 @@ neoForge { register("client") { client() ideName = "NeoForge Client (${project.path})" + gameDirectory = rootProject.layout.projectDirectory.dir("runs/client") } register("server") { server() ideName = "NeoForge Server (${project.path})" + gameDirectory = rootProject.layout.projectDirectory.dir("runs/server") } }