From 256901ff009915b80d463dbb77dcac937f47eff5 Mon Sep 17 00:00:00 2001 From: seb Date: Sun, 10 May 2026 20:55:01 +0200 Subject: [PATCH] Refactor build system to support 1.21.10, 1.21.11, and 26.1.2 --- .github/workflows/build.yml | 8 +- .gitignore | 25 ++- README.md | 52 +++-- build.gradle | 86 -------- build.gradle.kts | 120 ++++++++++++ gradle.properties | 23 +-- settings.gradle | 17 -- settings.gradle.kts | 34 ++++ .../github/sebseb7/autotrade/InitHandler.java | 2 + .../sebseb7/autotrade/config/Configs.java | 38 ++-- .../autotrade/event/AutoTradeClientTick.java | 13 ++ .../autotrade/event/ContainerIoHelper.java | 25 +++ .../gui/EnchantmentSelectionScreen.java | 183 ++++++++++++++++++ .../gui/MerchantScreenButtonInjector.java | 93 +++++++++ .../render/TraderHighlightRenderer.java | 35 +++- .../autotrade/render/VillagerTradeCache.java | 8 +- .../render/VillagerTradeOverlayRenderer.java | 58 +++--- .../{resources => templates}/fabric.mod.json | 16 +- stonecutter.gradle.kts | 10 + versions/1.21.10-fabric/gradle.properties | 8 + versions/1.21.11-fabric/gradle.properties | 8 + versions/26.1.2-fabric/gradle.properties | 8 + 22 files changed, 665 insertions(+), 205 deletions(-) delete mode 100644 build.gradle create mode 100644 build.gradle.kts delete mode 100644 settings.gradle create mode 100644 settings.gradle.kts create mode 100644 src/main/java/com/github/sebseb7/autotrade/gui/EnchantmentSelectionScreen.java create mode 100644 src/main/java/com/github/sebseb7/autotrade/gui/MerchantScreenButtonInjector.java rename src/main/{resources => templates}/fabric.mod.json (59%) create mode 100644 stonecutter.gradle.kts create mode 100644 versions/1.21.10-fabric/gradle.properties create mode 100644 versions/1.21.11-fabric/gradle.properties create mode 100644 versions/26.1.2-fabric/gradle.properties diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a9219a5..65d56b2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,15 +7,15 @@ jobs: environment: modrinth steps: - uses: actions/checkout@v3 - - name: Set up JDK 21 + - name: Set up JDK 25 uses: actions/setup-java@v3 with: - java-version: '21' + java-version: '25' distribution: 'temurin' - name: Setup Gradle uses: gradle/gradle-build-action@v2 - - name: Execute Gradle build - run: ./gradlew build + - name: Execute Gradle build (all Stonecutter targets) + run: ./gradlew chiseledBuild - run: mkdir staging && cp build/libs/*.jar staging - run: cd build/libs && md5sum *.jar > ../../md5sum.txt - run: echo "filename=`ls build/libs/*.jar |xargs basename`" >> $GITHUB_ENV diff --git a/.gitignore b/.gitignore index 5290444..a25c5a2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,24 @@ -.gradle +# Gradle +.gradle/ build/ -.jdk21/ \ No newline at end of file + +# Local JDK / toolchain installs (optional per-machine) +.jdk21/ + +# Fabric Loom – dev client / game output +run/ +runs/ + +# IDE +.idea/ +*.iml + +# OS +.DS_Store +Thumbs.db + +# Logs & local scratch +*.log +out.txt +full_build.txt +compile_err.txt diff --git a/README.md b/README.md index 3a70ff1..4d0ad27 100644 --- a/README.md +++ b/README.md @@ -4,13 +4,13 @@ ## Table of contents 1. [Description](#description) -2. [Build](#build-for-1203--1204) +2. [Build](#build) 3. [Known Issues](#known-issues) 4. [Possible Setup](#possible-setup) 5. [Void Trading Example & Settings](#void-trading-example--settings) 6. [WDL](#wdl) -feel free to ask questions: https://github.com/sebseb7/autotrade-fabric/discussions +Feel free to ask questions: https://github.com/sebseb7/autotrade-fabric/discussions ### Description @@ -24,29 +24,51 @@ Beginning with version v0.0.10 you can select the sell/buy items using nametagge From v0.0.13, those selections (and the set-buy / set-sell hotkeys) store exact enchantments when the stack is enchanted, so you can target a specific book or tool; a plain item id in config still matches any enchantment variant of that item. -if you can't access settings via the keybind, try modmenu https://modrinth.com/mod/modmenu +If you can't access settings via the keybind, try Mod Menu https://modrinth.com/mod/modmenu -#### Supported Version: +#### Supported versions (this branch) -- Minecraft 1.19.4 - 1.20.4 , 1.21.11, 26.1.2 +Fabric targets maintained in-tree ([Stonecutter](https://stonecutter.kikugie.dev/) + [Modstitch](https://github.com/modunion/modstitch)): -### Build for 1.20.3 / 1.20.4 +- **Minecraft 1.21.10** (`:1.21.10-fabric`) +- **Minecraft 1.21.11** (`:1.21.11-fabric`) +- **Minecraft 26.1.2** (`:26.1.2-fabric`) -``` -./gradlew build +Older Minecraft releases (for example 1.19.x–1.20.x) are not built from this multi-target setup; use an older release tag or branch if you need those versions. + +### Build + +Requirements: a JDK suitable for the target (the build uses Java **21** for 1.21.x and **25** for 26.x via Gradle toolchains). + +Build **every** configured game version (recommended before you commit): + +```bash +./gradlew chiseledBuild ``` -#### Build for older minecraft versions: +If the parallel build is flaky, try: -``` -./gradlew build -Pminecraft_version_out=1.20.2 -Pminecraft_version=1.20.2 -Pminecraft_version_min=1.20.2 -Pmalilib_version=0.17.0 -Pmod_menu_version=8.0.1 -Pmappings_version=1.20.2+build.4 -./gradlew build -Pminecraft_version_out=1.20.1 -Pminecraft_version=1.20.1 -Pminecraft_version_min=1.20 -Pmalilib_version=0.16.1 -Pmod_menu_version=7.0.1 -Pmappings_version=1.20.1+build.10 -./gradlew build -Pminecraft_version_out=1.19.4 -Pminecraft_version=1.19.4 -Pminecraft_version_min=1.19.4 -Pmalilib_version=0.15.2 -Pmod_menu_version=6.1.0 -Pmappings_version=1.19.4+build.2 +```bash +./gradlew chiseledBuild --max-workers=1 ``` -#### Requires: +Build **one** Fabric target (jar ends up under that version subproject, e.g. `versions/1.21.11-fabric/build/libs/`): -- malilib +```bash +./gradlew :1.21.10-fabric:build +./gradlew :1.21.11-fabric:build +./gradlew :26.1.2-fabric:build +``` + +When using Stonecutter’s active-project workflow, reset it before committing: + +```bash +./gradlew resetActiveProject +``` + +#### Requires + +- malilib ### Known Issues diff --git a/build.gradle b/build.gradle deleted file mode 100644 index 2e009f9..0000000 --- a/build.gradle +++ /dev/null @@ -1,86 +0,0 @@ -plugins { - id 'net.fabricmc.fabric-loom' version '1.16-SNAPSHOT' - id 'com.diffplug.spotless' version '6.19.0' - id "com.modrinth.minotaur" version "2.+" -} - -java { - toolchain { - languageVersion = JavaLanguageVersion.of(25) - } -} - -repositories { - exclusiveContent { - forRepository { - maven { - name = 'Modrinth' - url = 'https://api.modrinth.com/maven' - } - } - filter { - includeGroup 'maven.modrinth' - } - } - maven { url 'https://maven.terraformersmc.com/releases/' } - maven { url 'https://jitpack.io' } - flatDir { - dirs '.' - } -} - -dependencies { - minecraft "com.mojang:minecraft:${project.minecraft_version}" - implementation "net.fabricmc:fabric-loader:${project.fabric_loader_version}" - // Official 26.1 template uses `implementation` for fabric-api (not modImplementation). - implementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_api_version}" - implementation "maven.modrinth:malilib:${project.malilib_version}" - compileOnly "com.terraformersmc:modmenu:${project.mod_menu_version}" -} - -group = project.group + "." + project.mod_id -base { - archivesName = project.mod_file_name + '-' + project.minecraft_version_out -} -version = project.mod_version - -if (version.endsWith('-dev')) { - version += "." + new Date().format('yyyyMMdd.HHmmss') -} - -processResources { - inputs.property "mod_version", project.mod_version - - filesMatching("fabric.mod.json") { - expand([ - "mod_version" : project.version, - "minecraft_version_min": project.property("minecraft_version_min"), - "malilib_version" : project.property("malilib_version") - ]) - } -} - -tasks.withType(JavaCompile).configureEach { - it.options.encoding = "UTF-8" -} - -spotless { - java { - importOrder() - removeUnusedImports() - cleanthat() - eclipse() - formatAnnotations() - } -} - -modrinth { - token = System.getenv("MODRINTH_TOKEN") - syncBodyFrom = rootProject.file("README.md").text - projectId = 'C1naQCmt' - // Loom 1.16+ with net.fabricmc.fabric-loom (unobfuscated MC) does not create remapJar; ship the standard jar - uploadFile = tasks.jar - gameVersions = ['26.1.2'] - loaders = ['fabric'] - dependencies = [] -} diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..23c2caf --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,120 @@ +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter +import org.gradle.jvm.toolchain.JavaLanguageVersion + +plugins { + id("dev.isxander.modstitch.base") version "0.8.4" + id("com.modrinth.minotaur") version "2.+" +} + +repositories { + mavenCentral() + maven("https://maven.fabricmc.net/") + maven("https://api.modrinth.com/maven") + maven("https://maven.terraformersmc.com/releases/") + maven("https://jitpack.io") +} + +val minecraft = findProperty("deps.minecraft")?.toString() + ?: error("deps.minecraft must be set by the active Stonecutter target (see versions/*/gradle.properties).") + +val javaLanguageVersion = when (minecraft) { + "1.21.10", "1.21.11" -> 21 + "26.1.2" -> 25 + else -> error("Add Java toolchain mapping for Minecraft $minecraft in build.gradle.kts.") +} + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(javaLanguageVersion)) + } +} + +val rawModVersion = findProperty("mod_version")?.toString() ?: error("mod_version") +val modReleaseVersion = if (rawModVersion.endsWith("-dev")) { + "$rawModVersion.${DateTimeFormatter.ofPattern("yyyyMMdd.HHmmss").format(LocalDateTime.now())}" +} else { + rawModVersion +} + +modstitch { + minecraftVersion = minecraft + + metadata { + modId = findProperty("mod_id")?.toString() ?: error("mod_id") + modName = findProperty("mod_name")?.toString() ?: error("mod_name") + modVersion = modReleaseVersion + modGroup = findProperty("mod_group")?.toString() ?: error("mod_group") + modAuthor = findProperty("author")?.toString() ?: error("author") + + replacementProperties.put("mod_author", findProperty("author")?.toString() ?: error("author")) + replacementProperties.put("mod_issue_tracker", "https://github.com/sebseb7/autotrade-fabric/issues") + replacementProperties.put("mod_sources", "https://github.com/sebseb7/autotrade-fabric") + replacementProperties.put("mod_homepage", "https://modrinth.com/mod/autotrade-fabric") + replacementProperties.put("malilib_version", findProperty("malilib_version")?.toString() ?: error("malilib_version")) + replacementProperties.put( + "fabric_api_dependency", + when (minecraft) { + "26.1.2" -> ">=0.145.0" + "1.21.11" -> ">=0.140.0" + "1.21.10" -> ">=0.136.0" + else -> error("fabric_api_dependency mapping for $minecraft") + }, + ) + replacementProperties.put( + "minecraft_dependency", + ">=" + (findProperty("minecraft_version_min")?.toString() ?: minecraft), + ) + } + + loom { + fabricLoaderVersion = findProperty("fabric_loader_version")?.toString() + ?: error("fabric_loader_version") + + configureLoom { + } + } +} + +val loader = project.name.substringAfterLast("-") +stonecutter { + consts( + "fabric" to loader.equals("fabric", ignoreCase = true), + "neoforge" to loader.equals("neoforge", ignoreCase = true), + "forge" to loader.equals("forge", ignoreCase = true), + "vanilla" to loader.equals("vanilla", ignoreCase = true), + "mc26" to (minecraft == "26.1.2"), + "npcSplit" to (minecraft == "26.1.2" || minecraft == "1.21.11"), + "npcFlat" to (minecraft == "1.21.10"), + ) +} + +dependencies { + val fabricApi = findProperty("fabric_api_version")?.toString() ?: error("fabric_api_version") + val malilib = findProperty("malilib_version")?.toString() ?: error("malilib_version") + val modMenu = findProperty("mod_menu_version")?.toString() ?: error("mod_menu_version") + + modstitchModImplementation("net.fabricmc.fabric-api:fabric-api:$fabricApi") + modstitchModImplementation("maven.modrinth:malilib:$malilib") + modstitchModCompileOnly("com.terraformersmc:modmenu:$modMenu") +} + +group = findProperty("mod_group")?.toString() ?: error("mod_group") +base { + archivesName.set("${findProperty("mod_file_name")}-$minecraft") +} + +version = modReleaseVersion + +tasks.withType().configureEach { + options.encoding = "UTF-8" +} + +modrinth { + token.set(System.getenv("MODRINTH_TOKEN")) + syncBodyFrom.set(rootProject.file("README.md").readText()) + projectId.set("C1naQCmt") + uploadFile.set(tasks.jar) + gameVersions.addAll(listOf("26.1.2", "1.21.10", "1.21.11")) + loaders.add("fabric") +} diff --git a/gradle.properties b/gradle.properties index 2842e19..d0c1013 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,20 +3,11 @@ org.gradle.jvmargs=-Xmx1G org.gradle.parallel=true org.gradle.configuration-cache=false -group = com.github.sebseb7 -mod_id = autotrade -mod_name = AutoTrade -author = sebseb7 -mod_file_name = autotrade-fabric +group=com.github.sebseb7 +mod_id=autotrade +mod_name=AutoTrade +mod_group=com.github.sebseb7.autotrade +author=sebseb7 +mod_file_name=autotrade-fabric -mod_version = 0.0.14 - -malilib_version = 0.28.2 -minecraft_version_min = 26.1.2 -minecraft_version_out = 26.1.2 - -minecraft_version = 26.1.2 - -fabric_loader_version = 0.19.2 -fabric_api_version = 0.145.4+26.1.2 -mod_menu_version = 18.0.0-alpha.8 +mod_version=0.0.14 diff --git a/settings.gradle b/settings.gradle deleted file mode 100644 index c167b5a..0000000 --- a/settings.gradle +++ /dev/null @@ -1,17 +0,0 @@ -pluginManagement { - repositories { - maven { - name = 'Fabric' - url = 'https://maven.fabricmc.net/' - } - mavenCentral() - gradlePluginPortal() - } -} - -// Lets Gradle 21 run the build while a JDK 25 toolchain is used to compile (required by 26.1 Mod Menu, etc.) -plugins { - id 'org.gradle.toolchains.foojay-resolver-convention' version '1.0.0' -} - -rootProject.name = 'autotrade-fabric' diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..7a7d6b3 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,34 @@ +pluginManagement { + repositories { + gradlePluginPortal() + mavenCentral() + maven("https://maven.isxander.dev/releases/") + maven("https://maven.fabricmc.net/") + maven("https://maven.neoforged.net/releases/") + maven("https://maven.kikugie.dev/releases") + maven("https://maven.kikugie.dev/snapshots") + } +} + +plugins { + id("dev.kikugie.stonecutter") version "0.6+" + id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0" +} + +stonecutter { + kotlinController = true + centralScript = "build.gradle.kts" + + create(rootProject) { + fun mc(mcVersion: String, name: String = mcVersion, loaders: Iterable) = + loaders.forEach { vers("$name-$it", mcVersion) } + + mc("26.1.2", loaders = listOf("fabric")) + mc("1.21.10", loaders = listOf("fabric")) + mc("1.21.11", loaders = listOf("fabric")) + + vcsVersion = "26.1.2-fabric" + } +} + +rootProject.name = "autotrade-fabric" diff --git a/src/main/java/com/github/sebseb7/autotrade/InitHandler.java b/src/main/java/com/github/sebseb7/autotrade/InitHandler.java index df82e32..2395212 100644 --- a/src/main/java/com/github/sebseb7/autotrade/InitHandler.java +++ b/src/main/java/com/github/sebseb7/autotrade/InitHandler.java @@ -3,6 +3,7 @@ package com.github.sebseb7.autotrade; import com.github.sebseb7.autotrade.config.Configs; import com.github.sebseb7.autotrade.event.InputHandler; import com.github.sebseb7.autotrade.event.KeybindCallbacks; +import com.github.sebseb7.autotrade.gui.MerchantScreenButtonInjector; import com.github.sebseb7.autotrade.render.TraderHighlightRenderer; import com.github.sebseb7.autotrade.render.VillagerTradeOverlayRenderer; import fi.dy.masa.malilib.config.ConfigManager; @@ -19,6 +20,7 @@ public class InitHandler implements IInitializationHandler { TraderHighlightRenderer.register(); VillagerTradeOverlayRenderer.register(); + MerchantScreenButtonInjector.register(); InputHandler handler = new InputHandler(); InputEventHandler.getKeybindManager().registerKeybindProvider(handler); diff --git a/src/main/java/com/github/sebseb7/autotrade/config/Configs.java b/src/main/java/com/github/sebseb7/autotrade/config/Configs.java index 8faf5c5..f59d8a4 100644 --- a/src/main/java/com/github/sebseb7/autotrade/config/Configs.java +++ b/src/main/java/com/github/sebseb7/autotrade/config/Configs.java @@ -2,16 +2,21 @@ package com.github.sebseb7.autotrade.config; import com.github.sebseb7.autotrade.Reference; import com.google.common.collect.ImmutableList; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import com.google.gson.JsonParser; import fi.dy.masa.malilib.config.ConfigUtils; import fi.dy.masa.malilib.config.IConfigHandler; import fi.dy.masa.malilib.config.IConfigValue; import fi.dy.masa.malilib.config.options.ConfigBoolean; import fi.dy.masa.malilib.config.options.ConfigInteger; import fi.dy.masa.malilib.config.options.ConfigString; -import fi.dy.masa.malilib.util.JsonUtils; import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import net.fabricmc.loader.api.FabricLoader; public class Configs implements IConfigHandler { @@ -60,25 +65,32 @@ public class Configs implements IConfigHandler { 30000000, "delay in ticks; to get signal from trapped chest"); public static final ConfigBoolean SHOW_TRADES = new ConfigBoolean("showTrades", true, "Display villager/wandering-trader trades above their heads (requires trading with them once to cache the offers)"); + public static final ConfigString SELECTED_ENCHANTMENTS = new ConfigString("selectedEnchantments", "", + "Comma-separated list of selected enchantment IDs (set via the \"Select Enchantments\" button on a librarian's trade screen)"); public static final ImmutableList OPTIONS = ImmutableList.of(ENABLED, ITEM_FRAME, GLASS_BLOCK, SELECTOR_OFFSET, ENABLE_SELL, SELL_ITEM, SELL_LIMIT, ENABLE_BUY, BUY_ITEM, BUY_LIMIT, MAX_INPUT_ITEMS, INPUT_CONTAINER_X, INPUT_CONTAINER_Y, INPUT_CONTAINER_Z, OUTPUT_CONTAINER_X, OUTPUT_CONTAINER_Y, OUTPUT_CONTAINER_Z, VOID_TRADING_DELAY, VOID_TRADING_DELAY_AFTER_TELEPORT, CONTAINER_CLOSE_DELAY, - SHOW_TRADES); + SHOW_TRADES, SELECTED_ENCHANTMENTS); } public static void loadFromFile() { File configFile = new File(getConfigDirectory(), CONFIG_FILE_NAME); if (configFile.exists() && configFile.isFile() && configFile.canRead()) { - JsonElement element = JsonUtils.parseJsonFile(configFile.toPath()); + try { + String json = Files.readString(configFile.toPath(), StandardCharsets.UTF_8); + JsonElement element = JsonParser.parseString(json); - if (element != null && element.isJsonObject()) { - JsonObject root = element.getAsJsonObject(); + if (element != null && element.isJsonObject()) { + JsonObject root = element.getAsJsonObject(); - ConfigUtils.readConfigBase(root, "Generic", Generic.OPTIONS); - ConfigUtils.readConfigBase(root, "Hotkeys", Hotkeys.HOTKEY_LIST); + ConfigUtils.readConfigBase(root, "Generic", Generic.OPTIONS); + ConfigUtils.readConfigBase(root, "Hotkeys", Hotkeys.HOTKEY_LIST); + } + } catch (IOException ignored) { + // Malformed or unreadable config; defaults stay active. } } } @@ -87,12 +99,16 @@ public class Configs implements IConfigHandler { File dir = getConfigDirectory(); if ((dir.exists() && dir.isDirectory()) || dir.mkdirs()) { - JsonObject root = new JsonObject(); + try { + JsonObject root = new JsonObject(); - ConfigUtils.writeConfigBase(root, "Generic", Generic.OPTIONS); - ConfigUtils.writeConfigBase(root, "Hotkeys", Hotkeys.HOTKEY_LIST); + ConfigUtils.writeConfigBase(root, "Generic", Generic.OPTIONS); + ConfigUtils.writeConfigBase(root, "Hotkeys", Hotkeys.HOTKEY_LIST); - JsonUtils.writeJsonToFile(root, new File(dir, CONFIG_FILE_NAME).toPath()); + Gson gson = new GsonBuilder().setPrettyPrinting().create(); + Files.writeString(new File(dir, CONFIG_FILE_NAME).toPath(), gson.toJson(root), StandardCharsets.UTF_8); + } catch (IOException ignored) { + } } } diff --git a/src/main/java/com/github/sebseb7/autotrade/event/AutoTradeClientTick.java b/src/main/java/com/github/sebseb7/autotrade/event/AutoTradeClientTick.java index df85bef..9d9a01c 100644 --- a/src/main/java/com/github/sebseb7/autotrade/event/AutoTradeClientTick.java +++ b/src/main/java/com/github/sebseb7/autotrade/event/AutoTradeClientTick.java @@ -19,8 +19,14 @@ import net.minecraft.network.protocol.game.ServerboundSelectTradePacket; import net.minecraft.world.InteractionHand; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.decoration.ItemFrame; +//? if npcSplit { import net.minecraft.world.entity.npc.villager.Villager; import net.minecraft.world.entity.npc.wanderingtrader.WanderingTrader; +//?} +//? if npcFlat { +import net.minecraft.world.entity.npc.Villager; +import net.minecraft.world.entity.npc.WanderingTrader; +//?} import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.AbstractContainerMenu; @@ -33,7 +39,9 @@ import net.minecraft.world.item.trading.MerchantOffers; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.BlockHitResult; +//? if mc26 { import net.minecraft.world.phys.EntityHitResult; +//?} import net.minecraft.world.phys.Vec3; final class AutoTradeClientTick { @@ -144,8 +152,12 @@ final class AutoTradeClientTick { if (!newVillagersInRange.contains(entity)) { found = true; newVillagersInRange.add(entity); + //? if mc26 { EntityHitResult ehr = new EntityHitResult(entity, entity.position()); mc.gameMode.interact(mc.player, entity, ehr, InteractionHand.MAIN_HAND); + //?} else { + mc.gameMode.interact(mc.player, entity, InteractionHand.MAIN_HAND); + //?} voidDelay = Configs.Generic.VOID_TRADING_DELAY.getIntegerValue(); villagerActive = entity.getId(); state = false; @@ -338,6 +350,7 @@ final class AutoTradeClientTick { } } screen.onClose(); + ContainerIoHelper.syncPlayerInventoryAfterMerchant(mc); startTraderGlow(mc, villagerActive); } diff --git a/src/main/java/com/github/sebseb7/autotrade/event/ContainerIoHelper.java b/src/main/java/com/github/sebseb7/autotrade/event/ContainerIoHelper.java index bd7c27a..48c45a4 100644 --- a/src/main/java/com/github/sebseb7/autotrade/event/ContainerIoHelper.java +++ b/src/main/java/com/github/sebseb7/autotrade/event/ContainerIoHelper.java @@ -9,7 +9,11 @@ import java.util.Map; import net.minecraft.client.Minecraft; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.inventory.AbstractContainerMenu; +//? if mc26 { import net.minecraft.world.inventory.ContainerInput; +//?} else { +import net.minecraft.world.inventory.ClickType; +//?} import net.minecraft.world.inventory.Slot; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; @@ -20,7 +24,28 @@ final class ContainerIoHelper { static void quickMoveResultSlot(Minecraft mc, AbstractContainerMenu menu, int slotIndex) { Slot slot = menu.getSlot(slotIndex); + //? if mc26 { mc.gameMode.handleContainerInput(menu.containerId, slot.index, 0, ContainerInput.QUICK_MOVE, mc.player); + //?} else { + mc.gameMode.handleInventoryMouseClick(menu.containerId, slot.index, 0, ClickType.QUICK_MOVE, mc.player); + //?} + } + + /** + * After automated merchant packets (select trade + shift-clicks), client-side + * prediction can drift from the server. Flush the carried/cursor stack and run + * again on the next tick so pending slot updates have landed. + */ + static void syncPlayerInventoryAfterMerchant(Minecraft mc) { + if (mc.player == null || mc.gameMode == null) { + return; + } + mc.gameMode.ensureHasSentCarriedItem(); + mc.execute(() -> { + if (mc.player != null && mc.gameMode != null) { + mc.gameMode.ensureHasSentCarriedItem(); + } + }); } private static final String EMERALD_SPEC = "minecraft:emerald"; diff --git a/src/main/java/com/github/sebseb7/autotrade/gui/EnchantmentSelectionScreen.java b/src/main/java/com/github/sebseb7/autotrade/gui/EnchantmentSelectionScreen.java new file mode 100644 index 0000000..ff67743 --- /dev/null +++ b/src/main/java/com/github/sebseb7/autotrade/gui/EnchantmentSelectionScreen.java @@ -0,0 +1,183 @@ +package com.github.sebseb7.autotrade.gui; + +import com.github.sebseb7.autotrade.config.Configs; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import net.minecraft.client.Minecraft; +//? if mc26 { +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.Checkbox; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.core.Registry; +import net.minecraft.core.registries.Registries; +import net.minecraft.network.chat.Component; +import net.minecraft.world.item.enchantment.Enchantment; + +/** + * Full-screen GUI that lists every registered enchantment at every possible + * level with a checkbox. For example, Sharpness (max level 5) produces five + * rows: {@code minecraft:sharpness=1} through {@code minecraft:sharpness=5}. + * + *

