diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 88a50df..8797eaf 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -28,9 +28,9 @@ jobs: steps: - uses: actions/checkout@v2 - name: Set up JDK - uses: actions/setup-java@v2.3.1 + uses: actions/setup-java@v4 with: - java-version: 17 + java-version: 21 distribution: temurin - uses: actions/cache@v2 with: diff --git a/README.md b/README.md index 14dfe3e..da9d358 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,25 @@ # Cull Less Leaves -Cull Less Leaves is an improved version of [Cull Leaves](https://www.curseforge.com/minecraft/mc-mods/cull-leaves). -This mod gives you a **9 percent performance improvement** +Cull Less Leaves skips rendering of inner leaf layers, improving performance. + +## Performance + +In a worst-case scenario, overlooking a mangrove swamp, this mod can +have up to a _**30% performance increase**_ (measured on low-end hardware). Instead of leaving just the outer layer of leaves, Cull Less Leaves also renders a certain amount of layers defined in the config. **[Works best with Sodium](https://modrinth.com/mod/sodium)** +## Why not just use [Cull Leaves](https://www.curseforge.com/minecraft/mc-mods/cull-leaves)? + +Cull Leaves only renders the outermost layer of leaves, which can look +quite ugly (see fast culling in image below), but **Cull Leaves improves +performance more than Cull Less Leaves**. + +## Comparison + ![comparison](https://i.imgur.com/yMUQxMX.png) [![](https://www.bisecthosting.com/partners/custom-banners/08bbd3ff-5c0d-4480-8738-de0f070a04dd.png)](https://bisecthosting.com/xander) diff --git a/build.gradle.kts b/build.gradle.kts index cd09bde..f135650 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,27 +1,32 @@ plugins { java - id("fabric-loom") version "1.0.+" - id("io.github.juuxel.loom-quiltflower") version "1.7.+" + id("fabric-loom") version "1.6.+" + //id("io.github.juuxel.loom-vineflower") version "1.11.+" - id("com.modrinth.minotaur") version "2.+" + id("com.modrinth.minotaur") version "2.7.+" id("com.matthewprenger.cursegradle") version "1.+" id("com.github.breadmoirai.github-release") version "2.4.+" `maven-publish` - id("io.github.p03w.machete") version "1.+" + id("io.github.p03w.machete") version "2.+" } group = "dev.isxander" -version = "1.0.6" +version = "1.5.0" repositories { mavenCentral() + maven("https://api.modrinth.com/maven") { + content { + includeGroup("maven.modrinth") + } + } maven("https://jitpack.io") maven("https://maven.isxander.dev/releases") maven("https://maven.shedaniel.me") maven("https://maven.terraformersmc.com") - maven("https://maven.flashyreese.me/snapshots") + maven("https://oss.sonatype.org/content/repositories/snapshots") } val minecraftVersion: String by project @@ -34,24 +39,18 @@ dependencies { modImplementation("net.fabricmc:fabric-loader:$fabricLoaderVersion") - modImplementation("dev.isxander:yet-another-config-lib:1.5.0") - modImplementation("com.terraformersmc:modmenu:4.0.6") + modImplementation("dev.isxander:yet-another-config-lib:3.5.0+1.21-fabric") + modImplementation("com.terraformersmc:modmenu:11.0.1") - "com.github.llamalad7:mixinextras:0.0.12".let { + "io.github.llamalad7:mixinextras-fabric:0.4.0".let { implementation(it) annotationProcessor(it) include(it) } // sodium compat - modImplementation("me.jellysquid.mods:sodium-fabric:0.4.4+build.+") - - // more culling compat - modImplementation("com.github.fxmorin.MoreCulling:moreculling:v0.10.0") - "com.github.Fallen-Breath:conditional-mixin:v0.3.0".let { - modImplementation(it) - include(it) - } + modImplementation("maven.modrinth:sodium:mc1.21-0.5.11") + modRuntimeOnly("net.fabricmc.fabric-api:fabric-api:0.100.6+1.21") } java { @@ -105,15 +104,17 @@ if (modrinthId.isNotEmpty()) { versionNumber.set("${project.version}") versionType.set("release") uploadFile.set(tasks["remapJar"]) - gameVersions.set(listOf("1.19", "1.19.1", "1.19.2")) + gameVersions.set(listOf("1.20.6")) loaders.set(listOf("fabric", "quilt")) changelog.set(changelogText) syncBodyFrom.set(file("README.md").readText()) dependencies { - required.project("cloth-config") + required.project("yacl") optional.project("modmenu") } } + + tasks.getByName("modrinth").dependsOn("optimizeOutputsOfRemapJar") } val curseforgeId: String by project @@ -127,15 +128,13 @@ if (hasProperty("curseforge.token") && curseforgeId.isNotEmpty()) { id = curseforgeId releaseType = "release" - addGameVersion("1.19") - addGameVersion("1.19.1") - addGameVersion("1.19.2") + addGameVersion("1.21") addGameVersion("Fabric") addGameVersion("Quilt") - addGameVersion("Java 17") + addGameVersion("Java 21") relations(closureOf { - requiredDependency("cloth-config") + requiredDependency("yacl") optionalDependency("modmenu") }) @@ -157,11 +156,13 @@ githubRelease { owner(split[0]) repo(split[1]) tagName("${project.version}") - targetCommitish("1.19") + targetCommitish("1.21") body(changelogText) releaseAssets(tasks["remapJar"].outputs.files) } +tasks.getByName("githubRelease").dependsOn("optimizeOutputsOfRemapJar") + publishing { publications { create("mod") { @@ -170,6 +171,8 @@ publishing { from(components["java"]) } + + tasks.getByName("generateMetadataFileForModPublication").dependsOn("optimizeOutputsOfRemapJar") } repositories { diff --git a/changelogs/1.3.0.md b/changelogs/1.3.0.md new file mode 100644 index 0000000..c55cd44 --- /dev/null +++ b/changelogs/1.3.0.md @@ -0,0 +1,3 @@ +- Support Sodium 0.5 +- Add face rejection to increase performance +- Add mangrove roots culling diff --git a/gradle.properties b/gradle.properties index 392abd9..0f232fb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ org.gradle.jvmargs=-Xmx2G -minecraftVersion=1.19.2 -fabricLoaderVersion=0.14.+ +minecraftVersion=1.21 +fabricLoaderVersion=0.15.11 modId=cull-less-leaves modName=Cull Less Leaves diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 41d9927..d64cd49 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 92f06b5..a441313 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 1b6c787..1aa94a4 100755 --- a/gradlew +++ b/gradlew @@ -55,7 +55,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -80,13 +80,11 @@ do esac done -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" +# This is normally unused +# shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -133,22 +131,29 @@ location of your Java installation." fi else JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -193,11 +198,15 @@ if "$cygwin" || "$msys" ; then done fi -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ @@ -205,6 +214,12 @@ set -- \ org.gradle.wrapper.GradleWrapperMain \ "$@" +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. diff --git a/gradlew.bat b/gradlew.bat index ac1b06f..7101f8e 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -14,7 +14,7 @@ @rem limitations under the License. @rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +25,8 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,13 +41,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -56,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -75,13 +76,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +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! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +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 diff --git a/src/main/java/dev/isxander/culllessleaves/CullLessLeaves.java b/src/main/java/dev/isxander/culllessleaves/CullLessLeaves.java index 255b847..6c4fd6c 100644 --- a/src/main/java/dev/isxander/culllessleaves/CullLessLeaves.java +++ b/src/main/java/dev/isxander/culllessleaves/CullLessLeaves.java @@ -3,56 +3,45 @@ import com.llamalad7.mixinextras.MixinExtrasBootstrap; import dev.isxander.culllessleaves.compat.Compat; import dev.isxander.culllessleaves.config.CullLessLeavesConfig; -import me.shedaniel.autoconfig.AutoConfig; +import dev.isxander.culllessleaves.util.BlockConstantRandom; import net.fabricmc.api.ClientModInitializer; -import net.fabricmc.loader.api.FabricLoader; -import net.fabricmc.loader.api.Version; -import net.fabricmc.loader.api.VersionParsingException; -import net.fabricmc.loader.api.entrypoint.PreLaunchEntrypoint; -import net.fabricmc.loader.api.metadata.version.VersionComparisonOperator; -import net.minecraft.block.LeavesBlock; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Direction; +import net.minecraft.util.math.Vec3i; import net.minecraft.world.BlockView; -public class CullLessLeaves implements ClientModInitializer, PreLaunchEntrypoint { - @Override - public void onInitializeClient() { - FabricLoader.getInstance().getModContainer("moreculling").ifPresent((mod) -> { - try { - if (!VersionComparisonOperator.GREATER_EQUAL.test(mod.getMetadata().getVersion(), Version.parse("0.5.0"))) { - throw new RuntimeException("MoreCulling compatibility requires version >=0.5.0"); - } - } catch (VersionParsingException e) { - e.printStackTrace(); - } - }); - - getConfig().load(); - } +import java.util.function.Function; +public class CullLessLeaves implements ClientModInitializer { @Override - public void onPreLaunch() { - MixinExtrasBootstrap.init(); + public void onInitializeClient() { + CullLessLeavesConfig.INSTANCE.load(); } public static CullLessLeavesConfig getConfig() { - return CullLessLeavesConfig.INSTANCE; + return CullLessLeavesConfig.INSTANCE.instance(); } - public static boolean shouldCullSide(BlockView view, BlockPos pos, Direction facing) { - var depth = getConfig().depth; + public static boolean shouldCullSide(int depth, BlockView view, BlockPos pos, Direction facing, Function blockCheck) { + float rejectionChance = getConfig().randomRejection; - // if not fancy leaves, cull everything apart from outside - if (!Compat.isFancyLeaves()) - depth = 1; + Vec3i vec = facing.getVector(); + boolean cull = true; - var vec = facing.getVector(); - var cull = true; + boolean outerBlock = false; for (int i = 1; i <= depth; i++) { - var state = view.getBlockState(pos.add(vec.multiply(i))); - cull &= state != null && state.getBlock() instanceof LeavesBlock; + BlockState state = view.getBlockState(pos.add(vec.multiply(i))); + cull &= state != null && blockCheck.apply(state.getBlock()); + + if (!cull && i == 1) + outerBlock = true; } + + if (!outerBlock && !cull && BlockConstantRandom.getConstantRandomSeeded(pos) <= rejectionChance) + cull = true; + return cull; } } diff --git a/src/main/java/dev/isxander/culllessleaves/Cullable.java b/src/main/java/dev/isxander/culllessleaves/Cullable.java new file mode 100644 index 0000000..37b8bbf --- /dev/null +++ b/src/main/java/dev/isxander/culllessleaves/Cullable.java @@ -0,0 +1,10 @@ +package dev.isxander.culllessleaves; + +import net.minecraft.block.BlockState; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.world.BlockView; + +public interface Cullable { + boolean cll$shouldCullSide(BlockState state, BlockView view, BlockPos pos, Direction facing); +} diff --git a/src/main/java/dev/isxander/culllessleaves/compat/SodiumCompat.java b/src/main/java/dev/isxander/culllessleaves/compat/SodiumCompat.java index 5eb24eb..f2d2db6 100644 --- a/src/main/java/dev/isxander/culllessleaves/compat/SodiumCompat.java +++ b/src/main/java/dev/isxander/culllessleaves/compat/SodiumCompat.java @@ -4,7 +4,6 @@ import dev.isxander.culllessleaves.config.CullLessLeavesConfig; import me.jellysquid.mods.sodium.client.SodiumClientMod; import me.jellysquid.mods.sodium.client.gui.options.storage.OptionStorage; -import me.shedaniel.autoconfig.AutoConfig; import net.minecraft.client.MinecraftClient; public class SodiumCompat { @@ -16,12 +15,13 @@ public CullLessLeavesConfig getData() { @Override public void save() { - CullLessLeaves.getConfig().save(); + CullLessLeavesConfig.INSTANCE.save(); } }; public static boolean isFancyLeaves() { - return SodiumClientMod.options().quality.leavesQuality.isFancy(MinecraftClient.getInstance().options.getGraphicsMode().getValue()); + return SodiumClientMod.options().quality.leavesQuality + .isFancy(MinecraftClient.getInstance().options.getGraphicsMode().getValue()); } public static OptionStorage getOptionStorage() { diff --git a/src/main/java/dev/isxander/culllessleaves/config/CullLessLeavesConfig.java b/src/main/java/dev/isxander/culllessleaves/config/CullLessLeavesConfig.java index 553568f..4e7bd8d 100644 --- a/src/main/java/dev/isxander/culllessleaves/config/CullLessLeavesConfig.java +++ b/src/main/java/dev/isxander/culllessleaves/config/CullLessLeavesConfig.java @@ -1,92 +1,88 @@ package dev.isxander.culllessleaves.config; - -import com.google.gson.Gson; import com.google.gson.GsonBuilder; -import com.google.gson.JsonObject; -import dev.isxander.yacl.api.ConfigCategory; -import dev.isxander.yacl.api.Option; -import dev.isxander.yacl.api.OptionFlag; -import dev.isxander.yacl.api.YetAnotherConfigLib; -import dev.isxander.yacl.gui.controllers.TickBoxController; -import dev.isxander.yacl.gui.controllers.slider.IntegerSliderController; +import dev.isxander.yacl3.api.*; +import dev.isxander.yacl3.api.controller.FloatSliderControllerBuilder; +import dev.isxander.yacl3.api.controller.IntegerSliderControllerBuilder; +import dev.isxander.yacl3.api.controller.TickBoxControllerBuilder; +import dev.isxander.yacl3.config.v2.api.ConfigClassHandler; +import dev.isxander.yacl3.config.v2.api.SerialEntry; +import dev.isxander.yacl3.config.v2.api.serializer.GsonConfigSerializerBuilder; import net.fabricmc.loader.api.FabricLoader; import net.minecraft.client.gui.screen.Screen; import net.minecraft.text.Text; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; +import java.util.function.Function; public class CullLessLeavesConfig { - public static final CullLessLeavesConfig INSTANCE = new CullLessLeavesConfig(); + private static final Function PERCENT_FORMATTER = value -> Text.literal(String.format("%.0f%%", value * 100)); - public final Path configFile = FabricLoader.getInstance().getConfigDir().resolve("cull-less-leaves.json"); - private final Gson gson = new GsonBuilder().setPrettyPrinting().create(); + public static final ConfigClassHandler INSTANCE = ConfigClassHandler.createBuilder(CullLessLeavesConfig.class) + .serializer(config -> GsonConfigSerializerBuilder.create(config) + .setPath(FabricLoader.getInstance().getConfigDir().resolve("cull-less-leaves.json")) + .overrideGsonBuilder(new GsonBuilder().setPrettyPrinting()) + .build()) + .build(); + @SerialEntry public boolean enabled = true; + @SerialEntry public int depth = 2; + @SerialEntry + public float randomRejection = 0.2f; - public void save() { - try { - Files.deleteIfExists(configFile); - - JsonObject json = new JsonObject(); - json.addProperty("enabled", enabled); - json.addProperty("depth", depth); - - Files.writeString(configFile, gson.toJson(json)); - } catch (IOException e) { - e.printStackTrace(); - } - } - - public void load() { - try { - if (Files.notExists(configFile)) { - save(); - return; - } - - JsonObject json = gson.fromJson(Files.readString(configFile), JsonObject.class); - - if (json.has("enabled")) - enabled = json.getAsJsonPrimitive("enabled").getAsBoolean(); - if (json.has("depth")) - depth = json.getAsJsonPrimitive("depth").getAsInt(); - } catch (IOException e) { - e.printStackTrace(); - } - } + @SerialEntry + public boolean fastMangroveRootsCulling = false; - public Screen makeScreen(Screen parent) { - return YetAnotherConfigLib.createBuilder() - .title(Text.translatable("cull-less-leaves.title")) - .category(ConfigCategory.createBuilder() - .name(Text.translatable("cull-less-leaves.title")) - .option(Option.createBuilder(boolean.class) - .name(Text.translatable("cull-less-leaves.option.enabled")) - .binding( - true, - () -> enabled, - value -> enabled = value - ) - .controller(TickBoxController::new) - .build()) - .option(Option.createBuilder(int.class) - .name(Text.translatable("cull-less-leaves.option.depth")) - .tooltip(Text.translatable("cull-less-leaves.option.depth.tooltip")) - .binding( - 2, - () -> depth, - value -> depth = value - ) - .controller(yacl -> new IntegerSliderController(yacl, 1, 4, 1)) - .flag(OptionFlag.RELOAD_CHUNKS) - .build()) - .build()) - .save(this::save) - .build() + public static Screen makeScreen(Screen parent) { + return YetAnotherConfigLib.create(INSTANCE, (defaults, config, builder) -> builder + .title(Text.translatable("cull-less-leaves.title")) + .category(ConfigCategory.createBuilder() + .name(Text.translatable("cull-less-leaves.title")) + .option(Option.createBuilder() + .name(Text.translatable("cull-less-leaves.option.enabled")) + .binding( + defaults.enabled, + () -> config.enabled, + value -> config.enabled = value + ) + .controller(TickBoxControllerBuilder::create) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("cull-less-leaves.option.depth")) + .description(OptionDescription.of(Text.translatable("cull-less-leaves.option.depth.tooltip"))) + .binding( + defaults.depth, + () -> config.depth, + v -> config.depth = v + ) + .controller(opt -> IntegerSliderControllerBuilder.create(opt) + .range(1, 4) + .step(1)) + .flag(OptionFlag.RELOAD_CHUNKS) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("cull-less-leaves.option.random_rejection")) + .description(OptionDescription.of(Text.translatable("cull-less-leaves.option.random_rejection.tooltip"))) + .binding(defaults.randomRejection, () -> config.randomRejection, v -> config.randomRejection = v) + .controller(opt -> FloatSliderControllerBuilder.create(opt) + .range(0f, 1f) + .step(0.1f) + .formatValue(PERCENT_FORMATTER::apply)) + .flag(OptionFlag.RELOAD_CHUNKS) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("cull-less-leaves.option.fast_mangrove_roots_culling")) + .description(OptionDescription.of(Text.translatable("cull-less-leaves.option.fast_mangrove_roots_culling.tooltip"))) + .binding( + defaults.fastMangroveRootsCulling, + () -> config.fastMangroveRootsCulling, + value -> config.fastMangroveRootsCulling = value + ) + .controller(TickBoxControllerBuilder::create) + .flag(OptionFlag.RELOAD_CHUNKS) + .build()) + .build())) .generateScreen(parent); } } diff --git a/src/main/java/dev/isxander/culllessleaves/integrations/ModMenuIntegration.java b/src/main/java/dev/isxander/culllessleaves/integrations/ModMenuIntegration.java index d98f956..63611ed 100644 --- a/src/main/java/dev/isxander/culllessleaves/integrations/ModMenuIntegration.java +++ b/src/main/java/dev/isxander/culllessleaves/integrations/ModMenuIntegration.java @@ -4,11 +4,10 @@ import com.terraformersmc.modmenu.api.ModMenuApi; import dev.isxander.culllessleaves.CullLessLeaves; import dev.isxander.culllessleaves.config.CullLessLeavesConfig; -import me.shedaniel.autoconfig.AutoConfig; public class ModMenuIntegration implements ModMenuApi { @Override public ConfigScreenFactory getModConfigScreenFactory() { - return parent -> CullLessLeaves.getConfig().makeScreen(parent); + return CullLessLeavesConfig::makeScreen; } } diff --git a/src/main/java/dev/isxander/culllessleaves/mixins/BlockMixin.java b/src/main/java/dev/isxander/culllessleaves/mixins/BlockMixin.java index 025bdac..11e1794 100644 --- a/src/main/java/dev/isxander/culllessleaves/mixins/BlockMixin.java +++ b/src/main/java/dev/isxander/culllessleaves/mixins/BlockMixin.java @@ -2,8 +2,7 @@ import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import dev.isxander.culllessleaves.CullLessLeaves; -import me.fallenbreath.conditionalmixin.api.annotation.Condition; -import me.fallenbreath.conditionalmixin.api.annotation.Restriction; +import dev.isxander.culllessleaves.Cullable; import net.minecraft.block.*; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Direction; @@ -11,14 +10,13 @@ import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; -@Restriction(conflict = { @Condition("moreculling"), @Condition("sodium") }) @Mixin(Block.class) public class BlockMixin { @ModifyExpressionValue(method = "shouldDrawSide", at = @At(value = "INVOKE", target = "Lnet/minecraft/block/BlockState;isSideInvisible(Lnet/minecraft/block/BlockState;Lnet/minecraft/util/math/Direction;)Z")) private static boolean shouldCullLeafSide(boolean isSideInvisible, BlockState state, BlockView world, BlockPos pos, Direction side, BlockPos blockPos) { - if (!(state.getBlock() instanceof LeavesBlock) || !CullLessLeaves.getConfig().enabled) - return isSideInvisible; - - return CullLessLeaves.shouldCullSide(world, pos, side); + if (CullLessLeaves.getConfig().enabled && state.getBlock() instanceof Cullable cullable) { + return isSideInvisible || cullable.cll$shouldCullSide(state, world, pos, side); + } + return isSideInvisible; } } diff --git a/src/main/java/dev/isxander/culllessleaves/mixins/blocks/LeavesBlockMixin.java b/src/main/java/dev/isxander/culllessleaves/mixins/blocks/LeavesBlockMixin.java new file mode 100644 index 0000000..dab4a52 --- /dev/null +++ b/src/main/java/dev/isxander/culllessleaves/mixins/blocks/LeavesBlockMixin.java @@ -0,0 +1,25 @@ +package dev.isxander.culllessleaves.mixins.blocks; + +import dev.isxander.culllessleaves.CullLessLeaves; +import dev.isxander.culllessleaves.Cullable; +import dev.isxander.culllessleaves.compat.Compat; +import net.minecraft.block.BlockState; +import net.minecraft.block.LeavesBlock; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.world.BlockView; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(LeavesBlock.class) +public class LeavesBlockMixin implements Cullable { + @Override + public boolean cll$shouldCullSide(BlockState state, BlockView view, BlockPos pos, Direction facing) { + return CullLessLeaves.shouldCullSide( + Compat.isFancyLeaves() ? CullLessLeaves.getConfig().depth : 1, + view, + pos, + facing, + block -> block instanceof LeavesBlock + ); + } +} diff --git a/src/main/java/dev/isxander/culllessleaves/mixins/blocks/MangroveRootsBlockMixin.java b/src/main/java/dev/isxander/culllessleaves/mixins/blocks/MangroveRootsBlockMixin.java new file mode 100644 index 0000000..a47e254 --- /dev/null +++ b/src/main/java/dev/isxander/culllessleaves/mixins/blocks/MangroveRootsBlockMixin.java @@ -0,0 +1,24 @@ +package dev.isxander.culllessleaves.mixins.blocks; + +import dev.isxander.culllessleaves.CullLessLeaves; +import dev.isxander.culllessleaves.Cullable; +import net.minecraft.block.BlockState; +import net.minecraft.block.MangroveRootsBlock; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.world.BlockView; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(MangroveRootsBlock.class) +public class MangroveRootsBlockMixin implements Cullable { + @Override + public boolean cll$shouldCullSide(BlockState state, BlockView view, BlockPos pos, Direction facing) { + return CullLessLeaves.getConfig().fastMangroveRootsCulling && CullLessLeaves.shouldCullSide( + 1, + view, + pos, + facing, + block -> block instanceof MangroveRootsBlock + ); + } +} diff --git a/src/main/java/dev/isxander/culllessleaves/mixins/morecullingcompat/LeavesBlockMixin.java b/src/main/java/dev/isxander/culllessleaves/mixins/morecullingcompat/LeavesBlockMixin.java deleted file mode 100644 index 5c85bf8..0000000 --- a/src/main/java/dev/isxander/culllessleaves/mixins/morecullingcompat/LeavesBlockMixin.java +++ /dev/null @@ -1,31 +0,0 @@ -package dev.isxander.culllessleaves.mixins.morecullingcompat; - -import ca.fxco.moreculling.api.block.MoreBlockCulling; -import dev.isxander.culllessleaves.CullLessLeaves; -import me.fallenbreath.conditionalmixin.api.annotation.Condition; -import me.fallenbreath.conditionalmixin.api.annotation.Restriction; -import net.minecraft.block.BlockState; -import net.minecraft.block.LeavesBlock; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Direction; -import net.minecraft.world.BlockView; -import org.spongepowered.asm.mixin.Mixin; - -import java.util.Optional; - -@Restriction(require = @Condition("moreculling")) -@Mixin(LeavesBlock.class) -public class LeavesBlockMixin implements MoreBlockCulling { - @Override - public boolean usesCustomShouldDrawFace(BlockState state) { - return CullLessLeaves.getConfig().enabled; - } - - @Override - public Optional customShouldDrawFace(BlockView view, BlockState thisState, BlockState sideState, BlockPos thisPos, BlockPos sidePos, Direction side) { - if (CullLessLeaves.shouldCullSide(view, thisPos, side)) - return Optional.of(false); - else - return Optional.empty(); - } -} diff --git a/src/main/java/dev/isxander/culllessleaves/mixins/sodiumcompat/BlockOcclusionCacheMixin.java b/src/main/java/dev/isxander/culllessleaves/mixins/sodiumcompat/BlockOcclusionCacheMixin.java index 4c992af..dbed706 100644 --- a/src/main/java/dev/isxander/culllessleaves/mixins/sodiumcompat/BlockOcclusionCacheMixin.java +++ b/src/main/java/dev/isxander/culllessleaves/mixins/sodiumcompat/BlockOcclusionCacheMixin.java @@ -2,18 +2,17 @@ import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import dev.isxander.culllessleaves.CullLessLeaves; -import me.fallenbreath.conditionalmixin.api.annotation.Condition; -import me.fallenbreath.conditionalmixin.api.annotation.Restriction; -import me.jellysquid.mods.sodium.client.render.occlusion.BlockOcclusionCache; +import dev.isxander.culllessleaves.Cullable; +import me.jellysquid.mods.sodium.client.render.chunk.compile.pipeline.BlockOcclusionCache; import net.minecraft.block.BlockState; -import net.minecraft.block.LeavesBlock; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Direction; import net.minecraft.world.BlockView; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Pseudo; import org.spongepowered.asm.mixin.injection.At; -@Restriction(require = @Condition("sodium")) +@Pseudo @Mixin(BlockOcclusionCache.class) public class BlockOcclusionCacheMixin { /** @@ -22,9 +21,9 @@ public class BlockOcclusionCacheMixin { */ @ModifyExpressionValue(method = "shouldDrawSide", at = @At(value = "INVOKE", target = "Lnet/minecraft/block/BlockState;isSideInvisible(Lnet/minecraft/block/BlockState;Lnet/minecraft/util/math/Direction;)Z")) private boolean shouldCullSide(boolean isSideInvisible, BlockState state, BlockView view, BlockPos pos, Direction facing) { - if (!(state.getBlock() instanceof LeavesBlock) || !CullLessLeaves.getConfig().enabled) - return isSideInvisible; - - return CullLessLeaves.shouldCullSide(view, pos, facing); + if (CullLessLeaves.getConfig().enabled && state.getBlock() instanceof Cullable cullable) { + return isSideInvisible || cullable.cll$shouldCullSide(state, view, pos, facing); + } + return isSideInvisible; } } diff --git a/src/main/java/dev/isxander/culllessleaves/mixins/sodiumcompat/LeavesBlockMixin.java b/src/main/java/dev/isxander/culllessleaves/mixins/sodiumcompat/LeavesBlockMixin.java index e14ecd8..6398b02 100644 --- a/src/main/java/dev/isxander/culllessleaves/mixins/sodiumcompat/LeavesBlockMixin.java +++ b/src/main/java/dev/isxander/culllessleaves/mixins/sodiumcompat/LeavesBlockMixin.java @@ -1,14 +1,9 @@ package dev.isxander.culllessleaves.mixins.sodiumcompat; -import me.fallenbreath.conditionalmixin.api.annotation.Condition; -import me.fallenbreath.conditionalmixin.api.annotation.Restriction; import net.minecraft.block.Block; -import net.minecraft.block.BlockState; import net.minecraft.block.LeavesBlock; -import net.minecraft.util.math.Direction; import org.spongepowered.asm.mixin.Mixin; -@Restriction(require = @Condition("sodium"), conflict = @Condition("moreculling")) @Mixin(value = LeavesBlock.class, priority = 1100) public class LeavesBlockMixin extends Block { public LeavesBlockMixin(Settings settings) { @@ -22,9 +17,9 @@ public LeavesBlockMixin(Settings settings) { * this mixin simply reverts to vanilla behaviour * @see me.jellysquid.mods.sodium.mixin.features.render_layer.leaves.MixinLeavesBlock */ - @Override - @SuppressWarnings("deprecation") - public boolean isSideInvisible(BlockState state, BlockState stateFrom, Direction direction) { - return super.isSideInvisible(state, stateFrom, direction); - } +// @Override +// @SuppressWarnings("deprecation") +// public boolean isSideInvisible(BlockState state, BlockState stateFrom, Direction direction) { +// return super.isSideInvisible(state, stateFrom, direction); +// } } diff --git a/src/main/java/dev/isxander/culllessleaves/mixins/sodiumcompat/SodiumGameOptionPagesMixin.java b/src/main/java/dev/isxander/culllessleaves/mixins/sodiumcompat/SodiumGameOptionPagesMixin.java index 57d8601..00638bb 100644 --- a/src/main/java/dev/isxander/culllessleaves/mixins/sodiumcompat/SodiumGameOptionPagesMixin.java +++ b/src/main/java/dev/isxander/culllessleaves/mixins/sodiumcompat/SodiumGameOptionPagesMixin.java @@ -1,8 +1,6 @@ package dev.isxander.culllessleaves.mixins.sodiumcompat; import dev.isxander.culllessleaves.compat.SodiumCompat; -import me.fallenbreath.conditionalmixin.api.annotation.Condition; -import me.fallenbreath.conditionalmixin.api.annotation.Restriction; import me.jellysquid.mods.sodium.client.gui.SodiumGameOptionPages; import me.jellysquid.mods.sodium.client.gui.options.*; import me.jellysquid.mods.sodium.client.gui.options.control.ControlValueFormatter; @@ -10,12 +8,13 @@ import me.jellysquid.mods.sodium.client.gui.options.control.TickBoxControl; import net.minecraft.text.Text; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Pseudo; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.ModifyVariable; import java.util.List; -@Restriction(require = @Condition("sodium")) +@Pseudo @Mixin(value = SodiumGameOptionPages.class, remap = false) public class SodiumGameOptionPagesMixin { @ModifyVariable(method = "performance", at = @At(value = "INVOKE", target = "Lcom/google/common/collect/ImmutableList;copyOf(Ljava/util/Collection;)Lcom/google/common/collect/ImmutableList;")) @@ -39,6 +38,24 @@ private static List addLeavesCulling(List groups) { .setFlags(OptionFlag.REQUIRES_RENDERER_RELOAD) .build() ) + .add(OptionImpl.createBuilder(int.class, SodiumCompat.getOptionStorage()) + .setName(Text.translatable("cull-less-leaves.option.random_rejection")) + .setTooltip(Text.translatable("cull-less-leaves.option.random_rejection.tooltip")) + .setControl(o -> new SliderControl(o, 0, 100, 10, ControlValueFormatter.percentage())) + .setBinding((opts, value) -> opts.randomRejection = value / 100f, opts -> (int)(opts.randomRejection * 100f)) + .setImpact(OptionImpact.LOW) + .setFlags(OptionFlag.REQUIRES_RENDERER_RELOAD) + .build() + ) + .add(OptionImpl.createBuilder(boolean.class, SodiumCompat.getOptionStorage()) + .setName(Text.translatable("cull-less-leaves.option.fast_mangrove_roots_culling")) + .setTooltip(Text.translatable("cull-less-leaves.option.fast_mangrove_roots_culling.tooltip")) + .setControl(TickBoxControl::new) + .setBinding((opts, value) -> opts.fastMangroveRootsCulling = value, opts -> opts.fastMangroveRootsCulling) + .setImpact(OptionImpact.LOW) + .setFlags(OptionFlag.REQUIRES_RENDERER_RELOAD) + .build() + ) .build() ); diff --git a/src/main/java/dev/isxander/culllessleaves/util/BlockConstantRandom.java b/src/main/java/dev/isxander/culllessleaves/util/BlockConstantRandom.java new file mode 100644 index 0000000..a52ae8c --- /dev/null +++ b/src/main/java/dev/isxander/culllessleaves/util/BlockConstantRandom.java @@ -0,0 +1,19 @@ +package dev.isxander.culllessleaves.util; + +import net.minecraft.util.math.BlockPos; + +public class BlockConstantRandom { + // GitHub Copilot suggested this code whilst writing the name of the method lol + public static float getConstantRandomHashed(BlockPos pos) { + int i = pos.getX() * 3129871 ^ pos.getZ() * 116129781 ^ pos.getY(); + i = i * i * 42317861 + i * 11; + return ((float) ((i >> 16 & 15) + 0.5D) / 16.0F); + } + + // This code is like setting up a random instance seeded with the block position + public static float getConstantRandomSeeded(BlockPos pos) { + long seed = (pos.asLong() ^ 25214903917L) & 281474976710655L; + seed = seed * 25214903917L + 11L & 281474976710655L; + return ((int) (seed >> 48 - 24)) * 5.9604645E-8F; + } +} diff --git a/src/main/java/dev/isxander/culllessleaves/utils/MixinPlugin.java b/src/main/java/dev/isxander/culllessleaves/utils/MixinPlugin.java deleted file mode 100644 index 9611b2d..0000000 --- a/src/main/java/dev/isxander/culllessleaves/utils/MixinPlugin.java +++ /dev/null @@ -1,23 +0,0 @@ -package dev.isxander.culllessleaves.utils; - -import me.fallenbreath.conditionalmixin.api.mixin.RestrictiveMixinConfigPlugin; - -import java.util.List; -import java.util.Set; - -public class MixinPlugin extends RestrictiveMixinConfigPlugin { - @Override - public String getRefMapperConfig() { - return null; - } - - @Override - public void acceptTargets(Set myTargets, Set otherTargets) { - - } - - @Override - public List getMixins() { - return null; - } -} diff --git a/src/main/resources/assets/cull-less-leaves/lang/en_us.json b/src/main/resources/assets/cull-less-leaves/lang/en_us.json index 4cf6a0d..4696678 100644 --- a/src/main/resources/assets/cull-less-leaves/lang/en_us.json +++ b/src/main/resources/assets/cull-less-leaves/lang/en_us.json @@ -5,5 +5,9 @@ "cull-less-leaves.option.depth.tooltip": "Amount of layers of leaves before the inside of the leaves is culled.\n1 = Fastest but least good-looking.\n2 = Recommended.\n4 = Slowest but most good-looking.", "cull-less-leaves.sodium.option.enabled": "Cull Leaves", "cull-less-leaves.sodium.option.enabled.description": "Enables Cull Less Leaves mod.", - "cull-less-leaves.sodium.option.depth": "Cull Leaves Depth" + "cull-less-leaves.sodium.option.depth": "Cull Leaves Depth", + "cull-less-leaves.option.random_rejection": "Random Rejection", + "cull-less-leaves.option.random_rejection.tooltip": "Reject a percentage of inner-layer leaves inside of the accepted depth range randomly.", + "cull-less-leaves.option.fast_mangrove_roots_culling": "Fast Mangrove Roots Culling", + "cull-less-leaves.option.fast_mangrove_roots_culling.tooltip": "Does a depth-1 cull on mangrove roots, any more is useless as roots generate in single layers. This doesn't do much." } diff --git a/src/main/resources/assets/cull-less-leaves/lang/it_it.json b/src/main/resources/assets/cull-less-leaves/lang/it_it.json index f275156..7ae6ddf 100644 --- a/src/main/resources/assets/cull-less-leaves/lang/it_it.json +++ b/src/main/resources/assets/cull-less-leaves/lang/it_it.json @@ -1,9 +1,9 @@ { "cull-less-leaves.title": "Cull Less Leaves", "cull-less-leaves.option.enabled": "Attivo", - "cull-less-leaves.option.depth": "Spessore", + "cull-less-leaves.option.depth": "Profondità", "cull-less-leaves.option.depth.tooltip": "Quantità di strati di foglie prima che l'interno delle foglie venga eliminato.\n1 = Più veloce ma meno bello.\n2 = Consigliato.\n4 = Più lento ma più bello.", "cull-less-leaves.sodium.option.enabled": "Culling delle foglie", "cull-less-leaves.sodium.option.enabled.description": "Attiva la mod Cull Less Leaves.", - "cull-less-leaves.sodium.option.depth": "Spessore del culling delle foglie" + "cull-less-leaves.sodium.option.depth": "Profondità del culling delle foglie" } diff --git a/src/main/resources/cull-less-leaves.mixins.json b/src/main/resources/cull-less-leaves.mixins.json index 26e1e20..65dbd10 100644 --- a/src/main/resources/cull-less-leaves.mixins.json +++ b/src/main/resources/cull-less-leaves.mixins.json @@ -2,13 +2,16 @@ "package": "dev.isxander.culllessleaves.mixins", "required": true, "minVersion": "0.8", - "compatibilityLevel": "JAVA_17", + "compatibilityLevel": "JAVA_21", "client": [ + "BlockMixin", "sodiumcompat.BlockOcclusionCacheMixin", "sodiumcompat.LeavesBlockMixin", - "sodiumcompat.SodiumGameOptionPagesMixin", - "morecullingcompat.LeavesBlockMixin" + "sodiumcompat.SodiumGameOptionPagesMixin" ], - "plugin": "dev.isxander.culllessleaves.utils.MixinPlugin" + "mixins": [ + "blocks.LeavesBlockMixin", + "blocks.MangroveRootsBlockMixin" + ] } diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 5da0c25..0c6a572 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -16,9 +16,6 @@ "environment": "client", "icon": "cull-less-leaves.png", "entrypoints": { - "preLaunch": [ - "dev.isxander.culllessleaves.CullLessLeaves" - ], "client": [ "dev.isxander.culllessleaves.CullLessLeaves" ], @@ -30,10 +27,10 @@ "cull-less-leaves.mixins.json" ], "depends": { - "fabricloader": ">=0.14.0", - "minecraft": "1.19.x", - "java": ">=17", - "yet-another-config-lib": ">=1.4.0" + "fabricloader": ">=0.15.11", + "minecraft": "1.21", + "java": ">=21", + "yet_another_config_lib_v3": "*" }, "suggests": { "sodium": "*"