From 68c3206712e94da260190123ae29caaf6961f7ef Mon Sep 17 00:00:00 2001 From: 0xar-ds Date: Thu, 20 Aug 2026 23:50:44 +0200 Subject: [PATCH 01/25] Drop 1.21.11 & add 26.1.2 Signed-off-by: 0xar-ds --- settings.gradle.kts | 6 +++--- stonecutter.gradle.kts | 2 +- versions/1.21.11/gradle.properties | 14 -------------- 3 files changed, 4 insertions(+), 18 deletions(-) delete mode 100644 versions/1.21.11/gradle.properties diff --git a/settings.gradle.kts b/settings.gradle.kts index 9e298023ba..c987a57df4 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -13,9 +13,9 @@ plugins { stonecutter { kotlinController = true centralScript = "build.gradle.kts" - create(rootProject){ - versions("1.21.11") - vcsVersion = "1.21.11" + create(rootProject) { + versions("26.1.2") + vcsVersion = "26.1.2" } } diff --git a/stonecutter.gradle.kts b/stonecutter.gradle.kts index 43c2138a33..b575505a26 100644 --- a/stonecutter.gradle.kts +++ b/stonecutter.gradle.kts @@ -1,4 +1,4 @@ plugins { id("dev.kikugie.stonecutter") } -stonecutter active "1.21.11" \ No newline at end of file +stonecutter active "26.1.2" \ No newline at end of file diff --git a/versions/1.21.11/gradle.properties b/versions/1.21.11/gradle.properties deleted file mode 100644 index 14db0db685..0000000000 --- a/versions/1.21.11/gradle.properties +++ /dev/null @@ -1,14 +0,0 @@ -deps.core.mcVersion=1.21.11 -deps.core.maxMcVersion=1.21.11 - -deps.fabric_api=0.141.2+1.21.11 -deps.api.architectury=13.0.6 -deps.resourcefulconfig_version=1.21.11:3.11.3 -deps.modmenu_version=13.0.3 -deps.skyblock_api_version=4.1.10 -deps.skyblock_api_platform=1.21.11 -deps.skyblocker_version=6.0.0-beta.2+1.21.11 -deps.owo_version=0.13.0+1.21.11 -deps.hypixel_mod_api_version=1.0.1+build.1+mc1.21 - -loom.platform=fabric \ No newline at end of file From dcc13c7ddf96c5ec418a30af64f82622f0a23c3e Mon Sep 17 00:00:00 2001 From: 0xar-ds Date: Sat, 22 Aug 2026 14:30:28 +0200 Subject: [PATCH 02/25] Bump gradle ver Signed-off-by: 0xar-ds --- gradle/wrapper/gradle-wrapper.jar | Bin 45633 -> 47505 bytes gradle/wrapper/gradle-wrapper.properties | 4 ++- gradlew | 6 ++-- gradlew.bat | 35 ++++++++--------------- 4 files changed, 18 insertions(+), 27 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index f8e1ee3125fe0768e9a76ee977ac089eb657005e..eddabd2eef8d94a5437d6168ff9c87a78ff725b3 100644 GIT binary patch delta 39258 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%0D}_YK{Q?32MR^ESETLLJ>zQ@qcaOVwT<` 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 delta 37448 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 O00CYA5YPTOBmD&2 echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo location of your Java installation. 1>&2 -goto fail +"%COMSPEC%" /c exit 1 :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% @@ -65,29 +65,18 @@ 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 -goto fail +"%COMSPEC%" /c exit 1 :execute @rem Setup the command line -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* +@rem Execute gradlew +@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 -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 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! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +:exitWithErrorLevel +@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts +"%COMSPEC%" /c exit %ERRORLEVEL% From 0b45a084c9e1478c85f9ad0fd5cf012beb59b0e9 Mon Sep 17 00:00:00 2001 From: 0xar-ds Date: Sat, 22 Aug 2026 14:58:21 +0200 Subject: [PATCH 03/25] Bump kotlin ver Signed-off-by: 0xar-ds --- gradle.properties | 2 +- src/main/resources/fabric.mod.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index 0ceacdb256..ab5da6419c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,7 +4,7 @@ org.gradle.parallel=true org.gradle.caching=true org.gradle.caching.debug=false -fabric_kotlin_version=1.13.1+kotlin.2.1.10 +fabric_kotlin_version=1.13.10+kotlin.2.3.20 mod_version=1.0.0 # Allowed: stable, beta, alpha diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 37b0b6687e..9802f6450e 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -34,7 +34,7 @@ "java": ">=21", "fabric-api": "*", "resourcefulconfig": "*", - "fabric-language-kotlin": ">=1.13.4+kotlin.2.2.0" + "fabric-language-kotlin": ">=1.13.10+kotlin.2.3.20" }, "suggests": { "modmenu": ">=13.0.1" From 17200b3fa722ad499dcdb6121c2dc64d1978df31 Mon Sep 17 00:00:00 2001 From: 0xar-ds Date: Sat, 22 Aug 2026 15:01:19 +0200 Subject: [PATCH 04/25] Add 26.1.2 vers Signed-off-by: 0xar-ds --- versions/26.1.2/gradle.properties | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 versions/26.1.2/gradle.properties diff --git a/versions/26.1.2/gradle.properties b/versions/26.1.2/gradle.properties new file mode 100644 index 0000000000..de3df9cdd4 --- /dev/null +++ b/versions/26.1.2/gradle.properties @@ -0,0 +1,14 @@ +deps.core.mcVersion=26.1.2 +deps.core.maxMcVersion=26.1.2 + +deps.fabric_api=0.155.2+26.1.2 +deps.api.architectury=20.0.12 +deps.resourcefulconfig_version=26.1:4.0.1 +deps.modmenu_version=18.0.0 +deps.skyblock_api_version=4.2.22 +deps.skyblock_api_platform=26.1 +deps.skyblocker_version=6.5.3+26.1.2 +deps.owo_version=0.13.1+26.1 +deps.hypixel_mod_api_version=1.0.2+build.1+mc26.1 + +loom.platform=fabric \ No newline at end of file From 2423820f4ea33c5600df574748a625edfdd1491a Mon Sep 17 00:00:00 2001 From: 0xar-ds Date: Sat, 22 Aug 2026 15:00:54 +0200 Subject: [PATCH 05/25] Bump loom & update build fn calls per the new api Signed-off-by: 0xar-ds --- build.gradle.kts | 45 ++++++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index b64553041f..e249b163d0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("fabric-loom") version "1.15-SNAPSHOT" + id("net.fabricmc.fabric-loom") version "1.17.19" `maven-publish` java id("me.modmuss50.mod-publish-plugin") version "0.8.4" @@ -65,6 +65,7 @@ repositories { class ModDependencies { operator fun get(name: String) = property("deps.$name").toString() } + val deps = ModDependencies() val mcVersion = stonecutter.current.version val maxMcVersion = deps["core.maxMcVersion"] @@ -84,6 +85,7 @@ val owoLibVersion = deps["owo_version"] val resourcefulConfigVersion = deps["resourcefulconfig_version"] val autoUpdateVersion = deps["autoupdate_version"] val skyblockerVersion = deps["skyblocker_version"] + group = property("maven_group")!! val versionNumber = property("mod_version").toString().trim() val releaseChannel = property("mod_release_channel").toString().trim().ifEmpty { "stable" }.lowercase() @@ -104,26 +106,26 @@ val releaseLabel = "$versionNumber-$releaseChannel$prereleaseSuffix" version = "$releaseLabel+mc$mcVersion" dependencies { + // Mojang Minecraft dependency (no mappings required for 26.1+) minecraft("com.mojang:minecraft:${mcVersion}") - mappings(loom.officialMojangMappings()) - modImplementation("net.fabricmc:fabric-loader:${deps["fabricLoaderVersion"]}") - modImplementation("net.fabricmc.fabric-api:fabric-api:${deps["fabric_api"]}") + implementation("net.fabricmc:fabric-loader:${deps["fabricLoaderVersion"]}") + implementation("net.fabricmc.fabric-api:fabric-api:${deps["fabric_api"]}") - modLocalRuntime("maven.modrinth:hypixel-mod-api:$hypixelModApiVersion") - modRuntimeOnly("me.djtheredstoner:DevAuth-fabric:$devAuthVersion") + localRuntime("maven.modrinth:hypixel-mod-api:$hypixelModApiVersion") + runtimeOnly("me.djtheredstoner:DevAuth-fabric:$devAuthVersion") - modImplementation("tech.thatgravyboat:skyblock-api:${deps["skyblock_api_version"]}") { + implementation("tech.thatgravyboat:skyblock-api:${deps["skyblock_api_version"]}") { capabilities { requireCapability("tech.thatgravyboat:skyblock-api-${deps["skyblock_api_platform"]}") } } include("tech.thatgravyboat:skyblock-api:${deps["skyblock_api_version"]}") { - capabilities { requireCapability("tech.thatgravyboat:skyblock-api-${deps["skyblock_api_platform"]}-remapped") } + capabilities { requireCapability("tech.thatgravyboat:skyblock-api-${deps["skyblock_api_platform"]}") } } compileOnly("org.jetbrains.kotlin:kotlin-stdlib") - modImplementation("net.fabricmc:fabric-language-kotlin:$fabricKotlinVersion") + implementation("net.fabricmc:fabric-language-kotlin:$fabricKotlinVersion") - modImplementation("net.hypixel:hypixel-api-transport-apache:$hypixelApiVersion") + implementation("net.hypixel:hypixel-api-transport-apache:$hypixelApiVersion") include("net.hypixel:hypixel-api-transport-apache:$hypixelApiVersion") include("net.hypixel:hypixel-api-core:$hypixelApiVersion") @@ -133,28 +135,26 @@ dependencies { include("commons-logging:commons-logging:$commonsLoggingVersion") include("commons-codec:commons-codec:$commonsCodecVersion") - // Config lib and settings screen - modImplementation("com.teamresourceful.resourcefulconfig:resourcefulconfig-fabric-$resourcefulConfigVersion") - - modCompileOnly("com.terraformersmc:modmenu:$modMenuVersion") + implementation("com.teamresourceful.resourcefulconfig:resourcefulconfig-fabric-$resourcefulConfigVersion") + compileOnly("com.terraformersmc:modmenu:$modMenuVersion") // Project Lombok compileOnly("org.projectlombok:lombok:$lombokVersion") annotationProcessor("org.projectlombok:lombok:$lombokVersion") - testCompileOnly("org.projectlombok:lombok:$lombokVersion") testAnnotationProcessor("org.projectlombok:lombok:$lombokVersion") - // Mixin Constraints + include(implementation("com.moulberry:mixinconstraints:$mixinConstraintsVersion")!!) //gson extras for easy type adapters implementation("org.danilopianini:gson-extras:$gsonExtrasVersion") include("org.danilopianini:gson-extras:$gsonExtrasVersion") + // Skyblocker for compatibility - modCompileOnly("maven.modrinth:skyblocker-liap:v$skyblockerVersion") + compileOnly("maven.modrinth:skyblocker-liap:v$skyblockerVersion") // Owo Lib for lang features - modImplementation("io.wispforest:owo-lib:$owoLibVersion") + implementation("io.wispforest:owo-lib:$owoLibVersion") // Auto Update Library implementation("moe.nea:libautoupdate:$autoUpdateVersion") @@ -219,8 +219,8 @@ tasks { } loom { runConfigs.all { - ideConfigGenerated(true) // Run configurations are not created for subprojects by default - runDir = "../../run" // Use a shared run folder and create separate worlds + generateRunConfig.set(true) + runDirectory.set(layout.projectDirectory.dir("../../run")) } } java { @@ -228,9 +228,8 @@ java { } publishMods { - file = tasks.remapJar.get().archiveFile - additionalFiles.from(tasks.remapSourcesJar.get().archiveFile) - version = project.version.toString() + file.set(tasks.shadowJar.get().archiveFile) + version.set(project.version.toString()) type = when (releaseChannel) { "alpha" -> ALPHA From 88f991691d2058ce8a9df6cdd577a1fb0986fe7f Mon Sep 17 00:00:00 2001 From: 0xar-ds Date: Sat, 22 Aug 2026 15:01:49 +0200 Subject: [PATCH 06/25] Bump fabric loader ver Signed-off-by: 0xar-ds --- src/main/resources/fabric.mod.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 9802f6450e..b76c26c0ba 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -29,7 +29,7 @@ "bazaarutils.mixins.json" ], "depends": { - "fabricloader": ">=0.16.9", + "fabricloader": ">=0.19.3", "minecraft": ">=${mcVersion} <=${maxMcVersion}", "java": ">=21", "fabric-api": "*", From 3c07e03e9176183b5ce49de2416f4f25b04bb543 Mon Sep 17 00:00:00 2001 From: 0xar-ds Date: Sat, 22 Aug 2026 15:03:55 +0200 Subject: [PATCH 07/25] Specify target jvm/runtimes & reorder tasks declarations shadowJar was deprecated Signed-off-by: 0xar-ds --- build.gradle.kts | 86 +++++++++++++++++++----------- src/main/resources/fabric.mod.json | 2 +- 2 files changed, 56 insertions(+), 32 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index e249b163d0..5046c924db 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,9 +1,12 @@ +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + plugins { id("net.fabricmc.fabric-loom") version "1.17.19" `maven-publish` - java id("me.modmuss50.mod-publish-plugin") version "0.8.4" + id("org.jetbrains.kotlin.jvm") version "2.4.10" id("com.gradleup.shadow") version "9.2.2" + java } base { @@ -182,50 +185,71 @@ sourceSets { } } -tasks { - classes { - dependsOn(buildtimeInjectionTask) - } +tasks.withType().configureEach { + options.release = 25 +} - processResources { - inputs.property("version", project.version) - inputs.property("mcVersion", mcVersion) - inputs.property("minor_update_notes", rootProject.property("minor_update_notes") as String) - - filesMatching("fabric.mod.json") { - expand(mapOf( - "version" to project.version, - "mod_version" to rootProject.property("mod_version"), - "mcVersion" to mcVersion, - "maxMcVersion" to maxMcVersion, - "minor_update_notes" to rootProject.property("minor_update_notes") - )) - } +kotlin { + compilerOptions { + jvmTarget = JvmTarget.JVM_25 } +} - jar { - from("LICENSE"){ - rename { "${it}_${archiveBaseName.get()}" } - } +java { + withSourcesJar() + + sourceCompatibility = JavaVersion.VERSION_25 + targetCompatibility = JavaVersion.VERSION_25 + + toolchain { + languageVersion.set(JavaLanguageVersion.of(25)) } +} - shadowJar { - configurations = listOf(project.configurations.shadow.get()) +tasks.processResources { + inputs.property("version", project.version) + inputs.property("mcVersion", mcVersion) + inputs.property("minor_update_notes", rootProject.property("minor_update_notes") as String) + + filesMatching("fabric.mod.json") { + expand(mapOf( + "version" to project.version, + "mod_version" to rootProject.property("mod_version"), + "mcVersion" to mcVersion, + "maxMcVersion" to maxMcVersion, + "minor_update_notes" to rootProject.property("minor_update_notes") + )) } - remapJar { - dependsOn(shadowJar) - inputFile.set(shadowJar.get().archiveFile) +} + +tasks.classes { + dependsOn(buildtimeInjectionTask) +} + +tasks.jar { + enabled = false +} + +tasks.shadowJar { + archiveClassifier.set("") + + configurations = listOf(project.configurations.shadow.get()) + + from("LICENSE") { + rename { "${it}_${archiveBaseName.get()}" } } } + +tasks.assemble { + dependsOn(tasks.shadowJar) +} + loom { runConfigs.all { generateRunConfig.set(true) runDirectory.set(layout.projectDirectory.dir("../../run")) } } -java { - withSourcesJar() -} publishMods { file.set(tasks.shadowJar.get().archiveFile) diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index b76c26c0ba..1f0412ff13 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -31,7 +31,7 @@ "depends": { "fabricloader": ">=0.19.3", "minecraft": ">=${mcVersion} <=${maxMcVersion}", - "java": ">=21", + "java": ">=25", "fabric-api": "*", "resourcefulconfig": "*", "fabric-language-kotlin": ">=1.13.10+kotlin.2.3.20" From fce0391ec3a2679e2aa6dee5dbc42e8f25f22862 Mon Sep 17 00:00:00 2001 From: 0xar-ds Date: Sat, 22 Aug 2026 15:04:14 +0200 Subject: [PATCH 08/25] Bump mod publish plugin Signed-off-by: 0xar-ds --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 5046c924db..4ee2cf7f11 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -3,7 +3,7 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget plugins { id("net.fabricmc.fabric-loom") version "1.17.19" `maven-publish` - id("me.modmuss50.mod-publish-plugin") version "0.8.4" + id("me.modmuss50.mod-publish-plugin") version "2.0.0" id("org.jetbrains.kotlin.jvm") version "2.4.10" id("com.gradleup.shadow") version "9.2.2" java From 06c641ff882bcffad8ae90abe42dea0b0b247f0e Mon Sep 17 00:00:00 2001 From: 0xar-ds Date: Sun, 23 Aug 2026 18:47:39 +0200 Subject: [PATCH 09/25] Bump stonecutter Signed-off-by: 0xar-ds --- settings.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle.kts b/settings.gradle.kts index c987a57df4..66a3b09296 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -7,7 +7,7 @@ pluginManagement { } plugins { - id("dev.kikugie.stonecutter") version "0.8.2" + id("dev.kikugie.stonecutter") version "0.9.7" } stonecutter { From 23e28244a8f2c6aac0ecad136cc7a07935dde325 Mon Sep 17 00:00:00 2001 From: 0xar-ds Date: Sat, 22 Aug 2026 15:28:59 +0200 Subject: [PATCH 10/25] Bump loombok Signed-off-by: 0xar-ds --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index ab5da6419c..00afc04b67 100644 --- a/gradle.properties +++ b/gradle.properties @@ -20,7 +20,7 @@ deps.apache_httpclient_version=4.5.14 deps.apache_httpcore_version=4.4.16 deps.commons_logging_version=1.2 deps.commons_codec_version=1.16.0 -deps.lombok_version=1.18.36 +deps.lombok_version=1.18.40 deps.mixinconstraints_version=1.0.8 deps.gson_extras_version=3.3.0 deps.autoupdate_version=1.3.1 From a796695d166f8a1a906c077ae74e4f1985539752 Mon Sep 17 00:00:00 2001 From: 0xar-ds Date: Sun, 23 Aug 2026 18:58:22 +0200 Subject: [PATCH 11/25] Bump apache vers Signed-off-by: 0xar-ds --- gradle.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index 00afc04b67..634c76d1df 100644 --- a/gradle.properties +++ b/gradle.properties @@ -18,8 +18,8 @@ deps.devauth_version=1.2.1 deps.hypixel_api_version=4.4 deps.apache_httpclient_version=4.5.14 deps.apache_httpcore_version=4.4.16 -deps.commons_logging_version=1.2 -deps.commons_codec_version=1.16.0 +deps.commons_logging_version=1.4.0 +deps.commons_codec_version=1.22.1 deps.lombok_version=1.18.40 deps.mixinconstraints_version=1.0.8 deps.gson_extras_version=3.3.0 From 60f5808cbfac8cfc38856e5ad5952ce0645f2bc9 Mon Sep 17 00:00:00 2001 From: 0xar-ds Date: Sun, 23 Aug 2026 18:58:35 +0200 Subject: [PATCH 12/25] Bump asm vers for java 25 Signed-off-by: 0xar-ds --- buildSrc/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 4013da8972..7b4eca3b7b 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -8,6 +8,6 @@ repositories { dependencies { implementation(gradleApi()) - implementation("org.ow2.asm:asm:9.6") - implementation("org.ow2.asm:asm-tree:9.6") + implementation("org.ow2.asm:asm:9.10.1") + implementation("org.ow2.asm:asm-tree:9.10.1") } \ No newline at end of file From f11aff7bf5d1bddf14dbcbabcf883dec4e3d93f2 Mon Sep 17 00:00:00 2001 From: 0xar-ds Date: Sun, 23 Aug 2026 18:47:21 +0200 Subject: [PATCH 13/25] Handle migrations Signed-off-by: 0xar-ds --- .../bazaarutils/commands/BUCommand.java | 6 ++--- .../commands/BazaarUtilsCommand.java | 4 +-- .../ConvertNameDeveloperCommand.java | 4 +-- .../commands/orders/QueryOrderCommand.java | 4 +-- .../commands/orders/RemoveOrderCommand.java | 5 ++-- .../options/AbstractSelectorOverlay.java | 10 ++++---- .../options/types/ItemOptionWidget.java | 25 ++++++++++--------- .../options/types/selector/ContainerCell.java | 16 ++++++------ .../minecraft/SlotInteractionEvent.java | 12 ++++----- .../amount/BuyOrderAmountHelper.java | 4 ++- .../amount/InstantBuyAmountHelper.java | 4 ++- .../amount/SellOfferAmountHelper.java | 4 ++- .../price/BuyOrderPriceHelper.java | 4 ++- .../price/FlipOrderPriceHelper.java | 4 ++- .../price/SellOfferPriceHelper.java | 4 ++- .../features/util/BUKeybinding.java | 4 +-- .../bazaarutils/mixin/DrawContentMixin.java | 6 ++--- .../mixin/MixinAbstractContainerScreen.java | 8 +++--- .../bazaarutils/utils/PlayerActionUtil.java | 2 +- .../bazaar/market/order/OrderUpdater.java | 8 +++--- .../gui/container/ContainerManager.java | 4 +-- .../gui/widgets/ItemSlotButtonWidget.java | 10 ++++---- .../gui/widgets/TextDisplayWidget.java | 7 +++--- 23 files changed, 86 insertions(+), 73 deletions(-) diff --git a/src/main/java/com/github/mkram17/bazaarutils/commands/BUCommand.java b/src/main/java/com/github/mkram17/bazaarutils/commands/BUCommand.java index 37ddbadbaa..cdfccc4791 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/commands/BUCommand.java +++ b/src/main/java/com/github/mkram17/bazaarutils/commands/BUCommand.java @@ -3,7 +3,7 @@ import com.github.mkram17.bazaarutils.generated.BazaarUtilsCommands; import com.github.mkram17.bazaarutils.utils.annotations.modules.Command; import com.mojang.brigadier.builder.LiteralArgumentBuilder; -import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; +import net.fabricmc.fabric.api.client.command.v2.ClientCommands; import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; import net.minecraft.network.chat.Component; @@ -56,10 +56,10 @@ default List getAliases() { default List> getCommandBuilders() { List> builders = new ArrayList<>(); - builders.add(getCommandBuilder(ClientCommandManager.literal(getCommandName()))); + builders.add(getCommandBuilder(ClientCommands.literal(getCommandName()))); for (String alias : getAliases()) { - builders.add(getCommandBuilder(ClientCommandManager.literal(alias))); + builders.add(getCommandBuilder(ClientCommands.literal(alias))); } return builders; diff --git a/src/main/java/com/github/mkram17/bazaarutils/commands/BazaarUtilsCommand.java b/src/main/java/com/github/mkram17/bazaarutils/commands/BazaarUtilsCommand.java index 6308885e71..24124b8199 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/commands/BazaarUtilsCommand.java +++ b/src/main/java/com/github/mkram17/bazaarutils/commands/BazaarUtilsCommand.java @@ -5,7 +5,7 @@ import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import lombok.Getter; -import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; +import net.fabricmc.fabric.api.client.command.v2.ClientCommands; import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; @@ -30,7 +30,7 @@ public BazaarUtilsCommand() { private void registerWithCommands(CommandDispatcher dispatcher, List subcommands) { for (String prefix : PREFIXES) { - LiteralArgumentBuilder base = getCommandBuilder(ClientCommandManager.literal(prefix)); + LiteralArgumentBuilder base = getCommandBuilder(ClientCommands.literal(prefix)); subcommands.forEach(command -> command.getCommandBuilders().forEach(base::then)); diff --git a/src/main/java/com/github/mkram17/bazaarutils/commands/developer/ConvertNameDeveloperCommand.java b/src/main/java/com/github/mkram17/bazaarutils/commands/developer/ConvertNameDeveloperCommand.java index 035f4812c9..ed24e1950d 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/commands/developer/ConvertNameDeveloperCommand.java +++ b/src/main/java/com/github/mkram17/bazaarutils/commands/developer/ConvertNameDeveloperCommand.java @@ -9,7 +9,7 @@ import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.context.CommandContext; import lombok.Getter; -import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; +import net.fabricmc.fabric.api.client.command.v2.ClientCommands; import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; import net.minecraft.ChatFormatting; import net.minecraft.network.chat.Component; @@ -24,7 +24,7 @@ public final class ConvertNameDeveloperCommand implements BUCommand { @Override public LiteralArgumentBuilder getCommandBuilder(LiteralArgumentBuilder base) { - return base.then(ClientCommandManager.argument("item name", StringArgumentType.string()).executes(this::convertNameToId)); + return base.then(ClientCommands.argument("item name", StringArgumentType.string()).executes(this::convertNameToId)); } private int convertNameToId(CommandContext context) { diff --git a/src/main/java/com/github/mkram17/bazaarutils/commands/orders/QueryOrderCommand.java b/src/main/java/com/github/mkram17/bazaarutils/commands/orders/QueryOrderCommand.java index a92cb1f028..963e3f94fa 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/commands/orders/QueryOrderCommand.java +++ b/src/main/java/com/github/mkram17/bazaarutils/commands/orders/QueryOrderCommand.java @@ -9,7 +9,7 @@ import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.context.CommandContext; import lombok.Getter; -import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; +import net.fabricmc.fabric.api.client.command.v2.ClientCommands; import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; import net.minecraft.ChatFormatting; import net.minecraft.network.chat.Component; @@ -26,7 +26,7 @@ public final class QueryOrderCommand implements BUCommand { @Override public LiteralArgumentBuilder getCommandBuilder(LiteralArgumentBuilder base) { - return base.then(ClientCommandManager.argument("index", IntegerArgumentType.integer(0)).executes(this::queryByIndex)); + return base.then(ClientCommands.argument("index", IntegerArgumentType.integer(0)).executes(this::queryByIndex)); } private int queryByIndex(CommandContext context) { diff --git a/src/main/java/com/github/mkram17/bazaarutils/commands/orders/RemoveOrderCommand.java b/src/main/java/com/github/mkram17/bazaarutils/commands/orders/RemoveOrderCommand.java index 4af9120566..2803383a73 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/commands/orders/RemoveOrderCommand.java +++ b/src/main/java/com/github/mkram17/bazaarutils/commands/orders/RemoveOrderCommand.java @@ -2,7 +2,6 @@ import com.github.mkram17.bazaarutils.commands.BUCommand; import com.github.mkram17.bazaarutils.commands.OrdersCommands; -import com.github.mkram17.bazaarutils.data.stored.UserOrdersStorage; import com.github.mkram17.bazaarutils.misc.NotificationType; import com.github.mkram17.bazaarutils.utils.PlayerActionUtil; import com.github.mkram17.bazaarutils.utils.annotations.modules.Command; @@ -11,7 +10,7 @@ import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.context.CommandContext; import lombok.Getter; -import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; +import net.fabricmc.fabric.api.client.command.v2.ClientCommands; import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; import net.minecraft.ChatFormatting; import net.minecraft.network.chat.Component; @@ -28,7 +27,7 @@ public final class RemoveOrderCommand implements BUCommand { @Override public LiteralArgumentBuilder getCommandBuilder(LiteralArgumentBuilder base) { - return base.then(ClientCommandManager.argument("index", IntegerArgumentType.integer(0)).executes(this::removeByIndex)); + return base.then(ClientCommands.argument("index", IntegerArgumentType.integer(0)).executes(this::removeByIndex)); } private int removeByIndex(CommandContext context) { diff --git a/src/main/java/com/github/mkram17/bazaarutils/config/util/client/components/options/AbstractSelectorOverlay.java b/src/main/java/com/github/mkram17/bazaarutils/config/util/client/components/options/AbstractSelectorOverlay.java index 08139b8a46..31b9074be0 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/config/util/client/components/options/AbstractSelectorOverlay.java +++ b/src/main/java/com/github/mkram17/bazaarutils/config/util/client/components/options/AbstractSelectorOverlay.java @@ -3,9 +3,9 @@ import com.teamresourceful.resourcefulconfig.client.components.ModSprites; import com.teamresourceful.resourcefulconfig.client.screens.base.OverlayScreen; import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.renderer.RenderPipelines; import net.minecraft.client.input.MouseButtonEvent; -import net.minecraft.client.gui.GuiGraphics; import org.jetbrains.annotations.NotNull; public abstract class AbstractSelectorOverlay extends OverlayScreen { @@ -20,10 +20,10 @@ protected boolean isOverOverlay(double mouseX, double mouseY) { } @Override - public void renderBackground(@NotNull GuiGraphics context, int mouseX, int mouseY, float delta) { - super.renderBackground(context, mouseX, mouseY, delta); - context.blitSprite(RenderPipelines.GUI_TEXTURED, ModSprites.ACCENT, ox, oy, ow, oh); - context.blitSprite(RenderPipelines.GUI_TEXTURED, ModSprites.BUTTON, ox + 1, oy + 1, ow - 2, oh - 2); + public void extractBackground(@NotNull GuiGraphicsExtractor graphics, int mouseX, int mouseY, float delta) { + super.extractBackground(graphics, mouseX, mouseY, delta); + graphics.blitSprite(RenderPipelines.GUI_TEXTURED, ModSprites.ACCENT, ox, oy, ow, oh); + graphics.blitSprite(RenderPipelines.GUI_TEXTURED, ModSprites.BUTTON, ox + 1, oy + 1, ow - 2, oh - 2); } @Override diff --git a/src/main/java/com/github/mkram17/bazaarutils/config/util/client/components/options/types/ItemOptionWidget.java b/src/main/java/com/github/mkram17/bazaarutils/config/util/client/components/options/types/ItemOptionWidget.java index 4d19501fc9..9a7d43ced3 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/config/util/client/components/options/types/ItemOptionWidget.java +++ b/src/main/java/com/github/mkram17/bazaarutils/config/util/client/components/options/types/ItemOptionWidget.java @@ -9,8 +9,8 @@ import com.teamresourceful.resourcefulconfig.client.components.options.text.TextBox; import com.teamresourceful.resourcefulconfig.client.utils.ListenableState; import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.renderer.RenderPipelines; -import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.components.AbstractWidget; import net.minecraft.client.input.InputWithModifiers; import net.minecraft.client.input.CharacterEvent; @@ -18,6 +18,7 @@ import net.minecraft.network.chat.Component; import net.minecraft.util.Mth; import org.jetbrains.annotations.NotNull; +import org.jspecify.annotations.NonNull; import java.util.ArrayList; import java.util.List; @@ -39,13 +40,13 @@ public ItemOptionWidget(List items, Supplier getter, Consumer } @Override - protected void renderContents(GuiGraphics context, int mouseX, int mouseY, float delta) { - super.renderContents(context, mouseX, mouseY, delta); + protected void extractContents(@NonNull GuiGraphicsExtractor graphics, int mouseX, int mouseY, float delta) { + super.extractContents(graphics, mouseX, mouseY, delta); ItemStack stack = ItemsRepo.resolve(getter.get()); if (stack != null) { - context.renderItem(stack, getX(), getY()); + graphics.item(stack, getX(), getY()); } } @@ -167,10 +168,10 @@ protected void init() { this.searchBox = new TextBox(ow - PADDING * 2, SEARCH_HEIGHT, searchState) { @Override - public void renderWidget(GuiGraphics context, int mouseX, int mouseY, float delta) { - context.blitSprite(RenderPipelines.GUI_TEXTURED, ModSprites.BUTTON, getX(), getY(), getWidth(), getHeight()); - super.renderWidget(context, mouseX, mouseY, delta); - this.applyCursor(context); + public void extractWidgetRenderState(@NonNull GuiGraphicsExtractor graphics, int mouseX, int mouseY, float delta) { + graphics.blitSprite(RenderPipelines.GUI_TEXTURED, ModSprites.BUTTON, getX(), getY(), getWidth(), getHeight()); + super.extractWidgetRenderState(graphics, mouseX, mouseY, delta); + this.applyCursor(graphics); } }; this.searchBox.setPosition(ox + PADDING, oy + PADDING); @@ -181,16 +182,16 @@ public void renderWidget(GuiGraphics context, int mouseX, int mouseY, float delt } @Override - public void renderBackground(@NotNull GuiGraphics context, int mouseX, int mouseY, float delta) { - super.renderBackground(context, mouseX, mouseY, delta); + public void extractBackground(@NotNull GuiGraphicsExtractor graphics, int mouseX, int mouseY, float delta) { + super.extractBackground(graphics, mouseX, mouseY, delta); if (maxScroll() > 0) { int trackTop = oy + PADDING + SEARCH_HEIGHT + SPACING; int trackHeight = visibleRows() * ContainerCell.CELL_SIZE; int thumbHeight = Math.max(6, trackHeight * MAX_ROWS / totalRows()); int thumbTop = trackTop + (trackHeight - thumbHeight) * scrollOffset / Math.max(1, maxScroll()); - context.fill(ox + ow - 3, trackTop, ox + ow - 1, trackTop + trackHeight, 0x44FFFFFF); - context.fill(ox + ow - 3, thumbTop, ox + ow - 1, thumbTop + thumbHeight, 0xAAFFFFFF); + graphics.fill(ox + ow - 3, trackTop, ox + ow - 1, trackTop + trackHeight, 0x44FFFFFF); + graphics.fill(ox + ow - 3, thumbTop, ox + ow - 1, thumbTop + thumbHeight, 0xAAFFFFFF); } } diff --git a/src/main/java/com/github/mkram17/bazaarutils/config/util/client/components/options/types/selector/ContainerCell.java b/src/main/java/com/github/mkram17/bazaarutils/config/util/client/components/options/types/selector/ContainerCell.java index 868c481b0e..7b5baf2fac 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/config/util/client/components/options/types/selector/ContainerCell.java +++ b/src/main/java/com/github/mkram17/bazaarutils/config/util/client/components/options/types/selector/ContainerCell.java @@ -3,9 +3,9 @@ import com.github.mkram17.bazaarutils.utils.minecraft.components.CustomDataComponents; import com.teamresourceful.resourcefulconfig.client.components.base.BaseWidget; import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.renderer.RenderPipelines; import net.minecraft.client.input.MouseButtonEvent; -import net.minecraft.client.gui.GuiGraphics; import net.minecraft.core.component.DataComponents; import net.minecraft.world.item.component.TooltipDisplay; import net.minecraft.world.item.Item; @@ -34,20 +34,20 @@ public ContainerCell(int x, int y, ItemStack stack, boolean selected, Runnable o } @Override - protected void renderWidget(GuiGraphics context, int mouseX, int mouseY, float delta) { - context.blitSprite(RenderPipelines.GUI_TEXTURED, SLOT_SPRITE, getX(), getY(), CELL_SIZE, CELL_SIZE); + protected void extractWidgetRenderState(@NotNull GuiGraphicsExtractor graphics, int mouseX, int mouseY, float delta) { + graphics.blitSprite(RenderPipelines.GUI_TEXTURED, SLOT_SPRITE, getX(), getY(), CELL_SIZE, CELL_SIZE); if (selected) { - context.fill(getX() + 1, getY() + 1, getX() + CELL_SIZE - 1, getY() + CELL_SIZE - 1, 0x8800AA00); + graphics.fill(getX() + 1, getY() + 1, getX() + CELL_SIZE - 1, getY() + CELL_SIZE - 1, 0x8800AA00); } else if (isHovered()) { - context.fill(getX() + 1, getY() + 1, getX() + CELL_SIZE - 1, getY() + CELL_SIZE - 1, 0x80FFFFFF); + graphics.fill(getX() + 1, getY() + 1, getX() + CELL_SIZE - 1, getY() + CELL_SIZE - 1, 0x80FFFFFF); } if (!stack.isEmpty()) { - context.renderItem(stack, getX() + 1, getY() + 1); + graphics.item(stack, getX() + 1, getY() + 1); if (isHovered() && !stack.getOrDefault(DataComponents.TOOLTIP_DISPLAY, TooltipDisplay.DEFAULT).hideTooltip()) { - context.setComponentTooltipForNextFrame( + graphics.setComponentTooltipForNextFrame( Minecraft.getInstance().font, stack.getTooltipLines(Item.TooltipContext.EMPTY, null, TooltipFlag.NORMAL), mouseX, mouseY @@ -55,7 +55,7 @@ protected void renderWidget(GuiGraphics context, int mouseX, int mouseY, float d } } - this.applyCursor(context); + this.applyCursor(graphics); } @Override diff --git a/src/main/java/com/github/mkram17/bazaarutils/events/minecraft/SlotInteractionEvent.java b/src/main/java/com/github/mkram17/bazaarutils/events/minecraft/SlotInteractionEvent.java index 1aec256893..74135c0504 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/events/minecraft/SlotInteractionEvent.java +++ b/src/main/java/com/github/mkram17/bazaarutils/events/minecraft/SlotInteractionEvent.java @@ -3,7 +3,7 @@ import lombok.Getter; import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; import net.minecraft.world.entity.player.Inventory; -import net.minecraft.world.inventory.ClickType; +import net.minecraft.world.inventory.ContainerInput; import net.minecraft.world.inventory.Slot; import org.jetbrains.annotations.NotNull; import tech.thatgravyboat.skyblockapi.api.events.base.CancellableSkyBlockEvent; @@ -26,7 +26,7 @@ *

* * @see Slot - * @see ClickType + * @see ContainerInput */ @Getter public class SlotInteractionEvent extends CancellableSkyBlockEvent { @@ -48,7 +48,7 @@ public class SlotInteractionEvent extends CancellableSkyBlockEvent { private final int slotId; /** - * The mouse button, or — for {@link ClickType#SWAP} — the target hotbar index. + * The mouse button, or — for {@link ContainerInput#SWAP} — the target hotbar index. */ private final int button; @@ -56,7 +56,7 @@ public class SlotInteractionEvent extends CancellableSkyBlockEvent { * The type of interaction vanilla resolved this click to. */ @NotNull - private final ClickType clickType; + private final ContainerInput actionType; /** * Whether the interacted slot belongs to the player's inventory rather than the container. @@ -70,12 +70,12 @@ public SlotInteractionEvent( @NotNull Slot slot, int slotId, int button, - @NotNull ClickType clickType) { + @NotNull ContainerInput actionType) { this.screen = screen; this.slot = slot; this.slotId = slotId; this.button = button; - this.clickType = clickType; + this.actionType = actionType; this.inPlayerInventory = slot.container instanceof Inventory; } } diff --git a/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/BuyOrderAmountHelper.java b/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/BuyOrderAmountHelper.java index 076882ee20..9c3df99c9c 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/BuyOrderAmountHelper.java +++ b/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/BuyOrderAmountHelper.java @@ -115,7 +115,9 @@ public Component getTitle(int index) { @Override public Component getDescription(int index) { - return Component.literal("Slot " + slotIndex + " · " + resolveStack().getItem().getName().getString()); + var stack = resolveStack(); + + return Component.literal("Slot " + slotIndex + " · " + stack.getItem().getName(stack).getString()); } private static int getNextSlotIndex() { diff --git a/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/InstantBuyAmountHelper.java b/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/InstantBuyAmountHelper.java index 2e63e6ef5c..1738dd2485 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/InstantBuyAmountHelper.java +++ b/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/InstantBuyAmountHelper.java @@ -115,7 +115,9 @@ public Component getTitle(int index) { @Override public Component getDescription(int index) { - return Component.literal("Slot " + slotIndex + " · " + resolveStack().getItem().getName().getString()); + var stack = resolveStack(); + + return Component.literal("Slot " + slotIndex + " · " + stack.getItem().getName(stack).getString()); } private static int getNextSlotIndex() { diff --git a/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/SellOfferAmountHelper.java b/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/SellOfferAmountHelper.java index 9d56d4e53e..21da5a9fbb 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/SellOfferAmountHelper.java +++ b/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/SellOfferAmountHelper.java @@ -115,7 +115,9 @@ public Component getTitle(int index) { @Override public Component getDescription(int index) { - return Component.literal("Slot " + slotIndex + " · " + resolveStack().getItem().getName().getString()); + var stack = resolveStack(); + + return Component.literal("Slot " + slotIndex + " · " + stack.getItem().getName(stack).getString()); } private static int getNextSlotIndex() { diff --git a/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/price/BuyOrderPriceHelper.java b/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/price/BuyOrderPriceHelper.java index 61bce73fdd..1dac3e8ae0 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/price/BuyOrderPriceHelper.java +++ b/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/price/BuyOrderPriceHelper.java @@ -104,7 +104,9 @@ public Component getTitle(int index) { @Override public Component getDescription(int index) { - return Component.literal("Slot " + slotIndex + " · " + resolveStack().getItem().getName().getString()); + var stack = resolveStack(); + + return Component.literal("Slot " + slotIndex + " · " + stack.getItem().getName(stack).getString()); } private static int getNextSlotIndex() { diff --git a/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/price/FlipOrderPriceHelper.java b/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/price/FlipOrderPriceHelper.java index 699eb30575..ab0ca1c78f 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/price/FlipOrderPriceHelper.java +++ b/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/price/FlipOrderPriceHelper.java @@ -104,7 +104,9 @@ public Component getTitle(int index) { @Override public Component getDescription(int index) { - return Component.literal("Slot " + slotIndex + " · " + resolveStack().getItem().getName().getString()); + var stack = resolveStack(); + + return Component.literal("Slot " + slotIndex + " · " + stack.getItem().getName(stack).getString()); } private static int getNextSlotIndex() { diff --git a/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/price/SellOfferPriceHelper.java b/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/price/SellOfferPriceHelper.java index acda7d801e..03fc8831e2 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/price/SellOfferPriceHelper.java +++ b/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/price/SellOfferPriceHelper.java @@ -104,7 +104,9 @@ public Component getTitle(int index) { @Override public Component getDescription(int index) { - return Component.literal("Slot " + slotIndex + " · " + resolveStack().getItem().getName().getString()); + var stack = resolveStack(); + + return Component.literal("Slot " + slotIndex + " · " + stack.getItem().getName(stack).getString()); } private static int getNextSlotIndex() { diff --git a/src/main/java/com/github/mkram17/bazaarutils/features/util/BUKeybinding.java b/src/main/java/com/github/mkram17/bazaarutils/features/util/BUKeybinding.java index b66ce107fd..0b6aede68c 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/features/util/BUKeybinding.java +++ b/src/main/java/com/github/mkram17/bazaarutils/features/util/BUKeybinding.java @@ -1,6 +1,6 @@ package com.github.mkram17.bazaarutils.features.util; -import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; +import net.fabricmc.fabric.api.client.keymapping.v1.KeyMappingHelper; import net.minecraft.client.KeyMapping; public abstract class BUKeybinding { @@ -13,7 +13,7 @@ public BUKeybinding(KeyMapping keyBinding) { } private static void registerKeybinding(KeyMapping keyBinding){ - KeyBindingHelper.registerKeyBinding(keyBinding); + KeyMappingHelper.registerKeyMapping(keyBinding); } protected void registerOnPressed(){} diff --git a/src/main/java/com/github/mkram17/bazaarutils/mixin/DrawContentMixin.java b/src/main/java/com/github/mkram17/bazaarutils/mixin/DrawContentMixin.java index 6903dc392b..190c943d5d 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/mixin/DrawContentMixin.java +++ b/src/main/java/com/github/mkram17/bazaarutils/mixin/DrawContentMixin.java @@ -2,7 +2,7 @@ import com.github.mkram17.bazaarutils.utils.minecraft.components.CustomDataComponents; import net.minecraft.client.gui.Font; -import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.world.item.ItemStack; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; @@ -10,10 +10,10 @@ import tech.thatgravyboat.skyblockapi.api.item.VisualItemAccessorKt; //used to change stack size String -@Mixin(GuiGraphics.class) +@Mixin(GuiGraphicsExtractor.class) public abstract class DrawContentMixin { @ModifyVariable( - method = "renderItemCount", + method = "itemCount", at = @At("HEAD"), ordinal = 0, argsOnly = true) diff --git a/src/main/java/com/github/mkram17/bazaarutils/mixin/MixinAbstractContainerScreen.java b/src/main/java/com/github/mkram17/bazaarutils/mixin/MixinAbstractContainerScreen.java index 864cae54da..62066737b8 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/mixin/MixinAbstractContainerScreen.java +++ b/src/main/java/com/github/mkram17/bazaarutils/mixin/MixinAbstractContainerScreen.java @@ -5,7 +5,7 @@ import com.github.mkram17.bazaarutils.events.minecraft.SlotInteractionEvent; import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; -import net.minecraft.world.inventory.ClickType; +import net.minecraft.world.inventory.ContainerInput; import net.minecraft.world.inventory.Slot; import net.minecraft.network.chat.Component; import org.spongepowered.asm.mixin.Mixin; @@ -25,12 +25,12 @@ protected MixinAbstractContainerScreen(Component title) { // through — mouse clicks, number-key hotbar swaps, the drop key, double-click — so posting the // cancellable SlotInteractionEvent here keeps the insta-sell / sell-sacks safety gate covering // keyboard-driven sells, which the mouse-only event silently missed. - @Inject(method = "slotClicked(Lnet/minecraft/world/inventory/Slot;IILnet/minecraft/world/inventory/ClickType;)V", at = @At("HEAD"), cancellable = true) - private void onSlotClicked_RestrictionGate(Slot slot, int slotId, int button, ClickType clickType, CallbackInfo ci) { + @Inject(method = "slotClicked(Lnet/minecraft/world/inventory/Slot;IILnet/minecraft/world/inventory/ContainerInput;)V", at = @At("HEAD"), cancellable = true) + private void onSlotClicked_RestrictionGate(Slot slot, int slotId, int button, ContainerInput actionType, CallbackInfo ci) { if (slot == null) return; AbstractContainerScreen screen = (AbstractContainerScreen) (Object) this; - if (new SlotInteractionEvent(screen, slot, slotId, button, clickType).post(BazaarUtils.EVENT_BUS)) { + if (new SlotInteractionEvent(screen, slot, slotId, button, actionType).post(BazaarUtils.EVENT_BUS)) { ci.cancel(); } } diff --git a/src/main/java/com/github/mkram17/bazaarutils/utils/PlayerActionUtil.java b/src/main/java/com/github/mkram17/bazaarutils/utils/PlayerActionUtil.java index cb409de23b..dfed4bbf2d 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/utils/PlayerActionUtil.java +++ b/src/main/java/com/github/mkram17/bazaarutils/utils/PlayerActionUtil.java @@ -20,7 +20,7 @@ public static void runCommand(String command){ static void sendPlayerMessage(Component message){ if (Minecraft.getInstance().player != null) { - Minecraft.getInstance().player.displayClientMessage(message, false); + Minecraft.getInstance().player.sendSystemMessage(message); } else { Util.logMessage("Could not send notification because player is null. Message: " + message); Util.tickExecuteLater(100, () -> sendPlayerMessage(message)); diff --git a/src/main/java/com/github/mkram17/bazaarutils/utils/bazaar/market/order/OrderUpdater.java b/src/main/java/com/github/mkram17/bazaarutils/utils/bazaar/market/order/OrderUpdater.java index 0453bb6966..cbbf19cdae 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/utils/bazaar/market/order/OrderUpdater.java +++ b/src/main/java/com/github/mkram17/bazaarutils/utils/bazaar/market/order/OrderUpdater.java @@ -98,13 +98,13 @@ private static void updateBazaarOrder(Order order, ItemInfo parsedItemInfo) { order.setItemInfo(parsedItemInfo); - Optional loreComponent = order.getItemInfo().itemStack().getComponentsPatch().get(DataComponents.LORE); + ItemLore loreComponent = order.getItemInfo().itemStack().get(DataComponents.LORE); - if (loreComponent == null || loreComponent.isEmpty()) { + if (loreComponent == null) { return; } - List loreLines = loreComponent.get().styledLines(); + List loreLines = loreComponent.styledLines(); int amountFilled = parseAmountFilled(loreLines); int amountClaimed = parseAmountClaimed(loreLines, amountFilled); @@ -130,7 +130,7 @@ private static void updateBazaarOrder(Order order, ItemInfo parsedItemInfo) { private static OrderInfo parseOrderFromItemStack(ItemStack stack) { String title = stack.getHoverName().getString(); - Optional loreComponent = stack.getComponentsPatch().get(DataComponents.LORE); + Optional loreComponent = Optional.ofNullable(stack.get(DataComponents.LORE)); ItemInfo itemInfo = new ItemInfo(mapScreenIndexToInventoryIndex(stack), stack); diff --git a/src/main/java/com/github/mkram17/bazaarutils/utils/minecraft/gui/container/ContainerManager.java b/src/main/java/com/github/mkram17/bazaarutils/utils/minecraft/gui/container/ContainerManager.java index 1af0aa5bab..ffda5bbae7 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/utils/minecraft/gui/container/ContainerManager.java +++ b/src/main/java/com/github/mkram17/bazaarutils/utils/minecraft/gui/container/ContainerManager.java @@ -13,10 +13,10 @@ import net.minecraft.network.chat.Component; import net.minecraft.world.Container; import net.minecraft.world.inventory.ChestMenu; +import net.minecraft.world.inventory.ContainerInput; import net.minecraft.world.inventory.Slot; import net.minecraft.world.item.ItemStack; import net.minecraft.world.inventory.AbstractContainerMenu; -import net.minecraft.world.inventory.ClickType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -98,7 +98,7 @@ public static void clickSlot(int slotIndex, int button) { int syncId = menu.get().containerId; Util.tickExecuteLater(1, () -> - interactionManager.handleInventoryMouseClick(syncId, slotIndex, button, ClickType.PICKUP, player) + interactionManager.handleContainerInput(syncId, slotIndex, button, ContainerInput.PICKUP, player) ); } } \ No newline at end of file diff --git a/src/main/java/com/github/mkram17/bazaarutils/utils/minecraft/gui/widgets/ItemSlotButtonWidget.java b/src/main/java/com/github/mkram17/bazaarutils/utils/minecraft/gui/widgets/ItemSlotButtonWidget.java index 5b92329b9e..c63d4fe52c 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/utils/minecraft/gui/widgets/ItemSlotButtonWidget.java +++ b/src/main/java/com/github/mkram17/bazaarutils/utils/minecraft/gui/widgets/ItemSlotButtonWidget.java @@ -1,12 +1,12 @@ package com.github.mkram17.bazaarutils.utils.minecraft.gui.widgets; -import net.minecraft.client.gui.GuiGraphics; -import net.minecraft.client.gui.components.Button.OnPress; +import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.components.WidgetSprites; import net.minecraft.client.gui.components.Tooltip; import net.minecraft.client.gui.components.ImageButton; import net.minecraft.world.item.ItemStack; import net.minecraft.network.chat.MutableComponent; +import org.jspecify.annotations.NonNull; public class ItemSlotButtonWidget extends ImageButton { private final ItemStack itemStack; @@ -18,14 +18,14 @@ public ItemSlotButtonWidget(int x, int y, int width, int height, WidgetSprites t } @Override - public void renderContents(GuiGraphics context, int mouseX, int mouseY, float delta) { - super.renderContents(context, mouseX, mouseY, delta); + public void extractContents(@NonNull GuiGraphicsExtractor graphics, int mouseX, int mouseY, float delta) { + super.extractContents(graphics, mouseX, mouseY, delta); if (this.itemStack != null && !this.itemStack.isEmpty()) { int itemX = this.getX() + (this.width - 16) / 2; int itemY = this.getY() + (this.height - 16) / 2; - context.renderItem(this.itemStack, itemX, itemY); + graphics.item(this.itemStack, itemX, itemY); } } } \ No newline at end of file diff --git a/src/main/java/com/github/mkram17/bazaarutils/utils/minecraft/gui/widgets/TextDisplayWidget.java b/src/main/java/com/github/mkram17/bazaarutils/utils/minecraft/gui/widgets/TextDisplayWidget.java index a24505aa87..de4442fc19 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/utils/minecraft/gui/widgets/TextDisplayWidget.java +++ b/src/main/java/com/github/mkram17/bazaarutils/utils/minecraft/gui/widgets/TextDisplayWidget.java @@ -2,10 +2,11 @@ import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; -import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.narration.NarrationElementOutput; import net.minecraft.client.gui.components.AbstractWidget; import net.minecraft.network.chat.Component; +import org.jspecify.annotations.NonNull; public class TextDisplayWidget extends AbstractWidget { public enum Alignment { @@ -28,7 +29,7 @@ public TextDisplayWidget(int x, int y, int width, int height, Component text) { } @Override - public void renderWidget(GuiGraphics context, int mouseX, int mouseY, float delta) { + public void extractWidgetRenderState(@NonNull GuiGraphicsExtractor graphics, int mouseX, int mouseY, float delta) { Font textRenderer = Minecraft.getInstance().font; int textY = this.getY() + (this.height - textRenderer.lineHeight) / 2; @@ -38,7 +39,7 @@ public void renderWidget(GuiGraphics context, int mouseX, int mouseY, float delt case RIGHT -> this.getX() + this.width - textRenderer.width(text); }; - context.drawString(textRenderer, text, textX, textY, 0xFFFFFFFF, false); + graphics.text(textRenderer, text, textX, textY, 0xFFFFFFFF, false); } @Override From 33d9caf1b3cb1b878750e8c0d298af16fcccd865 Mon Sep 17 00:00:00 2001 From: 0xar-ds Date: Sun, 23 Aug 2026 19:29:46 +0200 Subject: [PATCH 14/25] Yield onto ItemStackTemplates Signed-off-by: 0xar-ds --- .../mkram17/bazaarutils/BazaarUtils.java | 32 ++++++----- .../config/util/client/ItemRenderer.java | 5 +- .../options/types/ItemOptionWidget.java | 14 ++++- .../options/types/ItemStringOptionWidget.java | 20 +++++-- .../gui/buttons/CancelOrderAndSearch.java | 5 +- .../utils/minecraft/item/ItemButton.java | 26 +++++---- .../utils/minecraft/item/ItemRef.java | 13 +++-- .../utils/minecraft/item/ItemsRepo.java | 56 ++++++++++++++++--- .../minecraft/item/groups/ItemGroups.java | 46 +++++++-------- .../minecraft/item/groups/StateItem.java | 8 +-- .../minecraft/item/groups/StateItemGroup.java | 17 ++++-- 11 files changed, 164 insertions(+), 78 deletions(-) diff --git a/src/main/java/com/github/mkram17/bazaarutils/BazaarUtils.java b/src/main/java/com/github/mkram17/bazaarutils/BazaarUtils.java index a4ec388ced..e9546df477 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/BazaarUtils.java +++ b/src/main/java/com/github/mkram17/bazaarutils/BazaarUtils.java @@ -3,7 +3,7 @@ import com.github.mkram17.bazaarutils.config.util.ConfigUtil; import com.github.mkram17.bazaarutils.generated.*; import com.github.mkram17.bazaarutils.misc.BUCompatibilityHelper; -import com.github.mkram17.bazaarutils.utils.bazaar.market.order.OrderUtil; +import com.github.mkram17.bazaarutils.utils.Util; import com.github.mkram17.bazaarutils.utils.minecraft.item.ItemsRepo; import com.github.mkram17.bazaarutils.utils.update.UpdateUtil; import com.teamresourceful.resourcefulconfig.api.loader.Configurator; @@ -63,11 +63,15 @@ public void onInitializeClient() { // therefore subscribes them to the event bus already — no explicit subscription pass needed. if (RepoAPI.isInitialized()) { - onRepoReady(); + lateInitialize(); } } - private static final AtomicBoolean repoReady = new AtomicBoolean(false); + private static final AtomicBoolean lateInitDone = new AtomicBoolean(false); + + public static boolean isLateInitDone() { + return lateInitDone.get(); + } @Subscription(event = RepoStatusEvent.class) public void onRepoStatus(RepoStatusEvent event) { @@ -75,19 +79,19 @@ public void onRepoStatus(RepoStatusEvent event) { LOGGER.warn("SkyblockAPI repo did not load successfully (status: {}); continuing with late init anyway.", event.getStatus()); } - onRepoReady(); + lateInitialize(); } - private void onRepoReady() { - if (!repoReady.compareAndSet(false, true)) return; + private void lateInitialize() { + if (lateInitDone.compareAndSet(false, true)) { + // The repo-status event is posted from repolib's async worker thread; marshal onto the + // client thread before touching the (unsynchronized) event bus and command registration. + Minecraft.getInstance().execute(() -> { + BazaarUtilsLateInitModules.init(); - // The repo-status event is posted from repolib's async worker thread; marshal onto the - // client thread before touching the (unsynchronized) event bus and command registration. - Minecraft.getInstance().execute(() -> { - ItemsRepo.buildSkyBlockItemsCache(); - - BazaarUtilsLateInitModules.init(); - UpdateUtil.checkForUpdates(); - }); + UpdateUtil.checkForUpdates(); + Util.tickExecuteLater(1, ItemsRepo::buildSkyBlockItemsCache); + }); + } } } \ No newline at end of file diff --git a/src/main/java/com/github/mkram17/bazaarutils/config/util/client/ItemRenderer.java b/src/main/java/com/github/mkram17/bazaarutils/config/util/client/ItemRenderer.java index 8062b4f34b..26e0888d2f 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/config/util/client/ItemRenderer.java +++ b/src/main/java/com/github/mkram17/bazaarutils/config/util/client/ItemRenderer.java @@ -12,6 +12,8 @@ import net.minecraft.world.item.ItemStack; import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; public record ItemRenderer(ItemElement element) implements ResourcefulConfigElementRenderer { @Override @@ -29,6 +31,7 @@ public List widgets() { ResourcefulConfigValueEntry entry = element.valueEntry(); List items = ItemsRepo.getItems(element.tag()); + Set allowedIds = items.stream().map(ItemsRepo::identify).collect(Collectors.toUnmodifiableSet()); ItemOptionWidget itemWidget = new ItemOptionWidget(items, entry::getString, entry::setString); @@ -39,7 +42,7 @@ public List widgets() { if (resolved == null) return false; String resolvedId = ItemsRepo.identify(resolved); - if (items.stream().noneMatch(stack -> ItemsRepo.identify(stack).equals(resolvedId))) return false; + if (!allowedIds.contains(resolvedId)) return false; entry.setString(s); diff --git a/src/main/java/com/github/mkram17/bazaarutils/config/util/client/components/options/types/ItemOptionWidget.java b/src/main/java/com/github/mkram17/bazaarutils/config/util/client/components/options/types/ItemOptionWidget.java index 9a7d43ced3..ce0f548f4e 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/config/util/client/components/options/types/ItemOptionWidget.java +++ b/src/main/java/com/github/mkram17/bazaarutils/config/util/client/components/options/types/ItemOptionWidget.java @@ -22,6 +22,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.function.Consumer; import java.util.function.Supplier; @@ -32,6 +33,9 @@ public class ItemOptionWidget extends SelectorOptionWidget { private final Supplier getter; private final Consumer setter; + private String lastResolvedId; + private ItemStack lastResolvedStack; + public ItemOptionWidget(List items, Supplier getter, Consumer setter) { super(ModSprites.BUTTON, SELECT); this.items = items; @@ -43,10 +47,14 @@ public ItemOptionWidget(List items, Supplier getter, Consumer protected void extractContents(@NonNull GuiGraphicsExtractor graphics, int mouseX, int mouseY, float delta) { super.extractContents(graphics, mouseX, mouseY, delta); - ItemStack stack = ItemsRepo.resolve(getter.get()); + String id = getter.get(); + if (!Objects.equals(id, lastResolvedId)) { + lastResolvedId = id; + lastResolvedStack = ItemsRepo.resolve(id); + } - if (stack != null) { - graphics.item(stack, getX(), getY()); + if (lastResolvedStack != null) { + graphics.item(lastResolvedStack, getX(), getY()); } } diff --git a/src/main/java/com/github/mkram17/bazaarutils/config/util/client/components/options/types/ItemStringOptionWidget.java b/src/main/java/com/github/mkram17/bazaarutils/config/util/client/components/options/types/ItemStringOptionWidget.java index 9fff1bd943..026b97f357 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/config/util/client/components/options/types/ItemStringOptionWidget.java +++ b/src/main/java/com/github/mkram17/bazaarutils/config/util/client/components/options/types/ItemStringOptionWidget.java @@ -6,12 +6,16 @@ import net.minecraft.ChatFormatting; import net.minecraft.world.item.ItemStack; +import java.util.Objects; import java.util.function.Function; import java.util.function.Supplier; public class ItemStringOptionWidget extends StringOptionWidget implements ResetableWidget { private final Supplier getter; + private String lastResolvedId; + private String lastResolvedName; + public ItemStringOptionWidget(Supplier getter, Function setter) { super(getter, setter, false); this.getter = getter; @@ -20,12 +24,18 @@ public ItemStringOptionWidget(Supplier getter, Function @Override public void updateIfFocused() { if (!isFocused()) { - ItemStack stack = ItemsRepo.resolve(getter.get()); - String name = stack != null - ? ChatFormatting.stripFormatting(stack.getHoverName().getString()) - : getter.get(); + String id = getter.get(); + + if (!Objects.equals(id, lastResolvedId)) { + lastResolvedId = id; + + ItemStack stack = ItemsRepo.resolve(id); + lastResolvedName = stack != null + ? ChatFormatting.stripFormatting(stack.getHoverName().getString()) + : id; + } - setValue(name); + setValue(lastResolvedName); setCursorPosition(0); setHighlightPos(0); setTextColor(0xFFE0E0E0); diff --git a/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/CancelOrderAndSearch.java b/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/CancelOrderAndSearch.java index fe3e5b0a42..88c15ab337 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/CancelOrderAndSearch.java +++ b/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/CancelOrderAndSearch.java @@ -8,6 +8,7 @@ import com.teamresourceful.resourcefulconfig.api.annotations.ConfigObject; import lombok.Getter; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.ItemStackTemplate; import net.minecraft.world.item.Items; @ConfigObject @@ -17,8 +18,10 @@ public int getSlotIndex() { return ButtonsConfig.CANCEL_ORDER_AND_SEARCH.slotIndex; } + private static final ItemStackTemplate ITEM_TEPLATE = new ItemStackTemplate(Items.BLUE_TERRACOTTA); + public ItemRef getItemRef() { - return ItemRef.of(Items.BLUE_TERRACOTTA); + return ItemRef.of(ITEM_TEPLATE); } @Getter diff --git a/src/main/java/com/github/mkram17/bazaarutils/utils/minecraft/item/ItemButton.java b/src/main/java/com/github/mkram17/bazaarutils/utils/minecraft/item/ItemButton.java index 96a4a196c3..2eecf46719 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/utils/minecraft/item/ItemButton.java +++ b/src/main/java/com/github/mkram17/bazaarutils/utils/minecraft/item/ItemButton.java @@ -11,6 +11,7 @@ import net.minecraft.world.inventory.Slot; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.ItemStackTemplate; import net.minecraft.world.item.Items; import net.minecraft.core.Holder; import net.minecraft.sounds.SoundEvent; @@ -22,6 +23,7 @@ public interface ItemButton extends AbstractItemModifier { Item DEFAULT_ITEM = Items.BARRIER; + ItemStackTemplate DEFAULT_TEMPLATE = new ItemStackTemplate(DEFAULT_ITEM); float BUTTON_VOLUME = 0.2f; Holder BUTTON_SOUND = SoundEvents.UI_BUTTON_CLICK; @@ -84,32 +86,36 @@ default void retriggerModifier() { } default ItemStack resolveStack() { + return resolveTemplate().create(); + } + + default ItemStackTemplate resolveTemplate() { return switch (getItemRef()) { - case ItemRef.Direct(Item item) -> new ItemStack(item); + case ItemRef.Direct(ItemStackTemplate template) -> template; case ItemRef.ById(var id) -> resolveId(id.get()); - case ItemRef.Stateful stateful -> resolveStatefulStack(stateful); + case ItemRef.Stateful stateful -> resolveStatefulTemplate(stateful); }; } - private static ItemStack resolveId(String rawId) { - ItemStack resolved = ItemsRepo.resolve(rawId); + private static ItemStackTemplate resolveId(String rawId) { + ItemStackTemplate resolved = ItemsRepo.resolveTemplate(rawId); - return resolved != null ? resolved : DEFAULT_ITEM.getDefaultInstance(); + return resolved != null ? resolved : DEFAULT_TEMPLATE; } - private static ItemStack resolveStatefulStack(ItemRef.Stateful stateful) { - ItemStack resolved = stateful.source() + private static ItemStackTemplate resolveStatefulTemplate(ItemRef.Stateful stateful) { + ItemStackTemplate resolved = stateful.source() .map(source -> switch (source) { - case ItemRef.Direct(Item item) -> new ItemStack(item); + case ItemRef.Direct(ItemStackTemplate template) -> template; case ItemRef.ById(var id) -> resolveId(id.get()); case ItemRef.Stateful ignored -> throw new IllegalStateException("Stateful ItemRef cannot nest another Stateful as its source"); }) - .orElse(new ItemStack(DEFAULT_ITEM)); + .orElse(DEFAULT_TEMPLATE); S state = stateful.state().get(); for (StateItemGroup group : stateful.groups()) { - if (group.contains(resolved.getItem())) return new ItemStack(group.forState(state, resolved.getItem())); + if (group.contains(resolved)) return group.forState(state, resolved); } return resolved; diff --git a/src/main/java/com/github/mkram17/bazaarutils/utils/minecraft/item/ItemRef.java b/src/main/java/com/github/mkram17/bazaarutils/utils/minecraft/item/ItemRef.java index 77231ae148..f76196915d 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/utils/minecraft/item/ItemRef.java +++ b/src/main/java/com/github/mkram17/bazaarutils/utils/minecraft/item/ItemRef.java @@ -1,14 +1,15 @@ package com.github.mkram17.bazaarutils.utils.minecraft.item; import com.github.mkram17.bazaarutils.utils.minecraft.item.groups.StateItemGroup; -import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.ItemStackTemplate; import java.util.List; import java.util.Optional; import java.util.function.Supplier; public sealed interface ItemRef { - record Direct(Item item) implements ItemRef {} + record Direct(ItemStackTemplate template) implements ItemRef {} record ById(Supplier id) implements ItemRef {} record Stateful(Optional source, Supplier state, List> groups) implements ItemRef { static Stateful of(Supplier state, List> groups) { @@ -16,8 +17,12 @@ static Stateful of(Supplier state, List> groups) { } } - static ItemRef of(Item item) { - return new Direct(item); + static ItemRef of(ItemStackTemplate template) { + return new Direct(template); + } + + static ItemRef of(ItemStack stack) { + return new Direct(ItemStackTemplate.fromNonEmptyStack(stack)); } static ItemRef of(String id) { diff --git a/src/main/java/com/github/mkram17/bazaarutils/utils/minecraft/item/ItemsRepo.java b/src/main/java/com/github/mkram17/bazaarutils/utils/minecraft/item/ItemsRepo.java index 234e79f96c..d614910c51 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/utils/minecraft/item/ItemsRepo.java +++ b/src/main/java/com/github/mkram17/bazaarutils/utils/minecraft/item/ItemsRepo.java @@ -1,31 +1,51 @@ package com.github.mkram17.bazaarutils.utils.minecraft.item; +import com.github.mkram17.bazaarutils.BazaarUtils; +import com.github.mkram17.bazaarutils.events.BUListener; import com.github.mkram17.bazaarutils.utils.ResourceManager; import com.github.mkram17.bazaarutils.utils.Util; +import com.github.mkram17.bazaarutils.utils.annotations.modules.Module; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.Registries; import net.minecraft.resources.Identifier; import net.minecraft.tags.TagKey; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.ItemStackTemplate; import org.jetbrains.annotations.Nullable; import tech.thatgravyboat.repolib.api.RepoAPI; import tech.thatgravyboat.skyblockapi.api.data.SkyBlockCategory; import tech.thatgravyboat.skyblockapi.api.datatype.DataTypeItemStackKt; import tech.thatgravyboat.skyblockapi.api.datatype.DataTypes; +import tech.thatgravyboat.skyblockapi.api.events.base.Subscription; +import tech.thatgravyboat.skyblockapi.api.events.hypixel.HypixelJoinEvent; +import tech.thatgravyboat.skyblockapi.api.events.location.ServerDisconnectEvent; import tech.thatgravyboat.skyblockapi.api.repo.apis.SkyBlockItemsRepo; import java.util.*; import java.util.stream.Stream; -public final class ItemsRepo { +@Module +public final class ItemsRepo extends BUListener { - private static final Map RESOLVED_CACHE = new HashMap<>(); + private static final Map RESOLVED_CACHE = new HashMap<>(); private static List SKYBLOCK_ITEMS_CACHE = List.of(); private static boolean SKYBLOCK_REPO_READY = false; + private static boolean IN_HYPIXEL_SESSION = false; public ItemsRepo() {} + @Subscription + private void onHypixelJoin(HypixelJoinEvent event) { + IN_HYPIXEL_SESSION = true; + buildSkyBlockItemsCache(); + } + + @Subscription + private void onServerDisconnect(ServerDisconnectEvent event) { + IN_HYPIXEL_SESSION = false; + } + /** * Typed representation of an {@code @ItemTag} value. */ @@ -82,11 +102,15 @@ static ItemFilter parse(@Nullable String raw) { } } + // 26.1 doesn't bind default components onto item holders until a world/session is joined; + // constructing an ItemStack off repo data before that throws "Components not bound yet" + // out of Holder.Reference#components public static void buildSkyBlockItemsCache() { - if (SKYBLOCK_REPO_READY) return; - SKYBLOCK_REPO_READY = true; + if (!IN_HYPIXEL_SESSION || !BazaarUtils.isLateInitDone()) return; SKYBLOCK_ITEMS_CACHE = buildSkyBlockItems(); + SKYBLOCK_REPO_READY = true; + Util.logMessage("SkyBlock items cache built — %s items".formatted(SKYBLOCK_ITEMS_CACHE.size())); } @@ -95,15 +119,21 @@ public static void buildSkyBlockItemsCache() { * Tries SkyBlock first (if ready), then falls back to the vanilla registry. */ public static @Nullable ItemStack resolve(String rawId) { + ItemStackTemplate template = resolveTemplate(rawId); + + return template != null ? template.create() : null; + } + + public static @Nullable ItemStackTemplate resolveTemplate(String rawId) { if (rawId == null || rawId.isEmpty()) return null; return RESOLVED_CACHE.computeIfAbsent(rawId, id -> { if (SKYBLOCK_REPO_READY) { ItemStack result = SkyBlockItemsRepo.INSTANCE.getItemStack(id); - if (result != null) return result; + if (result != null) return ItemStackTemplate.fromNonEmptyStack(result); } - return resolveVanilla(id); + return resolveVanillaTemplate(id); }); } @@ -138,7 +168,7 @@ public static List getItems(ItemFilter filter) { case ItemFilter.All ignored -> { List vanilla = getVanillaItems(null); if (!SKYBLOCK_REPO_READY) yield vanilla; - yield Stream.concat(vanilla.stream(), skyBlockItems().stream()).toList(); + yield Stream.concat(vanilla.stream(), skyBlockItems().stream().map(ItemStack::copy)).toList(); } case ItemFilter.VanillaTag(Identifier tagId) -> getVanillaItems(tagId); @@ -166,6 +196,15 @@ public static List getItems(ItemFilter filter) { .orElse(null); } + private static @Nullable ItemStackTemplate resolveVanillaTemplate(String id) { + Identifier identifier = Identifier.tryParse(id); + if (identifier == null) return null; + + return BuiltInRegistries.ITEM.getOptional(identifier) + .map(ItemStackTemplate::new) + .orElse(null); + } + private static List getVanillaItems(@Nullable Identifier tagId) { Stream items = BuiltInRegistries.ITEM.stream(); @@ -190,9 +229,9 @@ private static List getSkyBlockByCategory(String category) { return skyBlockItems().stream() .filter(stack -> { SkyBlockCategory stackCategory = DataTypeItemStackKt.getData(stack, DataTypes.INSTANCE.getCATEGORY()); - return stackCategory != null && target.equals(stackCategory, false); }) + .map(ItemStack::copy) .toList(); } @@ -211,6 +250,7 @@ private static List getSkyBlockBazaarItems() { return skyBlockItems().stream() .filter(stack -> conversions.containsKey(Util.stripFormatCodes(stack.getHoverName().getString()).toLowerCase(Locale.ROOT))) + .map(ItemStack::copy) .toList(); } diff --git a/src/main/java/com/github/mkram17/bazaarutils/utils/minecraft/item/groups/ItemGroups.java b/src/main/java/com/github/mkram17/bazaarutils/utils/minecraft/item/groups/ItemGroups.java index 21e78105d1..3d84690c9b 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/utils/minecraft/item/groups/ItemGroups.java +++ b/src/main/java/com/github/mkram17/bazaarutils/utils/minecraft/item/groups/ItemGroups.java @@ -1,6 +1,6 @@ package com.github.mkram17.bazaarutils.utils.minecraft.item.groups; -import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStackTemplate; import net.minecraft.world.item.Items; import java.util.Set; @@ -8,32 +8,32 @@ public final class ItemGroups { private ItemGroups() {} - public static final Set GLASS_PANES = Set.of( - Items.GLASS_PANE, - Items.WHITE_STAINED_GLASS_PANE, Items.ORANGE_STAINED_GLASS_PANE, - Items.MAGENTA_STAINED_GLASS_PANE, Items.LIGHT_BLUE_STAINED_GLASS_PANE, - Items.YELLOW_STAINED_GLASS_PANE, Items.LIME_STAINED_GLASS_PANE, - Items.PINK_STAINED_GLASS_PANE, Items.GRAY_STAINED_GLASS_PANE, - Items.LIGHT_GRAY_STAINED_GLASS_PANE, Items.CYAN_STAINED_GLASS_PANE, - Items.PURPLE_STAINED_GLASS_PANE, Items.BLUE_STAINED_GLASS_PANE, - Items.BROWN_STAINED_GLASS_PANE, Items.GREEN_STAINED_GLASS_PANE, - Items.RED_STAINED_GLASS_PANE, Items.BLACK_STAINED_GLASS_PANE + public static final Set GLASS_PANES = Set.of( + new ItemStackTemplate(Items.GLASS_PANE), + new ItemStackTemplate(Items.WHITE_STAINED_GLASS_PANE), new ItemStackTemplate(Items.ORANGE_STAINED_GLASS_PANE), + new ItemStackTemplate(Items.MAGENTA_STAINED_GLASS_PANE), new ItemStackTemplate(Items.LIGHT_BLUE_STAINED_GLASS_PANE), + new ItemStackTemplate(Items.YELLOW_STAINED_GLASS_PANE), new ItemStackTemplate(Items.LIME_STAINED_GLASS_PANE), + new ItemStackTemplate(Items.PINK_STAINED_GLASS_PANE), new ItemStackTemplate(Items.GRAY_STAINED_GLASS_PANE), + new ItemStackTemplate(Items.LIGHT_GRAY_STAINED_GLASS_PANE), new ItemStackTemplate(Items.CYAN_STAINED_GLASS_PANE), + new ItemStackTemplate(Items.PURPLE_STAINED_GLASS_PANE), new ItemStackTemplate(Items.BLUE_STAINED_GLASS_PANE), + new ItemStackTemplate(Items.BROWN_STAINED_GLASS_PANE), new ItemStackTemplate(Items.GREEN_STAINED_GLASS_PANE), + new ItemStackTemplate(Items.RED_STAINED_GLASS_PANE), new ItemStackTemplate(Items.BLACK_STAINED_GLASS_PANE) ); - public static final Set GLASS_BLOCKS = Set.of( - Items.GLASS, - Items.WHITE_STAINED_GLASS, Items.ORANGE_STAINED_GLASS, - Items.MAGENTA_STAINED_GLASS, Items.LIGHT_BLUE_STAINED_GLASS, - Items.YELLOW_STAINED_GLASS, Items.LIME_STAINED_GLASS, - Items.PINK_STAINED_GLASS, Items.GRAY_STAINED_GLASS, - Items.LIGHT_GRAY_STAINED_GLASS, Items.CYAN_STAINED_GLASS, - Items.PURPLE_STAINED_GLASS, Items.BLUE_STAINED_GLASS, - Items.BROWN_STAINED_GLASS, Items.GREEN_STAINED_GLASS, - Items.RED_STAINED_GLASS, Items.BLACK_STAINED_GLASS + public static final Set GLASS_BLOCKS = Set.of( + new ItemStackTemplate(Items.GLASS), + new ItemStackTemplate(Items.WHITE_STAINED_GLASS), new ItemStackTemplate(Items.ORANGE_STAINED_GLASS), + new ItemStackTemplate(Items.MAGENTA_STAINED_GLASS), new ItemStackTemplate(Items.LIGHT_BLUE_STAINED_GLASS), + new ItemStackTemplate(Items.YELLOW_STAINED_GLASS), new ItemStackTemplate(Items.LIME_STAINED_GLASS), + new ItemStackTemplate(Items.PINK_STAINED_GLASS), new ItemStackTemplate(Items.GRAY_STAINED_GLASS), + new ItemStackTemplate(Items.LIGHT_GRAY_STAINED_GLASS), new ItemStackTemplate(Items.CYAN_STAINED_GLASS), + new ItemStackTemplate(Items.PURPLE_STAINED_GLASS), new ItemStackTemplate(Items.BLUE_STAINED_GLASS), + new ItemStackTemplate(Items.BROWN_STAINED_GLASS), new ItemStackTemplate(Items.GREEN_STAINED_GLASS), + new ItemStackTemplate(Items.RED_STAINED_GLASS), new ItemStackTemplate(Items.BLACK_STAINED_GLASS) ); public static final StateItemGroup BOOKMARKED_STATE_GROUP = StateItemGroup.any() - .on(true, Items.GREEN_STAINED_GLASS_PANE) - .on(false, Items.RED_STAINED_GLASS_PANE) + .on(true, new ItemStackTemplate(Items.GREEN_STAINED_GLASS_PANE)) + .on(false, new ItemStackTemplate(Items.RED_STAINED_GLASS_PANE)) .build(); } \ No newline at end of file diff --git a/src/main/java/com/github/mkram17/bazaarutils/utils/minecraft/item/groups/StateItem.java b/src/main/java/com/github/mkram17/bazaarutils/utils/minecraft/item/groups/StateItem.java index 7f0cd82bec..60f67195f1 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/utils/minecraft/item/groups/StateItem.java +++ b/src/main/java/com/github/mkram17/bazaarutils/utils/minecraft/item/groups/StateItem.java @@ -1,13 +1,13 @@ package com.github.mkram17.bazaarutils.utils.minecraft.item.groups; -import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStackTemplate; public sealed interface StateItem { - record Fixed(Item item) implements StateItem {} + record Fixed(ItemStackTemplate template) implements StateItem {} record Configured() implements StateItem {} - static StateItem of(Item item) { - return new Fixed(item); + static StateItem of(ItemStackTemplate template) { + return new Fixed(template); } static StateItem configured() { diff --git a/src/main/java/com/github/mkram17/bazaarutils/utils/minecraft/item/groups/StateItemGroup.java b/src/main/java/com/github/mkram17/bazaarutils/utils/minecraft/item/groups/StateItemGroup.java index abe7fae3f9..98e224a2e1 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/utils/minecraft/item/groups/StateItemGroup.java +++ b/src/main/java/com/github/mkram17/bazaarutils/utils/minecraft/item/groups/StateItemGroup.java @@ -1,6 +1,8 @@ package com.github.mkram17.bazaarutils.utils.minecraft.item.groups; import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemInstance; +import net.minecraft.world.item.ItemStackTemplate; import java.util.LinkedHashMap; import java.util.Map; @@ -20,11 +22,16 @@ public boolean contains(Item item) { return recognized == ANY || recognized.contains(item); } - public Item forState(S state, Item configured) { + /** Convenience overload for checking a resolved {@link ItemInstance} — live stack or template — directly. */ + public boolean contains(ItemInstance instance) { + return contains(instance.typeHolder().value()); + } + + public ItemStackTemplate forState(S state, ItemStackTemplate configured) { StateItem stateItem = states.getOrDefault(state, StateItem.configured()); return switch (stateItem) { - case StateItem.Fixed(Item item) -> item; + case StateItem.Fixed(ItemStackTemplate template) -> template; case StateItem.Configured() -> configured; }; } @@ -49,7 +56,7 @@ public static StateItemGroup of(Set recognized, Map s public static StateItemGroup of(Map states) { Set recognized = states.values().stream() .filter(state -> state instanceof StateItem.Fixed) - .map(state -> ((StateItem.Fixed) state).item()) + .map(state -> ((StateItem.Fixed) state).template().typeHolder().value()) .collect(Collectors.toSet()); return new StateItemGroup<>(recognized, states); @@ -63,8 +70,8 @@ private Builder(Set recognized) { this.recognized = recognized; } - public Builder on(S state, Item item) { - states.put(state, StateItem.of(item)); + public Builder on(S state, ItemStackTemplate template) { + states.put(state, StateItem.of(template)); return this; } From 114c05a647c45556a585e464f38b5b419a34bb68 Mon Sep 17 00:00:00 2001 From: 0xar-ds Date: Tue, 1 Sep 2026 03:20:32 +0200 Subject: [PATCH 15/25] Move renderer registry' to module system otherwise we have issues with holders initialization Signed-off-by: 0xar-ds --- .../github/mkram17/bazaarutils/config/util/ConfigUtil.java | 6 ------ .../config/util/client/ItemRendererProvider.java | 7 ++++++- .../config/util/client/SlotRendererProvider.java | 6 +++++- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/github/mkram17/bazaarutils/config/util/ConfigUtil.java b/src/main/java/com/github/mkram17/bazaarutils/config/util/ConfigUtil.java index 28eaaecb8f..6f976309d9 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/config/util/ConfigUtil.java +++ b/src/main/java/com/github/mkram17/bazaarutils/config/util/ConfigUtil.java @@ -3,8 +3,6 @@ import com.github.mkram17.bazaarutils.BazaarUtils; import com.github.mkram17.bazaarutils.config.BUConfig; import com.github.mkram17.bazaarutils.config.patcher.ConfigPatches; -import com.github.mkram17.bazaarutils.config.util.client.ItemRendererProvider; -import com.github.mkram17.bazaarutils.config.util.client.SlotRendererProvider; import com.github.mkram17.bazaarutils.utils.Util; import com.google.gson.JsonObject; import com.teamresourceful.resourcefulconfig.api.client.ResourcefulConfigScreen; @@ -21,7 +19,6 @@ import static com.github.mkram17.bazaarutils.BazaarUtils.CONFIGURATOR; - public class ConfigUtil { public static final Map> PATCHES = ConfigPatches.loadPatches(); @@ -64,9 +61,6 @@ public static ResourcefulConfig register(Configurator configurator) { "— expected VERSION = " + (PATCHES.size() + 1) ); } - - ItemRendererProvider.register(); - SlotRendererProvider.register(); configurator.register(BUConfig.class, event -> PATCHES.forEach((version, patch) -> diff --git a/src/main/java/com/github/mkram17/bazaarutils/config/util/client/ItemRendererProvider.java b/src/main/java/com/github/mkram17/bazaarutils/config/util/client/ItemRendererProvider.java index 527d86ab71..9611a14f0a 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/config/util/client/ItemRendererProvider.java +++ b/src/main/java/com/github/mkram17/bazaarutils/config/util/client/ItemRendererProvider.java @@ -1,11 +1,16 @@ package com.github.mkram17.bazaarutils.config.util.client; import com.github.mkram17.bazaarutils.config.util.api.ItemElement; +import com.github.mkram17.bazaarutils.utils.annotations.modules.PreInitModule; import com.teamresourceful.resourcefulconfig.api.client.ResourcefulConfigUI; import net.minecraft.resources.Identifier; + +@PreInitModule public final class ItemRendererProvider { - private ItemRendererProvider() {} + public ItemRendererProvider() { + register(); + } public static void register() { ResourcefulConfigUI.registerElementRenderer( diff --git a/src/main/java/com/github/mkram17/bazaarutils/config/util/client/SlotRendererProvider.java b/src/main/java/com/github/mkram17/bazaarutils/config/util/client/SlotRendererProvider.java index 550aeef9c2..78e4c29dbb 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/config/util/client/SlotRendererProvider.java +++ b/src/main/java/com/github/mkram17/bazaarutils/config/util/client/SlotRendererProvider.java @@ -3,13 +3,17 @@ import com.github.mkram17.bazaarutils.config.features.gui.ButtonsConfig; import com.github.mkram17.bazaarutils.config.util.api.SlotElement; import com.github.mkram17.bazaarutils.config.util.api.SlotProviders; +import com.github.mkram17.bazaarutils.utils.annotations.modules.PreInitModule; import com.teamresourceful.resourcefulconfig.api.client.ResourcefulConfigUI; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.resources.Identifier; +@PreInitModule public final class SlotRendererProvider { - private SlotRendererProvider() {} + public SlotRendererProvider() { + register(); + } public static void register() { ResourcefulConfigUI.registerElementRenderer( From 90d2ded7e1754b9df503c08e54ae3df80518d047 Mon Sep 17 00:00:00 2001 From: 0xar-ds Date: Fri, 22 May 2026 17:03:47 -0300 Subject: [PATCH 16/25] Use SkyblockAPI's CurrencyAPI for purse retrieving Signed-off-by: 0xar-ds --- .../utils/bazaar/SignInputHelper.java | 59 +------------------ 1 file changed, 3 insertions(+), 56 deletions(-) diff --git a/src/main/java/com/github/mkram17/bazaarutils/utils/bazaar/SignInputHelper.java b/src/main/java/com/github/mkram17/bazaarutils/utils/bazaar/SignInputHelper.java index c862674660..baaf867d2b 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/utils/bazaar/SignInputHelper.java +++ b/src/main/java/com/github/mkram17/bazaarutils/utils/bazaar/SignInputHelper.java @@ -19,7 +19,6 @@ import com.github.mkram17.bazaarutils.utils.minecraft.gui.ScreenManager; import com.github.mkram17.bazaarutils.utils.minecraft.gui.container.ContainerManager; import com.github.mkram17.bazaarutils.utils.minecraft.gui.sign.SignManager; -import it.unimi.dsi.fastutil.objects.ObjectArrayList; import lombok.Getter; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; @@ -27,14 +26,10 @@ import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.Container; import net.minecraft.world.item.ItemStack; -import net.minecraft.world.scores.ScoreHolder; -import net.minecraft.world.scores.DisplaySlot; -import net.minecraft.world.scores.Objective; -import net.minecraft.world.scores.PlayerTeam; import net.minecraft.world.inventory.ChestMenu; import net.minecraft.network.chat.Component; -import net.minecraft.ChatFormatting; import org.jetbrains.annotations.NotNull; +import tech.thatgravyboat.skyblockapi.api.profile.currency.CurrencyAPI; import java.util.List; import java.util.Optional; @@ -92,8 +87,6 @@ protected void handleAction(T state) { protected abstract ResolvedInput resolveInput(T state); public abstract static class TransactionAmount extends SignInputHelper { - private static final Pattern PURSE_PATTERN = Pattern.compile("(Purse|Piggy): (?[0-9,.]+)"); - public record TransactionState( @NotNull Double purse, @@ -147,53 +140,7 @@ protected Optional makeState(ContainerLoadedEvent event) { if (productId.isEmpty()) return Optional.empty(); - Optional purse = Optional.of(Minecraft.getInstance()) - .flatMap(client -> Optional.ofNullable(client.level)) - .flatMap(world -> Optional.of(world.getScoreboard())) - .flatMap(scoreboard -> { - Objective objective = scoreboard.getDisplayObjective(DisplaySlot.SIDEBAR); - - if (objective == null) { - return Optional.empty(); - } - - ObjectArrayList scoreboardLines = new ObjectArrayList<>(); - - for (ScoreHolder scoreHolder : scoreboard.getTrackedPlayers()) { - if (scoreboard.listPlayerScores(scoreHolder).containsKey(objective)) { - PlayerTeam team = scoreboard.getPlayersTeam(scoreHolder.getScoreboardName()); - - if (team != null) { - String line = team.getPlayerPrefix().getString() + team.getPlayerSuffix().getString(); - - if (!line.trim().isEmpty()) { - scoreboardLines.add(ChatFormatting.stripFormatting(line)); - } - } - } - } - - return Optional.of(scoreboardLines); - }) - .flatMap(lines -> { - for (String line : lines) { - if (line.contains("Purse:") || line.contains("Piggy:")) { - Matcher matcher = PURSE_PATTERN.matcher(line); - - if (matcher.find()) { - try { - return Optional.of(Double.parseDouble(matcher.group("purse").replace(",", ""))); - } catch (NumberFormatException e) { - Util.notifyError("Failed to parse purse from scoreboard", e); - } - } - } - } - - return Optional.empty(); - }); - - if (purse.isEmpty()) return Optional.empty(); + double purse = CurrencyAPI.INSTANCE.getPurse(); Optional playerInventory = Optional.of(Minecraft.getInstance()) .flatMap(client -> Optional.ofNullable(client.player)) @@ -201,7 +148,7 @@ protected Optional makeState(ContainerLoadedEvent event) { if (playerInventory.isEmpty()) return Optional.empty(); - return Optional.of(new TransactionState(purse.get(), productId.get(), productItem.get(), inputSign.get(), playerInventory.get(), container, event.getScreen())); + return Optional.of(new TransactionState(purse, productId.get(), productItem.get(), inputSign.get(), playerInventory.get(), container, event.getScreen())); } public TransactionAmount(@NotNull String name, @NotNull BazaarSlots.BazaarSlot inputSignRef) { From b17bf74dd219f7b9bf7217edf15f1b67495d7c6d Mon Sep 17 00:00:00 2001 From: 0xar-ds Date: Fri, 22 May 2026 18:04:00 -0300 Subject: [PATCH 17/25] Have each concrete SignInputHelper implement their AmountStrategy::Max computational logic Signed-off-by: 0xar-ds --- .../amount/BuyOrderAmountHelper.java | 12 +++++ .../amount/InstantBuyAmountHelper.java | 21 +++++++++ .../amount/SellOfferAmountHelper.java | 16 +++++++ .../utils/bazaar/SignInputHelper.java | 44 +------------------ 4 files changed, 50 insertions(+), 43 deletions(-) diff --git a/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/BuyOrderAmountHelper.java b/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/BuyOrderAmountHelper.java index 9c3df99c9c..d9acee5874 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/BuyOrderAmountHelper.java +++ b/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/BuyOrderAmountHelper.java @@ -6,6 +6,9 @@ import com.github.mkram17.bazaarutils.utils.bazaar.gui.BazaarScreenMatcher; import com.github.mkram17.bazaarutils.utils.bazaar.gui.BazaarScreenType; import com.github.mkram17.bazaarutils.utils.bazaar.gui.BazaarSlots; +import com.github.mkram17.bazaarutils.utils.bazaar.gui.layouts.TransactionPageLayout; +import com.github.mkram17.bazaarutils.utils.bazaar.market.order.OrderUtil; +import com.github.mkram17.bazaarutils.utils.bazaar.market.price.PricingPosition; import com.github.mkram17.bazaarutils.utils.minecraft.components.CustomDataComponents; import com.github.mkram17.bazaarutils.utils.bazaar.market.order.TransactionType; import com.github.mkram17.bazaarutils.utils.minecraft.gui.ScreenMatcher; @@ -100,6 +103,15 @@ protected int computeFixedValue(TransactionState state) { return getFixedAmount(); } + @Override + protected int computeMaxValue(TransactionAmount.TransactionState state) { + int amountCanAfford = (int) (state.purse() / OrderUtil.getPriceForPosition(state.productId(), PricingPosition.COMPETITIVE, getTransactionType())); + + return TransactionPageLayout.findBuyOrderAmountLimit(state.inputSign().itemStack()) + .map(limit -> Math.min(amountCanAfford, limit)) + .orElse(amountCanAfford); + } + @Override protected Component getButtonItemText(TransactionState state) { return Component.nullToEmpty("Order " + getButtonItemStackSize(state) + " items."); diff --git a/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/InstantBuyAmountHelper.java b/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/InstantBuyAmountHelper.java index 1738dd2485..087fc787e6 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/InstantBuyAmountHelper.java +++ b/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/InstantBuyAmountHelper.java @@ -3,10 +3,14 @@ import com.github.mkram17.bazaarutils.config.util.api.SlotProviders; import com.github.mkram17.bazaarutils.config.util.api.annotations.ContainerSlot; import com.github.mkram17.bazaarutils.utils.bazaar.SignInputHelper; +import com.github.mkram17.bazaarutils.utils.bazaar.data.BazaarDataUtil; import com.github.mkram17.bazaarutils.utils.bazaar.gui.BazaarScreenMatcher; import com.github.mkram17.bazaarutils.utils.bazaar.gui.BazaarScreenType; import com.github.mkram17.bazaarutils.utils.bazaar.gui.BazaarSlots; +import com.github.mkram17.bazaarutils.utils.bazaar.gui.layouts.TransactionPageLayout; import com.github.mkram17.bazaarutils.utils.bazaar.market.order.TransactionType; +import com.github.mkram17.bazaarutils.utils.minecraft.ItemInfo; +import com.github.mkram17.bazaarutils.utils.minecraft.SlotLookup; import com.github.mkram17.bazaarutils.utils.minecraft.components.CustomDataComponents; import com.github.mkram17.bazaarutils.utils.minecraft.gui.ScreenMatcher; import com.github.mkram17.bazaarutils.utils.minecraft.item.ItemRef; @@ -17,7 +21,10 @@ import com.teamresourceful.resourcefulconfig.api.types.info.ListEntryInfoProvider; import lombok.Getter; import net.minecraft.network.chat.Component; +import net.minecraft.world.item.ItemStack; +import java.util.Objects; +import java.util.Optional; import java.util.stream.IntStream; @Getter @@ -100,6 +107,20 @@ protected int computeFixedValue(TransactionState state) { return getFixedAmount(); } + @Override + protected int computeMaxValue(TransactionAmount.TransactionState state) { + return SlotLookup.getInventoryItem(state.container(), BazaarSlots.INSTANT_BUY.INPUT_FILLING_AMOUNT.slot) + .map(ItemInfo::itemStack) + .flatMap(TransactionPageLayout::findOptionAmount) + .map(value -> (int) Math.floor(value)) + .orElse((int) state.playerInventory() + .getNonEquipmentItems() + .stream() + .filter(ItemStack::isEmpty) + .count() + ); + } + @Override protected Component getButtonItemText(TransactionState state) { return Component.nullToEmpty("Offer " + getButtonItemStackSize(state) + " items."); diff --git a/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/SellOfferAmountHelper.java b/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/SellOfferAmountHelper.java index 21da5a9fbb..a8b6327bbd 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/SellOfferAmountHelper.java +++ b/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/SellOfferAmountHelper.java @@ -3,6 +3,7 @@ import com.github.mkram17.bazaarutils.config.util.api.SlotProviders; import com.github.mkram17.bazaarutils.config.util.api.annotations.ContainerSlot; import com.github.mkram17.bazaarutils.utils.bazaar.SignInputHelper; +import com.github.mkram17.bazaarutils.utils.bazaar.data.BazaarDataUtil; import com.github.mkram17.bazaarutils.utils.bazaar.gui.BazaarScreenMatcher; import com.github.mkram17.bazaarutils.utils.bazaar.gui.BazaarScreenType; import com.github.mkram17.bazaarutils.utils.bazaar.gui.BazaarSlots; @@ -17,7 +18,9 @@ import com.teamresourceful.resourcefulconfig.api.types.info.ListEntryInfoProvider; import lombok.Getter; import net.minecraft.network.chat.Component; +import net.minecraft.world.item.ItemStack; +import java.util.Optional; import java.util.stream.IntStream; @Getter @@ -100,6 +103,19 @@ protected int computeFixedValue(TransactionState state) { return getFixedAmount(); } + @Override + protected int computeMaxValue(TransactionState state) { + return state.playerInventory().getNonEquipmentItems().stream() + .filter(stack -> !stack.isEmpty()) + .filter(stack -> Optional.ofNullable(stack.getCustomName()) + .map(Component::getString) + .flatMap(BazaarDataUtil::findProductIdOptional) + .map(productId -> productId.equals(state.productId())) + .orElse(false)) + .mapToInt(ItemStack::getCount) + .sum(); + } + @Override protected Component getButtonItemText(TransactionState state) { return Component.nullToEmpty("Offer " + getButtonItemStackSize(state) + " items."); diff --git a/src/main/java/com/github/mkram17/bazaarutils/utils/bazaar/SignInputHelper.java b/src/main/java/com/github/mkram17/bazaarutils/utils/bazaar/SignInputHelper.java index baaf867d2b..7a11ff7937 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/utils/bazaar/SignInputHelper.java +++ b/src/main/java/com/github/mkram17/bazaarutils/utils/bazaar/SignInputHelper.java @@ -2,9 +2,7 @@ import com.github.mkram17.bazaarutils.events.minecraft.ContainerLoadedEvent; import com.github.mkram17.bazaarutils.utils.bazaar.gui.layouts.ProductPageLayout; -import com.github.mkram17.bazaarutils.utils.bazaar.gui.layouts.TransactionPageLayout; import com.github.mkram17.bazaarutils.utils.Util; -import com.github.mkram17.bazaarutils.utils.bazaar.data.BazaarDataUtil; import com.github.mkram17.bazaarutils.utils.bazaar.gui.BazaarScreenType; import com.github.mkram17.bazaarutils.utils.bazaar.gui.BazaarSlots; import com.github.mkram17.bazaarutils.utils.bazaar.market.order.Order; @@ -14,7 +12,6 @@ import com.github.mkram17.bazaarutils.utils.bazaar.market.price.PriceInfo; import com.github.mkram17.bazaarutils.utils.bazaar.market.price.PricingPosition; import com.github.mkram17.bazaarutils.utils.minecraft.ItemInfo; -import com.github.mkram17.bazaarutils.utils.minecraft.SlotLookup; import com.github.mkram17.bazaarutils.utils.minecraft.components.LoreParser; import com.github.mkram17.bazaarutils.utils.minecraft.gui.ScreenManager; import com.github.mkram17.bazaarutils.utils.minecraft.gui.container.ContainerManager; @@ -25,7 +22,6 @@ import net.minecraft.client.player.LocalPlayer; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.Container; -import net.minecraft.world.item.ItemStack; import net.minecraft.world.inventory.ChestMenu; import net.minecraft.network.chat.Component; import org.jetbrains.annotations.NotNull; @@ -174,45 +170,7 @@ protected ResolvedInput resolveInput(TransactionState state) { protected abstract int computeFixedValue(TransactionState state); - protected int computeMaxValue(TransactionState state) { - return switch (getTransactionType().getMethod()) { - case INSTANT -> { - if (getTransactionType().isBuy()) { - yield SlotLookup.getInventoryItem(state.container(), BazaarSlots.INSTANT_BUY.INPUT_FILLING_AMOUNT.slot) - .map(ItemInfo::itemStack) - .flatMap(TransactionPageLayout::findOptionAmount) - .map(value -> (int) Math.floor(value)) - .orElse((int) state.playerInventory() - .getNonEquipmentItems() - .stream() - .filter(ItemStack::isEmpty) - .count() - ); - } - // Should be impossible to reach, as there is no sign to input a custom amount on items to instant sell. - // TODO: consider refactors needed for this case not to exist - yield 0; - } - case ORDER -> { - if (getTransactionType().isBuy()) { - int amountCanAfford = (int) (state.purse() / OrderUtil.getPriceForPosition(state.productId(), PricingPosition.COMPETITIVE, getTransactionType())); - - yield TransactionPageLayout.findBuyOrderAmountLimit(state.inputSign().itemStack()) - .map(limit -> Math.min(amountCanAfford, limit)) - .orElse(amountCanAfford); - } - yield state.playerInventory().getNonEquipmentItems().stream() - .filter(stack -> !stack.isEmpty()) - .filter(stack -> Optional.ofNullable(stack.getCustomName()) - .map(Component::getString) - .flatMap(BazaarDataUtil::findProductIdOptional) - .map(productId -> productId.equals(state.productId())) - .orElse(false)) - .mapToInt(ItemStack::getCount) - .sum(); - } - }; - } + protected abstract int computeMaxValue(TransactionState state); } public abstract static class TransactionCost extends SignInputHelper { From 19f88089a22f0b2a95956a1dad9ba7e59aa00cfa Mon Sep 17 00:00:00 2001 From: 0xar-ds Date: Fri, 22 May 2026 18:04:33 -0300 Subject: [PATCH 18/25] Properly count amount of items fillable to the players inventory per context Signed-off-by: 0xar-ds --- .../amount/InstantBuyAmountHelper.java | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/InstantBuyAmountHelper.java b/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/InstantBuyAmountHelper.java index 087fc787e6..ce846eb316 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/InstantBuyAmountHelper.java +++ b/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/InstantBuyAmountHelper.java @@ -113,11 +113,22 @@ protected int computeMaxValue(TransactionAmount.TransactionState state) { .map(ItemInfo::itemStack) .flatMap(TransactionPageLayout::findOptionAmount) .map(value -> (int) Math.floor(value)) - .orElse((int) state.playerInventory() + .orElse(state.playerInventory() .getNonEquipmentItems() .stream() - .filter(ItemStack::isEmpty) - .count() + .mapToInt(stack -> { + int maxStackSize = state.productItem().itemStack().getMaxStackSize(); + if (stack.isEmpty()) return maxStackSize; + + boolean isSameItem = Optional.ofNullable(stack.getCustomName()) + .map(Component::getString) + .flatMap(BazaarDataUtil::findProductIdOptional) + .map(id -> id.equals(state.productId())) + .orElse(false); + + return isSameItem ? maxStackSize - stack.getCount() : 0; + }) + .sum() ); } From b15f139693ac7cca12e68854bfe82b12613e0dde Mon Sep 17 00:00:00 2001 From: 0xar-ds Date: Fri, 22 May 2026 18:04:54 -0300 Subject: [PATCH 19/25] Fix copy pasted #getButtonItemText Signed-off-by: 0xar-ds --- .../gui/buttons/inputhelper/amount/InstantBuyAmountHelper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/InstantBuyAmountHelper.java b/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/InstantBuyAmountHelper.java index ce846eb316..fff37ce8e3 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/InstantBuyAmountHelper.java +++ b/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/InstantBuyAmountHelper.java @@ -134,7 +134,7 @@ protected int computeMaxValue(TransactionAmount.TransactionState state) { @Override protected Component getButtonItemText(TransactionState state) { - return Component.nullToEmpty("Offer " + getButtonItemStackSize(state) + " items."); + return Component.nullToEmpty("Purchase " + getButtonItemStackSize(state) + " items."); } @Override From 48ca013659ca6abd626aa5f08d8baf3b312bd92c Mon Sep 17 00:00:00 2001 From: 0xar-ds Date: Fri, 22 May 2026 18:19:15 -0300 Subject: [PATCH 20/25] Memoize input resolution rather than to compute it for every consuming path Signed-off-by: 0xar-ds --- .../utils/bazaar/SignInputHelper.java | 67 ++++++++++++++++--- 1 file changed, 56 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/github/mkram17/bazaarutils/utils/bazaar/SignInputHelper.java b/src/main/java/com/github/mkram17/bazaarutils/utils/bazaar/SignInputHelper.java index 7a11ff7937..5edef11ff9 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/utils/bazaar/SignInputHelper.java +++ b/src/main/java/com/github/mkram17/bazaarutils/utils/bazaar/SignInputHelper.java @@ -16,6 +16,8 @@ import com.github.mkram17.bazaarutils.utils.minecraft.gui.ScreenManager; import com.github.mkram17.bazaarutils.utils.minecraft.gui.container.ContainerManager; import com.github.mkram17.bazaarutils.utils.minecraft.gui.sign.SignManager; +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; import lombok.Getter; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; @@ -25,6 +27,7 @@ import net.minecraft.world.inventory.ChestMenu; import net.minecraft.network.chat.Component; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import tech.thatgravyboat.skyblockapi.api.profile.currency.CurrencyAPI; import java.util.List; @@ -42,6 +45,7 @@ public sealed interface ResolvedInput permits ResolvedInput.Value, ResolvedInput record Value(Number amount) implements ResolvedInput { public String format() { double d = amount.doubleValue(); + return d == (long) d ? String.valueOf((long) d) : String.valueOf(Util.truncateNum(d)); @@ -57,13 +61,33 @@ public String format() { String format(); } + /** + * Wraps the lazily-computed, memoized result of {@link #resolveInput}. + * + *

On first {@link #get()} call the memoized supplier fires {@code resolveInput} exactly + * once and caches the result for the lifetime of this container load. + */ + protected class WorkingValue { + private final Supplier resolver; + + WorkingValue(T state) { + this.resolver = Suppliers.memoize(() -> resolveInput(state)); + } + + public ResolvedInput get() { + return resolver.get(); + } + } + @Getter @NotNull protected BazaarSlots.BazaarSlot inputSignRef; + @Nullable + private WorkingValue workingValue; + public SignInputHelper(@NotNull String name, @NotNull BazaarSlots.BazaarSlot inputSignRef) { super(name); - this.inputSignRef = inputSignRef; } @@ -71,11 +95,36 @@ protected Optional getInputSign(Container inventory) { return inputSignRef.query(inventory).first(inventory); } + /** + * Returns the live {@link WorkingValue} for this container load, creating it on first access. + * All render and action paths read through here so {@link #resolveInput} is called at most once. + */ + protected WorkingValue getWorkingValue(T state) { + if (workingValue == null) workingValue = createWorkingValue(state); + + return workingValue; + } + + /** + * Factory method for subclasses that need custom step/clamp logic for scroll. + * The default implementation returns a plain {@link WorkingValue} backed by + * {@link #resolveInput}. + */ + protected WorkingValue createWorkingValue(T state) { + return new WorkingValue(state); + } + + @Override + protected void resetState() { + workingValue = null; + super.resetState(); + } + @Override protected void handleAction(T state) { ContainerManager.clickSlot(state.inputSign().slotIndex(), 0); - ResolvedInput input = resolveInput(state); + ResolvedInput input = getWorkingValue(state).get(); SignManager.runOnNextSignOpen(event -> SignManager.setSignText(input.format(), true)); } @@ -153,9 +202,7 @@ public TransactionAmount(@NotNull String name, @NotNull BazaarSlots.BazaarSlot i @Override protected String getButtonItemStackSize(TransactionState state) { - ResolvedInput input = resolveInput(state); - - return input.format(); + return getWorkingValue(state).get().format(); } @Override @@ -205,11 +252,9 @@ protected Optional makeState(ContainerLoadedEvent event) { Container container = event.getContainer(); Optional inputSign = getInputSign(container); - if (inputSign.isEmpty()) return Optional.empty(); Optional productId = getItemProductId(inputSign.get()); - if (productId.isEmpty()) return Optional.empty(); return Optional.of(new TransactionState(productId.get(), inputSign.get(), container, event.getScreen())); @@ -221,14 +266,14 @@ public TransactionCost(@NotNull String name, @NotNull BazaarSlots.BazaarSlot inp @Override protected String getButtonItemStackSize(TransactionState state) { - ResolvedInput input = resolveInput(state); - - return input.format(); + return getWorkingValue(state).get().format(); } @Override protected ResolvedInput resolveInput(TransactionState state) { - return new ResolvedInput.Value(OrderUtil.getPriceForPosition(state.productId(), getPricingPosition(), getTransactionType())); + return new ResolvedInput.Value( + OrderUtil.getPriceForPosition(state.productId(), getPricingPosition(), getTransactionType()) + ); } } From 5bb8a8aaf2e99070cca257b0ff25d2fbb4426991 Mon Sep 17 00:00:00 2001 From: 0xar-ds Date: Fri, 22 May 2026 18:25:14 -0300 Subject: [PATCH 21/25] Reset state upon sign handled Signed-off-by: 0xar-ds --- .../bazaarutils/utils/bazaar/InputHelper.java | 6 ++-- .../utils/bazaar/SignInputHelper.java | 11 ++++-- .../utils/minecraft/gui/sign/SignManager.java | 35 +++++++++++++------ 3 files changed, 37 insertions(+), 15 deletions(-) diff --git a/src/main/java/com/github/mkram17/bazaarutils/utils/bazaar/InputHelper.java b/src/main/java/com/github/mkram17/bazaarutils/utils/bazaar/InputHelper.java index 82a21bcf9e..6f21dbcf91 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/utils/bazaar/InputHelper.java +++ b/src/main/java/com/github/mkram17/bazaarutils/utils/bazaar/InputHelper.java @@ -17,6 +17,7 @@ import org.jetbrains.annotations.Nullable; import java.util.Optional; +import java.util.concurrent.Callable; public abstract class InputHelper implements ItemButton { @Getter @@ -80,8 +81,7 @@ public Result onButtonClicked(int button) { SoundUtil.playSound(BUTTON_SOUND, BUTTON_VOLUME); - handleAction(state.get()); - resetState(); + handleAction(state.get(), this::resetState); return Result.CONSUMED; } @@ -94,5 +94,5 @@ public Result onButtonClicked(int button) { // Action stuff - protected abstract void handleAction(T state); + protected abstract void handleAction(T state, Runnable callback); } \ No newline at end of file diff --git a/src/main/java/com/github/mkram17/bazaarutils/utils/bazaar/SignInputHelper.java b/src/main/java/com/github/mkram17/bazaarutils/utils/bazaar/SignInputHelper.java index 5edef11ff9..94fe9704bd 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/utils/bazaar/SignInputHelper.java +++ b/src/main/java/com/github/mkram17/bazaarutils/utils/bazaar/SignInputHelper.java @@ -1,6 +1,7 @@ package com.github.mkram17.bazaarutils.utils.bazaar; import com.github.mkram17.bazaarutils.events.minecraft.ContainerLoadedEvent; +import com.github.mkram17.bazaarutils.utils.Result; import com.github.mkram17.bazaarutils.utils.bazaar.gui.layouts.ProductPageLayout; import com.github.mkram17.bazaarutils.utils.Util; import com.github.mkram17.bazaarutils.utils.bazaar.gui.BazaarScreenType; @@ -121,12 +122,18 @@ protected void resetState() { } @Override - protected void handleAction(T state) { + protected void handleAction(T state, Runnable resetState) { ContainerManager.clickSlot(state.inputSign().slotIndex(), 0); ResolvedInput input = getWorkingValue(state).get(); - SignManager.runOnNextSignOpen(event -> SignManager.setSignText(input.format(), true)); + SignManager.runOnNextSignOpen(event -> { + SignManager.setSignText(input.format(), true); + + resetState.run(); + + return Result.CONSUMED; + }); } protected abstract ResolvedInput resolveInput(T state); diff --git a/src/main/java/com/github/mkram17/bazaarutils/utils/minecraft/gui/sign/SignManager.java b/src/main/java/com/github/mkram17/bazaarutils/utils/minecraft/gui/sign/SignManager.java index 0a456c2598..68eea748a7 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/utils/minecraft/gui/sign/SignManager.java +++ b/src/main/java/com/github/mkram17/bazaarutils/utils/minecraft/gui/sign/SignManager.java @@ -6,39 +6,54 @@ import com.github.mkram17.bazaarutils.mixin.AccessorSignEditScreen; import com.github.mkram17.bazaarutils.utils.PlayerActionUtil; import com.github.mkram17.bazaarutils.utils.Priority; +import com.github.mkram17.bazaarutils.utils.Result; import com.github.mkram17.bazaarutils.utils.Util; import com.github.mkram17.bazaarutils.utils.minecraft.gui.ScreenContext; import com.github.mkram17.bazaarutils.utils.minecraft.gui.ScreenManager; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.inventory.SignEditScreen; import tech.thatgravyboat.skyblockapi.api.events.base.Subscription; +import tech.thatgravyboat.skyblockapi.api.events.screen.ContainerCloseEvent; import java.util.Optional; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.function.Consumer; +import java.util.function.Function; public class SignManager { - /** One-shot callbacks to run the next time a sign opens; drained by {@link SignOpenDispatcher}. */ - private static final Queue> PENDING = new ConcurrentLinkedQueue<>(); + /** Handlers queued for the next sign open; drained by {@link SignQueueDispatcher} in order. */ + private static final Queue> PENDING = new ConcurrentLinkedQueue<>(); static { - BazaarUtils.EVENT_BUS.register(new SignOpenDispatcher()); + BazaarUtils.EVENT_BUS.register(new SignQueueDispatcher()); } - private static final class SignOpenDispatcher { + private static final class SignQueueDispatcher { @Subscription(priority = Priority.FIRST) private void onSignOpen(SignOpenEvent event) { - Consumer action; + Function handler; - while ((action = PENDING.poll()) != null) { - action.accept(event); + while ((handler = PENDING.poll()) != null) { + Result result = handler.apply(event); + + if (!result.propagate()) break; } } + + @Subscription(priority = Priority.FIRST) + private void onContainerClosed(ContainerCloseEvent event) { + PENDING.clear(); + } } - public static void runOnNextSignOpen(Consumer action) { - PENDING.add(action); + /** + * Queues a handler for the next sign that opens. Handlers run in order against a single + * {@link SignOpenEvent}; return {@link Result#CONSUMED} once you've claimed the sign so + * nothing else queued fires on it. The whole queue clears on {@link ContainerCloseEvent}, + * so a handler waiting on a sign that never opens doesn't fire on some later, unrelated one. + */ + public static void runOnNextSignOpen(Function handler) { + PENDING.add(handler); } public static void setSignText(String text, boolean closeAfter) { From a1aba4adfb7e7baf821e6602abdd3984e45592e1 Mon Sep 17 00:00:00 2001 From: 0xar-ds Date: Sat, 5 Sep 2026 18:23:47 +0200 Subject: [PATCH 22/25] Standard event subscription for SignManagers' Queue Dispatcher Signed-off-by: 0xar-ds --- .../utils/minecraft/gui/sign/SignManager.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/github/mkram17/bazaarutils/utils/minecraft/gui/sign/SignManager.java b/src/main/java/com/github/mkram17/bazaarutils/utils/minecraft/gui/sign/SignManager.java index 68eea748a7..730f52ccff 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/utils/minecraft/gui/sign/SignManager.java +++ b/src/main/java/com/github/mkram17/bazaarutils/utils/minecraft/gui/sign/SignManager.java @@ -1,6 +1,6 @@ package com.github.mkram17.bazaarutils.utils.minecraft.gui.sign; -import com.github.mkram17.bazaarutils.BazaarUtils; +import com.github.mkram17.bazaarutils.events.BUListener; import com.github.mkram17.bazaarutils.events.minecraft.SignOpenEvent; import com.github.mkram17.bazaarutils.misc.NotificationType; import com.github.mkram17.bazaarutils.mixin.AccessorSignEditScreen; @@ -8,6 +8,7 @@ import com.github.mkram17.bazaarutils.utils.Priority; import com.github.mkram17.bazaarutils.utils.Result; import com.github.mkram17.bazaarutils.utils.Util; +import com.github.mkram17.bazaarutils.utils.annotations.modules.PreInitModule; import com.github.mkram17.bazaarutils.utils.minecraft.gui.ScreenContext; import com.github.mkram17.bazaarutils.utils.minecraft.gui.ScreenManager; import net.minecraft.client.Minecraft; @@ -24,11 +25,8 @@ public class SignManager { /** Handlers queued for the next sign open; drained by {@link SignQueueDispatcher} in order. */ private static final Queue> PENDING = new ConcurrentLinkedQueue<>(); - static { - BazaarUtils.EVENT_BUS.register(new SignQueueDispatcher()); - } - - private static final class SignQueueDispatcher { + @PreInitModule + public static final class SignQueueDispatcher extends BUListener { @Subscription(priority = Priority.FIRST) private void onSignOpen(SignOpenEvent event) { Function handler; From d53f80484c1d61c9b27af112e95fc590f06b58a3 Mon Sep 17 00:00:00 2001 From: mkram17 <1maxkramer@gmail.com> Date: Sun, 6 Sep 2026 21:57:46 +0000 Subject: [PATCH 23/25] Drop imports left unused by the helper refactor --- .../gui/buttons/inputhelper/amount/InstantBuyAmountHelper.java | 2 -- .../github/mkram17/bazaarutils/utils/bazaar/InputHelper.java | 1 - 2 files changed, 3 deletions(-) diff --git a/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/InstantBuyAmountHelper.java b/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/InstantBuyAmountHelper.java index fff37ce8e3..bde62aa723 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/InstantBuyAmountHelper.java +++ b/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/InstantBuyAmountHelper.java @@ -21,9 +21,7 @@ import com.teamresourceful.resourcefulconfig.api.types.info.ListEntryInfoProvider; import lombok.Getter; import net.minecraft.network.chat.Component; -import net.minecraft.world.item.ItemStack; -import java.util.Objects; import java.util.Optional; import java.util.stream.IntStream; diff --git a/src/main/java/com/github/mkram17/bazaarutils/utils/bazaar/InputHelper.java b/src/main/java/com/github/mkram17/bazaarutils/utils/bazaar/InputHelper.java index 6f21dbcf91..4bbde8b1a3 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/utils/bazaar/InputHelper.java +++ b/src/main/java/com/github/mkram17/bazaarutils/utils/bazaar/InputHelper.java @@ -17,7 +17,6 @@ import org.jetbrains.annotations.Nullable; import java.util.Optional; -import java.util.concurrent.Callable; public abstract class InputHelper implements ItemButton { @Getter From fcb27c123f15a8adde0a47c6b3ec0455c80d9617 Mon Sep 17 00:00:00 2001 From: mkram17 <1maxkramer@gmail.com> Date: Sun, 6 Sep 2026 21:57:56 +0000 Subject: [PATCH 24/25] Expire queued sign handlers instead of clearing them on container close MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The chest closing is the same server-side transition that opens the sign, so clearing the queue on ContainerCloseEvent races the very sign open it exists to serve. It usually loses that race harmlessly — SkyblockAPI posts the event a tick behind the clientbound close packet, which lands it after the open-sign packet handler in the same task drain — but nothing guarantees the two packets arrive in one drain, and when they don't the sign silently goes unfilled. Each handler now carries a deadline and is skipped once past it, which bounds a handler waiting on a sign that never opens without depending on packet ordering. Claiming the sign with CONSUMED now also discards the rest of the queue, which is what runOnNextSignOpen already documented and what the close-clear had been doing incidentally. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_014iXqZFi27zfBERVHkLAjuj --- .../utils/minecraft/gui/sign/SignManager.java | 48 +++++++++++++------ 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/github/mkram17/bazaarutils/utils/minecraft/gui/sign/SignManager.java b/src/main/java/com/github/mkram17/bazaarutils/utils/minecraft/gui/sign/SignManager.java index 730f52ccff..b90a60f472 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/utils/minecraft/gui/sign/SignManager.java +++ b/src/main/java/com/github/mkram17/bazaarutils/utils/minecraft/gui/sign/SignManager.java @@ -14,7 +14,6 @@ import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.inventory.SignEditScreen; import tech.thatgravyboat.skyblockapi.api.events.base.Subscription; -import tech.thatgravyboat.skyblockapi.api.events.screen.ContainerCloseEvent; import java.util.Optional; import java.util.Queue; @@ -22,36 +21,57 @@ import java.util.function.Function; public class SignManager { + /** + * How long a queued handler stays eligible for the next sign. + * + *

Expiry rather than clearing on container close: the chest closing is the same server-side + * transition that opens the sign, so a close-triggered clear races the very sign open it is + * meant to serve. SkyblockAPI posts {@code ContainerCloseEvent} a tick behind the clientbound + * close packet, which usually puts it after the sign open — but only usually. + */ + private static final long HANDLER_TTL_MILLIS = 5_000L; + + private record PendingHandler(Function handler, long expiresAtMillis) { + static PendingHandler of(Function handler) { + return new PendingHandler(handler, System.currentTimeMillis() + HANDLER_TTL_MILLIS); + } + + boolean isExpired() { + return System.currentTimeMillis() > expiresAtMillis; + } + } + /** Handlers queued for the next sign open; drained by {@link SignQueueDispatcher} in order. */ - private static final Queue> PENDING = new ConcurrentLinkedQueue<>(); + private static final Queue PENDING = new ConcurrentLinkedQueue<>(); @PreInitModule public static final class SignQueueDispatcher extends BUListener { @Subscription(priority = Priority.FIRST) private void onSignOpen(SignOpenEvent event) { - Function handler; + PendingHandler pending; - while ((handler = PENDING.poll()) != null) { - Result result = handler.apply(event); + while ((pending = PENDING.poll()) != null) { + if (pending.isExpired()) continue; - if (!result.propagate()) break; - } - } + if (!pending.handler().apply(event).propagate()) { + // Claiming the sign also discards the rest: they were queued for a different one. + PENDING.clear(); - @Subscription(priority = Priority.FIRST) - private void onContainerClosed(ContainerCloseEvent event) { - PENDING.clear(); + break; + } + } } } /** * Queues a handler for the next sign that opens. Handlers run in order against a single * {@link SignOpenEvent}; return {@link Result#CONSUMED} once you've claimed the sign so - * nothing else queued fires on it. The whole queue clears on {@link ContainerCloseEvent}, - * so a handler waiting on a sign that never opens doesn't fire on some later, unrelated one. + * nothing else queued fires on it. A handler whose sign never opens expires after + * {@link #HANDLER_TTL_MILLIS}, so it can't fire on some later, unrelated one. */ public static void runOnNextSignOpen(Function handler) { - PENDING.add(handler); + PENDING.removeIf(PendingHandler::isExpired); + PENDING.add(PendingHandler.of(handler)); } public static void setSignText(String text, boolean closeAfter) { From 7451946fe2d5cefdef69224db3d3ccdae4244460 Mon Sep 17 00:00:00 2001 From: mkram17 <1maxkramer@gmail.com> Date: Sun, 6 Sep 2026 21:58:06 +0000 Subject: [PATCH 25/25] Do not memoize an input resolution that failed WorkingValue cached whatever resolveInput returned, including the -1 that getPriceForPosition yields when the bazaar reply has not landed yet. That froze the button on -1 for the rest of the container load and typed it into the sign; before memoization every render recomputed, so a transient miss healed itself. resolveInput now reports failure as an empty Optional and WorkingValue caches only a value that resolved, so a later render retries. A helper with no market data renders a blank stack size rather than -1, and clicking it no longer opens a sign it cannot fill. computeMaxValue returns OptionalInt for the same reason: BuyOrderAmountHelper divided the purse by that -1, and by a price of 0 it would have typed Integer.MAX_VALUE. getPriceForPositionOptional carries the distinction; getPriceForPosition keeps its -1 sentinel for the callers that only display a price. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_014iXqZFi27zfBERVHkLAjuj --- .../amount/BuyOrderAmountHelper.java | 15 ++-- .../amount/InstantBuyAmountHelper.java | 7 +- .../amount/SellOfferAmountHelper.java | 7 +- .../utils/bazaar/SignInputHelper.java | 72 +++++++++++++------ .../utils/bazaar/market/order/OrderUtil.java | 17 +++-- 5 files changed, 81 insertions(+), 37 deletions(-) diff --git a/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/BuyOrderAmountHelper.java b/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/BuyOrderAmountHelper.java index d9acee5874..f354222171 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/BuyOrderAmountHelper.java +++ b/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/BuyOrderAmountHelper.java @@ -21,6 +21,8 @@ import lombok.Getter; import net.minecraft.network.chat.Component; +import java.util.OptionalDouble; +import java.util.OptionalInt; import java.util.stream.IntStream; @Getter @@ -104,12 +106,17 @@ protected int computeFixedValue(TransactionState state) { } @Override - protected int computeMaxValue(TransactionAmount.TransactionState state) { - int amountCanAfford = (int) (state.purse() / OrderUtil.getPriceForPosition(state.productId(), PricingPosition.COMPETITIVE, getTransactionType())); + protected OptionalInt computeMaxValue(TransactionAmount.TransactionState state) { + OptionalDouble price = OrderUtil.getPriceForPositionOptional(state.productId(), PricingPosition.COMPETITIVE, getTransactionType()); - return TransactionPageLayout.findBuyOrderAmountLimit(state.inputSign().itemStack()) + // A missing or non-positive price divides into an amount that is nonsense rather than large. + if (price.isEmpty() || price.getAsDouble() <= 0) return OptionalInt.empty(); + + int amountCanAfford = (int) (state.purse() / price.getAsDouble()); + + return OptionalInt.of(TransactionPageLayout.findBuyOrderAmountLimit(state.inputSign().itemStack()) .map(limit -> Math.min(amountCanAfford, limit)) - .orElse(amountCanAfford); + .orElse(amountCanAfford)); } @Override diff --git a/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/InstantBuyAmountHelper.java b/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/InstantBuyAmountHelper.java index bde62aa723..8e9c73078c 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/InstantBuyAmountHelper.java +++ b/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/InstantBuyAmountHelper.java @@ -23,6 +23,7 @@ import net.minecraft.network.chat.Component; import java.util.Optional; +import java.util.OptionalInt; import java.util.stream.IntStream; @Getter @@ -106,8 +107,8 @@ protected int computeFixedValue(TransactionState state) { } @Override - protected int computeMaxValue(TransactionAmount.TransactionState state) { - return SlotLookup.getInventoryItem(state.container(), BazaarSlots.INSTANT_BUY.INPUT_FILLING_AMOUNT.slot) + protected OptionalInt computeMaxValue(TransactionAmount.TransactionState state) { + return OptionalInt.of(SlotLookup.getInventoryItem(state.container(), BazaarSlots.INSTANT_BUY.INPUT_FILLING_AMOUNT.slot) .map(ItemInfo::itemStack) .flatMap(TransactionPageLayout::findOptionAmount) .map(value -> (int) Math.floor(value)) @@ -127,7 +128,7 @@ protected int computeMaxValue(TransactionAmount.TransactionState state) { return isSameItem ? maxStackSize - stack.getCount() : 0; }) .sum() - ); + )); } @Override diff --git a/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/SellOfferAmountHelper.java b/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/SellOfferAmountHelper.java index a8b6327bbd..426a4f9dab 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/SellOfferAmountHelper.java +++ b/src/main/java/com/github/mkram17/bazaarutils/features/gui/buttons/inputhelper/amount/SellOfferAmountHelper.java @@ -21,6 +21,7 @@ import net.minecraft.world.item.ItemStack; import java.util.Optional; +import java.util.OptionalInt; import java.util.stream.IntStream; @Getter @@ -104,8 +105,8 @@ protected int computeFixedValue(TransactionState state) { } @Override - protected int computeMaxValue(TransactionState state) { - return state.playerInventory().getNonEquipmentItems().stream() + protected OptionalInt computeMaxValue(TransactionState state) { + return OptionalInt.of(state.playerInventory().getNonEquipmentItems().stream() .filter(stack -> !stack.isEmpty()) .filter(stack -> Optional.ofNullable(stack.getCustomName()) .map(Component::getString) @@ -113,7 +114,7 @@ protected int computeMaxValue(TransactionState state) { .map(productId -> productId.equals(state.productId())) .orElse(false)) .mapToInt(ItemStack::getCount) - .sum(); + .sum()); } @Override diff --git a/src/main/java/com/github/mkram17/bazaarutils/utils/bazaar/SignInputHelper.java b/src/main/java/com/github/mkram17/bazaarutils/utils/bazaar/SignInputHelper.java index 94fe9704bd..9622bf848d 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/utils/bazaar/SignInputHelper.java +++ b/src/main/java/com/github/mkram17/bazaarutils/utils/bazaar/SignInputHelper.java @@ -17,8 +17,6 @@ import com.github.mkram17.bazaarutils.utils.minecraft.gui.ScreenManager; import com.github.mkram17.bazaarutils.utils.minecraft.gui.container.ContainerManager; import com.github.mkram17.bazaarutils.utils.minecraft.gui.sign.SignManager; -import com.google.common.base.Supplier; -import com.google.common.base.Suppliers; import lombok.Getter; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; @@ -33,6 +31,8 @@ import java.util.List; import java.util.Optional; +import java.util.OptionalDouble; +import java.util.OptionalInt; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -65,18 +65,25 @@ public String format() { /** * Wraps the lazily-computed, memoized result of {@link #resolveInput}. * - *

On first {@link #get()} call the memoized supplier fires {@code resolveInput} exactly - * once and caches the result for the lifetime of this container load. + *

On first {@link #get()} call {@code resolveInput} fires and, if it resolved, the result + * is cached for the lifetime of this container load. A failed resolution is deliberately not + * cached: market data can land after the first render, and freezing that miss would leave the + * button showing — and the sign filled with — a value that never recovers. */ protected class WorkingValue { - private final Supplier resolver; + private final T state; + + @Nullable + private ResolvedInput resolved; WorkingValue(T state) { - this.resolver = Suppliers.memoize(() -> resolveInput(state)); + this.state = state; } - public ResolvedInput get() { - return resolver.get(); + public Optional get() { + if (resolved == null) resolved = resolveInput(state).orElse(null); + + return Optional.ofNullable(resolved); } } @@ -115,6 +122,14 @@ protected WorkingValue createWorkingValue(T state) { return new WorkingValue(state); } + /** + * The working value as the button's stack-size overlay, or blank while it cannot be resolved — + * a button showing nothing reads better than one showing a placeholder that isn't a real amount. + */ + protected String formatWorkingValue(T state) { + return getWorkingValue(state).get().map(ResolvedInput::format).orElse(""); + } + @Override protected void resetState() { workingValue = null; @@ -123,12 +138,19 @@ protected void resetState() { @Override protected void handleAction(T state, Runnable resetState) { - ContainerManager.clickSlot(state.inputSign().slotIndex(), 0); + Optional input = getWorkingValue(state).get(); - ResolvedInput input = getWorkingValue(state).get(); + // Opening a sign we have nothing to type into it just strands the player on it. + if (input.isEmpty()) { + Util.logMessage("Cannot handle action for " + name + ", input could not be resolved."); + + return; + } + + ContainerManager.clickSlot(state.inputSign().slotIndex(), 0); SignManager.runOnNextSignOpen(event -> { - SignManager.setSignText(input.format(), true); + SignManager.setSignText(input.get().format(), true); resetState.run(); @@ -136,7 +158,7 @@ protected void handleAction(T state, Runnable resetState) { }); } - protected abstract ResolvedInput resolveInput(T state); + protected abstract Optional resolveInput(T state); public abstract static class TransactionAmount extends SignInputHelper { public record TransactionState( @@ -209,22 +231,24 @@ public TransactionAmount(@NotNull String name, @NotNull BazaarSlots.BazaarSlot i @Override protected String getButtonItemStackSize(TransactionState state) { - return getWorkingValue(state).get().format(); + return formatWorkingValue(state); } @Override - protected ResolvedInput resolveInput(TransactionState state) { - int amount = switch (getAmountStrategy()) { + protected Optional resolveInput(TransactionState state) { + OptionalInt amount = switch (getAmountStrategy()) { case MAX -> computeMaxValue(state); - case FIXED -> computeFixedValue(state); + case FIXED -> OptionalInt.of(computeFixedValue(state)); }; - return new ResolvedInput.Value(amount); + return amount.isPresent() + ? Optional.of(new ResolvedInput.Value(amount.getAsInt())) + : Optional.empty(); } protected abstract int computeFixedValue(TransactionState state); - protected abstract int computeMaxValue(TransactionState state); + protected abstract OptionalInt computeMaxValue(TransactionState state); } public abstract static class TransactionCost extends SignInputHelper { @@ -273,14 +297,16 @@ public TransactionCost(@NotNull String name, @NotNull BazaarSlots.BazaarSlot inp @Override protected String getButtonItemStackSize(TransactionState state) { - return getWorkingValue(state).get().format(); + return formatWorkingValue(state); } @Override - protected ResolvedInput resolveInput(TransactionState state) { - return new ResolvedInput.Value( - OrderUtil.getPriceForPosition(state.productId(), getPricingPosition(), getTransactionType()) - ); + protected Optional resolveInput(TransactionState state) { + OptionalDouble price = OrderUtil.getPriceForPositionOptional(state.productId(), getPricingPosition(), getTransactionType()); + + return price.isPresent() + ? Optional.of(new ResolvedInput.Value(price.getAsDouble())) + : Optional.empty(); } } diff --git a/src/main/java/com/github/mkram17/bazaarutils/utils/bazaar/market/order/OrderUtil.java b/src/main/java/com/github/mkram17/bazaarutils/utils/bazaar/market/order/OrderUtil.java index b34be3fb08..baa113f5b0 100644 --- a/src/main/java/com/github/mkram17/bazaarutils/utils/bazaar/market/order/OrderUtil.java +++ b/src/main/java/com/github/mkram17/bazaarutils/utils/bazaar/market/order/OrderUtil.java @@ -99,9 +99,18 @@ public static void trackUserOrder(Order order) { } public static double getPriceForPosition(String productID, PricingPosition pricingPosition, TransactionType transactionType) { + return getPriceForPositionOptional(productID, pricingPosition, transactionType).orElse(-1); + } + + /** + * Same resolution as {@link #getPriceForPosition}, but reports an unresolvable price as an + * empty result instead of the {@code -1} sentinel — so callers that must not present or act on + * a bogus price can tell the two apart. + */ + public static OptionalDouble getPriceForPositionOptional(String productID, PricingPosition pricingPosition, TransactionType transactionType) { if (productID == null || pricingPosition == null || transactionType == null) { Util.notifyError("Call to OrderUtil.getPriceForPosition contained a null param", new Exception("Price resolution error")); - return -1; + return OptionalDouble.empty(); } OptionalDouble marketSellPriceOpt = BazaarDataUtil.findItemPriceOptional(productID, TransactionType.of(TransactionType.Side.SELL, TransactionType.Method.ORDER)); @@ -109,13 +118,13 @@ public static double getPriceForPosition(String productID, PricingPosition prici if(marketBuyPriceOpt.isEmpty() || marketSellPriceOpt.isEmpty()) { Util.notifyError("Could not resolve market prices for " + productID + " when calculating price for position. Buy price present: " + marketBuyPriceOpt.isPresent() + " Sell price present: " + marketSellPriceOpt.isPresent(), new Exception("Price resolution error")); - return -1; + return OptionalDouble.empty(); } double marketBuyPrice = marketBuyPriceOpt.getAsDouble(); double marketSellPrice = marketSellPriceOpt.getAsDouble(); - return switch (transactionType.getPriceType()) { + return OptionalDouble.of(switch (transactionType.getPriceType()) { case PriceType.INSTABUY -> switch (pricingPosition) { case COMPETITIVE -> marketSellPrice - 0.1; case MATCHED -> marketSellPrice; @@ -126,6 +135,6 @@ public static double getPriceForPosition(String productID, PricingPosition prici case MATCHED -> marketBuyPrice; case OUTBID -> marketBuyPrice - 0.1; }; - }; + }); } }