+ * Ticked entries are persisted into + * {@link Configs.Generic#SELECTED_ENCHANTMENTS} as a comma-separated list (e.g. + * {@code minecraft:sharpness=5,minecraft:mending=1}). + */ +public class EnchantmentSelectionScreen extends Screen { + private final Screen parent; + + /** + * Every (enchantment, level) pair in alphabetical order. Each entry's + * {@link EnchantmentEntry#key} is the string that gets saved to config (e.g. + * {@code minecraft:sharpness=3}). + */ + private List entries = List.of(); + + /** Currently selected set (mutable copy while the screen is open). */ + private final Set selected = new LinkedHashSet<>(); + + private static final int ROW_HEIGHT = 24; + private static final int LEFT_PADDING = 10; + + /** Current scroll offset (in pixels). */ + private int scrollOffset = 0; + + /** Total content height (number of rows × ROW_HEIGHT). */ + private int contentHeight = 0; + + /** Checkboxes created for the current scroll window. */ + private final List checkboxEntries = new ArrayList<>(); + + public EnchantmentSelectionScreen(Screen parent) { + super(Component.literal("Select Enchantments")); + this.parent = parent; + } + + @Override + protected void init() { + super.init(); + + // Load currently-selected enchantments from config. + selected.clear(); + String cfg = Configs.Generic.SELECTED_ENCHANTMENTS.getStringValue().trim(); + if (!cfg.isEmpty()) { + Arrays.stream(cfg.split(",")).map(String::trim).filter(s -> !s.isEmpty()).forEach(selected::add); + } + + // Build (enchantment, level) pairs from the dynamic registry. + entries = new ArrayList<>(); + Minecraft mc = Minecraft.getInstance(); + if (mc.level != null) { + Registry registry = mc.level.registryAccess().lookupOrThrow(Registries.ENCHANTMENT); + var ids = new ArrayList<>(registry.keySet()); + ids.sort((a, b) -> a.toString().compareTo(b.toString())); + for (var id : ids) { + Enchantment ench = registry.getValue(id); + if (ench == null) { + continue; + } + int maxLevel = ench.getMaxLevel(); + for (int level = 1; level <= maxLevel; level++) { + String key = id.toString() + "=" + level; + String label = id.toString() + " " + level; + entries.add(new EnchantmentEntry(key, label)); + } + } + } + + contentHeight = entries.size() * ROW_HEIGHT; + + // "Done" button at the bottom. + this.addRenderableWidget(Button.builder(Component.literal("Done"), btn -> onDone()) + .bounds(this.width / 2 - 100, this.height - 28, 200, 20).build()); + + rebuildCheckboxes(); + } + + /** Rebuild the checkbox widgets for the current scroll offset. */ + private void rebuildCheckboxes() { + // Remove old checkboxes. + for (CheckboxEntry entry : checkboxEntries) { + this.removeWidget(entry.checkbox); + } + checkboxEntries.clear(); + + for (int i = 0; i < entries.size(); i++) { + int rowY = 30 + i * ROW_HEIGHT - scrollOffset; + if (rowY + ROW_HEIGHT < 30 || rowY > this.height - 36) { + continue; // off-screen + } + EnchantmentEntry e = entries.get(i); + boolean checked = selected.contains(e.key); + + Checkbox cb = Checkbox.builder(Component.literal(e.label), this.font).pos(LEFT_PADDING, rowY) + .selected(checked).build(); + this.addRenderableWidget(cb); + checkboxEntries.add(new CheckboxEntry(i, cb)); + } + } + + @Override + public boolean mouseScrolled(double mouseX, double mouseY, double horizontalAmount, double verticalAmount) { + int maxScroll = Math.max(0, contentHeight - (this.height - 60)); + scrollOffset = (int) Math.max(0, Math.min(maxScroll, scrollOffset - verticalAmount * ROW_HEIGHT)); + syncCheckboxSelections(); + rebuildCheckboxes(); + return true; + } + + /** Before rebuilding, read back checkbox state into our selected set. */ + private void syncCheckboxSelections() { + for (CheckboxEntry entry : checkboxEntries) { + String key = entries.get(entry.index).key; + if (entry.checkbox.selected()) { + selected.add(key); + } else { + selected.remove(key); + } + } + } + + //? if mc26 { + @Override + public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float partialTick) { + super.extractRenderState(graphics, mouseX, mouseY, partialTick); + graphics.centeredText(this.font, this.title, this.width / 2, 10, 0xFFFFFF); + } + //?} else { + @Override + public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) { + super.render(guiGraphics, mouseX, mouseY, partialTick); + guiGraphics.drawCenteredString(this.font, this.title, this.width / 2, 10, 0xFFFFFF); + } + //?} + + private void onDone() { + syncCheckboxSelections(); + String value = selected.stream().sorted().collect(Collectors.joining(",")); + Configs.Generic.SELECTED_ENCHANTMENTS.setValueFromString(value); + Configs.saveToFile(); + this.minecraft.setScreen(parent); + } + + @Override + public void onClose() { + onDone(); + } + + /** An enchantment at a specific level. */ + private record EnchantmentEntry(String key, String label) { + } + + /** Pairs a checkbox widget with its index in {@link #entries}. */ + private record CheckboxEntry(int index, Checkbox checkbox) { + } +} diff --git a/src/main/java/com/github/sebseb7/autotrade/gui/MerchantScreenButtonInjector.java b/src/main/java/com/github/sebseb7/autotrade/gui/MerchantScreenButtonInjector.java new file mode 100644 index 0000000..cdfbcdb --- /dev/null +++ b/src/main/java/com/github/sebseb7/autotrade/gui/MerchantScreenButtonInjector.java @@ -0,0 +1,93 @@ +package com.github.sebseb7.autotrade.gui; + +import com.github.sebseb7.autotrade.render.VillagerTradeCache; +import java.util.List; +import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents; +import net.fabricmc.fabric.api.client.screen.v1.Screens; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.gui.screens.inventory.MerchantScreen; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.Entity; +//? if npcSplit { +import net.minecraft.world.entity.npc.villager.Villager; +import net.minecraft.world.entity.npc.wanderingtrader.WanderingTrader; +//?} +//? if npcFlat { +import net.minecraft.world.entity.npc.Villager; +import net.minecraft.world.entity.npc.WanderingTrader; +//?} +import net.minecraft.world.item.trading.MerchantOffers; +import net.minecraft.world.phys.AABB; + +public final class MerchantScreenButtonInjector { + + private MerchantScreenButtonInjector() { + } + + public static void register() { + ScreenEvents.AFTER_INIT.register(MerchantScreenButtonInjector::onScreenInit); + } + + private static void onScreenInit(Minecraft client, Screen screen, int scaledWidth, int scaledHeight) { + if (!(screen instanceof MerchantScreen merchantScreen)) { + return; + } + + // Add button during AFTER_INIT so it is properly registered as renderable. + // We position it safely to the right of the merchant GUI. + // The merchant GUI is 276 pixels wide and centered. + Button button = Button + .builder(Component.literal("Select Enchantments"), + btn -> client.setScreen(new EnchantmentSelectionScreen(merchantScreen))) + .bounds(scaledWidth / 2 + 140, scaledHeight / 2 - 83, 120, 20).build(); + Screen asScreen = merchantScreen; + //? if mc26 { + Screens.getWidgets(asScreen).add(button); + //?} else { + Screens.getButtons(asScreen).add(button); + //?} + + // Offers arrive via a server packet after the screen opens. + // Register a per-screen tick handler to wait for offers and cache them. + final boolean[] handled = {false}; + ScreenEvents.afterTick(merchantScreen).register(s -> { + if (handled[0]) { + return; + } + MerchantOffers offers = merchantScreen.getMenu().getOffers(); + if (offers == null || offers.isEmpty()) { + return; // not yet synced + } + handled[0] = true; + + cacheOffersForNearestTrader(client, offers); + }); + } + + private static void cacheOffersForNearestTrader(Minecraft mc, MerchantOffers offers) { + if (mc.player == null || mc.level == null) { + return; + } + + AABB searchBox = mc.player.getBoundingBox().inflate(10.0); + List nearby = mc.level.getEntitiesOfClass(Entity.class, searchBox); + + Entity closest = null; + double closestDist = Double.MAX_VALUE; + + for (Entity entity : nearby) { + if (entity instanceof Villager || entity instanceof WanderingTrader) { + double dist = entity.distanceToSqr(mc.player); + if (dist < closestDist) { + closestDist = dist; + closest = entity; + } + } + } + if (closest != null) { + VillagerTradeCache.put(closest.getUUID(), offers); + } + } +} diff --git a/src/main/java/com/github/sebseb7/autotrade/render/TraderHighlightRenderer.java b/src/main/java/com/github/sebseb7/autotrade/render/TraderHighlightRenderer.java index 6d13e3c..e59b92b 100644 --- a/src/main/java/com/github/sebseb7/autotrade/render/TraderHighlightRenderer.java +++ b/src/main/java/com/github/sebseb7/autotrade/render/TraderHighlightRenderer.java @@ -1,5 +1,6 @@ package com.github.sebseb7.autotrade.render; +//? if mc26 { import com.github.sebseb7.autotrade.config.Configs; import com.github.sebseb7.autotrade.event.KeybindCallbacks; import com.mojang.blaze3d.vertex.PoseStack; @@ -16,6 +17,7 @@ import net.minecraft.world.entity.Entity; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.shapes.Shapes; +//?} /** * Client wireframe highlights: last-traded villager, and input/output container @@ -23,6 +25,17 @@ import net.minecraft.world.phys.shapes.Shapes; * boxes). */ public final class TraderHighlightRenderer { + + private TraderHighlightRenderer() { + } + + public static void register() { + //? if mc26 { + LevelRenderEvents.AFTER_SOLID_FEATURES.register(TraderHighlightRenderer::renderLevel); + //?} + } + + //? if mc26 { private static final ShapeRenderer SHAPE_RENDERER = new ShapeRenderer(); private static final int TRADER_OUTLINE_COLOR = 0xFF66FF66; @@ -31,14 +44,7 @@ public final class TraderHighlightRenderer { private static final float LINE_WIDTH = 2.5F; - private TraderHighlightRenderer() { - } - - public static void register() { - LevelRenderEvents.AFTER_SOLID_FEATURES.register(TraderHighlightRenderer::render); - } - - private static void render(LevelRenderContext context) { + private static void renderLevel(LevelRenderContext context) { Minecraft mc = Minecraft.getInstance(); if (mc.level == null) { return; @@ -57,14 +63,18 @@ public final class TraderHighlightRenderer { Vec3 camera = mc.gameRenderer.getMainCamera().position(); float tickDelta = mc.getDeltaTracker().getGameTimeDeltaPartialTick(true); + renderBoxes(mc, drawPose, consumer, camera, tickDelta, trader, inTicks, outTicks); + } + + private static void renderBoxes(Minecraft mc, PoseStack drawPose, VertexConsumer consumer, Vec3 camera, + float tickDelta, Entity trader, int inTicks, int outTicks) { if (trader != null) { double offX = Mth.lerp(tickDelta, trader.xOld, trader.getX()) - trader.getX(); double offY = Mth.lerp(tickDelta, trader.yOld, trader.getY()) - trader.getY(); double offZ = Mth.lerp(tickDelta, trader.zOld, trader.getZ()) - trader.getZ(); AABB worldBox = trader.getBoundingBox().move(offX, offY, offZ); AABB cameraRelative = worldBox.move(-camera.x, -camera.y, -camera.z); - SHAPE_RENDERER.renderShape(drawPose, consumer, Shapes.create(cameraRelative), 0.0D, 0.0D, 0.0D, - TRADER_OUTLINE_COLOR, LINE_WIDTH); + renderShape(drawPose, consumer, cameraRelative, TRADER_OUTLINE_COLOR); } if (inTicks > 0) { @@ -86,7 +96,12 @@ public final class TraderHighlightRenderer { int color) { AABB world = AABB.encapsulatingFullBlocks(pos, pos); AABB cameraRelative = world.move(-camera.x, -camera.y, -camera.z); + renderShape(drawPose, consumer, cameraRelative, color); + } + + private static void renderShape(PoseStack drawPose, VertexConsumer consumer, AABB cameraRelative, int color) { SHAPE_RENDERER.renderShape(drawPose, consumer, Shapes.create(cameraRelative), 0.0D, 0.0D, 0.0D, color, LINE_WIDTH); } + //?} } diff --git a/src/main/java/com/github/sebseb7/autotrade/render/VillagerTradeCache.java b/src/main/java/com/github/sebseb7/autotrade/render/VillagerTradeCache.java index d0a88b8..d00af27 100644 --- a/src/main/java/com/github/sebseb7/autotrade/render/VillagerTradeCache.java +++ b/src/main/java/com/github/sebseb7/autotrade/render/VillagerTradeCache.java @@ -9,7 +9,8 @@ import net.minecraft.world.item.trading.MerchantOffers; * Client-side cache of villager/wandering-trader trade offers, keyed by entity * UUID. * - *

Populated by {@code AutoTradeClientTick} when the mod opens a merchant + *

+ * Populated by {@code AutoTradeClientTick} when the mod opens a merchant * screen; consumed by {@link VillagerTradeOverlayRenderer} to draw trade labels * above each villager's head. */ @@ -24,7 +25,10 @@ public final class VillagerTradeCache { CACHE.put(entityUuid, offers); } - /** Retrieve cached offers, or {@code null} if we haven't seen this entity trade yet. */ + /** + * Retrieve cached offers, or {@code null} if we haven't seen this entity trade + * yet. + */ public static MerchantOffers get(UUID entityUuid) { return CACHE.get(entityUuid); } diff --git a/src/main/java/com/github/sebseb7/autotrade/render/VillagerTradeOverlayRenderer.java b/src/main/java/com/github/sebseb7/autotrade/render/VillagerTradeOverlayRenderer.java index e871806..f215bf5 100644 --- a/src/main/java/com/github/sebseb7/autotrade/render/VillagerTradeOverlayRenderer.java +++ b/src/main/java/com/github/sebseb7/autotrade/render/VillagerTradeOverlayRenderer.java @@ -2,34 +2,36 @@ package com.github.sebseb7.autotrade.render; import com.github.sebseb7.autotrade.config.Configs; import com.mojang.blaze3d.vertex.PoseStack; -import java.util.ArrayList; -import java.util.List; +//? if mc26 { import net.fabricmc.fabric.api.client.rendering.v1.level.LevelRenderContext; import net.fabricmc.fabric.api.client.rendering.v1.level.LevelRenderEvents; +//?} import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; -import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.util.Mth; import net.minecraft.world.entity.Entity; +//? if mc26 { import net.minecraft.world.entity.npc.villager.Villager; import net.minecraft.world.entity.npc.wanderingtrader.WanderingTrader; +//?} import net.minecraft.world.item.trading.MerchantOffer; import net.minecraft.world.item.trading.MerchantOffers; import net.minecraft.world.phys.Vec3; import org.joml.Matrix4f; +import java.util.ArrayList; +import java.util.List; + /** * Renders a compact summary of each villager's known trades above their head. * - *

Trade data comes from {@link VillagerTradeCache}, which is populated when - * the mod opens a merchant screen. Villagers whose trades haven't been seen - * yet show nothing. + *

+ * Trade data comes from {@link VillagerTradeCache}, which is populated when the + * mod opens a merchant screen. Villagers whose trades haven't been seen yet + * show nothing. */ public final class VillagerTradeOverlayRenderer { - /** Vertical gap between successive trade lines (in world-space blocks). */ - private static final float LINE_SPACING = 0.25F; - /** World-space scale of the text (vanilla name-tags use ~0.025). */ private static final float TEXT_SCALE = 0.02F; @@ -46,10 +48,13 @@ public final class VillagerTradeOverlayRenderer { } public static void register() { - LevelRenderEvents.AFTER_SOLID_FEATURES.register(VillagerTradeOverlayRenderer::render); + //? if mc26 { + LevelRenderEvents.COLLECT_SUBMITS.register(VillagerTradeOverlayRenderer::renderLevel); + //?} } - private static void render(LevelRenderContext context) { + //? if mc26 { + private static void renderLevel(LevelRenderContext context) { Minecraft mc = Minecraft.getInstance(); if (mc.level == null || mc.player == null) { return; @@ -59,7 +64,6 @@ public final class VillagerTradeOverlayRenderer { } Font font = mc.font; - MultiBufferSource.BufferSource bufferSource = context.bufferSource(); Vec3 camera = mc.gameRenderer.getMainCamera().position(); float tickDelta = mc.getDeltaTracker().getGameTimeDeltaPartialTick(true); @@ -67,7 +71,6 @@ public final class VillagerTradeOverlayRenderer { if (!(entity instanceof Villager) && !(entity instanceof WanderingTrader)) { continue; } - // Only render for villagers within a reasonable distance. if (entity.distanceToSqr(mc.player) > 64.0 * 64.0) { continue; } @@ -77,29 +80,24 @@ public final class VillagerTradeOverlayRenderer { continue; } - // Build compact trade lines: "CostA [+ CostB] → Result (uses/max)" List lines = buildTradeLines(offers); if (lines.isEmpty()) { continue; } - // Interpolated entity position relative to camera. double x = Mth.lerp(tickDelta, entity.xOld, entity.getX()) - camera.x; double y = Mth.lerp(tickDelta, entity.yOld, entity.getY()) - camera.y; double z = Mth.lerp(tickDelta, entity.zOld, entity.getZ()) - camera.z; - // Place the first line above the entity's head (entity height + small gap). float baseY = entity.getBbHeight() + 0.6F; PoseStack poseStack = new PoseStack(); poseStack.pushPose(); poseStack.translate(x, y + baseY, z); - // Face the camera (billboard). poseStack.mulPose(mc.gameRenderer.getMainCamera().rotation()); poseStack.scale(-TEXT_SCALE, -TEXT_SCALE, TEXT_SCALE); - // Draw lines from top (highest index) to bottom (index 0). for (int i = 0; i < lines.size(); i++) { TradeLineEntry entry = lines.get(i); float lineOffsetY = -(lines.size() - 1 - i) * (font.lineHeight + 2); @@ -111,46 +109,38 @@ public final class VillagerTradeOverlayRenderer { int textWidth = font.width(entry.text); float textX = -textWidth / 2.0F; - // Background - font.drawInBatch(entry.text, textX, 0, entry.color, false, matrix, bufferSource, - Font.DisplayMode.SEE_THROUGH, BG_COLOR, 0xF000F0); - // Foreground - font.drawInBatch(entry.text, textX, 0, entry.color, false, matrix, bufferSource, - Font.DisplayMode.NORMAL, 0, 0xF000F0); + context.submitNodeCollector().submitText(poseStack, textX, 0, + net.minecraft.network.chat.Component.literal(entry.text).getVisualOrderText(), false, + Font.DisplayMode.NORMAL, entry.color, 0xF000F0, BG_COLOR, 0); } poseStack.popPose(); } } + //?} + private static List buildTradeLines(MerchantOffers offers) { List lines = new ArrayList<>(); for (int i = 0; i < offers.size(); i++) { MerchantOffer offer = offers.get(i); StringBuilder sb = new StringBuilder(); - // Cost A if (!offer.getCostA().isEmpty()) { - sb.append(offer.getCostA().getCount()).append("× ") - .append(offer.getCostA().getHoverName().getString()); + sb.append(offer.getCostA().getCount()).append("× ").append(offer.getCostA().getHoverName().getString()); } - // Cost B (optional) if (!offer.getCostB().isEmpty()) { if (sb.length() > 0) { sb.append(" + "); } - sb.append(offer.getCostB().getCount()).append("× ") - .append(offer.getCostB().getHoverName().getString()); + sb.append(offer.getCostB().getCount()).append("× ").append(offer.getCostB().getHoverName().getString()); } sb.append(" → "); - // Result - sb.append(offer.getResult().getCount()).append("× ") - .append(offer.getResult().getHoverName().getString()); + sb.append(offer.getResult().getCount()).append("× ").append(offer.getResult().getHoverName().getString()); - // Remaining uses int remaining = offer.getMaxUses() - offer.getUses(); sb.append(" (").append(remaining).append("/").append(offer.getMaxUses()).append(")"); diff --git a/src/main/resources/fabric.mod.json b/src/main/templates/fabric.mod.json similarity index 59% rename from src/main/resources/fabric.mod.json rename to src/main/templates/fabric.mod.json index 5751538..756c5e6 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/templates/fabric.mod.json @@ -1,17 +1,17 @@ { "schemaVersion": 1, - "id": "autotrade", - "name": "Auto Trade", + "id": "${mod_id}", + "name": "${mod_name}", "version": "${mod_version}", "description": "AFK trade with villagers", "authors": [ - "sebseb7" + "${mod_author}" ], "contact": { - "homepage": "https://modrinth.com/mod/autotrade-fabric", - "issues": "https://github.com/sebseb7/autotrade-fabric/issues", - "sources": "https://github.com/sebseb7/autotrade-fabric" + "homepage": "${mod_homepage}", + "issues": "${mod_issue_tracker}", + "sources": "${mod_sources}" }, "license": "0BSD", @@ -30,8 +30,8 @@ ], "depends": { - "minecraft": ">=${minecraft_version_min}", + "minecraft": "${minecraft_dependency}", "malilib": ">=${malilib_version}", - "fabric-api": ">=0.145.0" + "fabric-api": "${fabric_api_dependency}" } } diff --git a/stonecutter.gradle.kts b/stonecutter.gradle.kts new file mode 100644 index 0000000..2ca756b --- /dev/null +++ b/stonecutter.gradle.kts @@ -0,0 +1,10 @@ +plugins { + id("dev.kikugie.stonecutter") +} + +stonecutter active "26.1.2-fabric" + +stonecutter registerChiseled tasks.register("chiseledBuild", stonecutter.chiseled) { + group = "project" + ofTask("build") +} diff --git a/versions/1.21.10-fabric/gradle.properties b/versions/1.21.10-fabric/gradle.properties new file mode 100644 index 0000000..747077f --- /dev/null +++ b/versions/1.21.10-fabric/gradle.properties @@ -0,0 +1,8 @@ +modstitch.platform=fabric-loom-remap +deps.minecraft=1.21.10 + +fabric_loader_version=0.19.2 +fabric_api_version=0.138.4+1.21.10 +malilib_version=0.26.8 +mod_menu_version=16.0.1 +minecraft_version_min=1.21.10 diff --git a/versions/1.21.11-fabric/gradle.properties b/versions/1.21.11-fabric/gradle.properties new file mode 100644 index 0000000..613f2f5 --- /dev/null +++ b/versions/1.21.11-fabric/gradle.properties @@ -0,0 +1,8 @@ +modstitch.platform=fabric-loom-remap +deps.minecraft=1.21.11 + +fabric_loader_version=0.19.2 +fabric_api_version=0.136.0+1.21.11 +malilib_version=0.27.3 +mod_menu_version=17.0.0 +minecraft_version_min=1.21.11 diff --git a/versions/26.1.2-fabric/gradle.properties b/versions/26.1.2-fabric/gradle.properties new file mode 100644 index 0000000..ea07a35 --- /dev/null +++ b/versions/26.1.2-fabric/gradle.properties @@ -0,0 +1,8 @@ +modstitch.platform=fabric-loom +deps.minecraft=26.1.2 + +fabric_loader_version=0.19.2 +fabric_api_version=0.145.4+26.1.2 +malilib_version=0.28.2 +mod_menu_version=18.0.0-alpha.8 +minecraft_version_min=26.1.2