Many QoL improvements & Resourcepack features

- Support Sodium 0.6+
- Allow resourcepacks to force culling and hide fully-encircled leaf blocks
- Force leaf culling when the smart leaves pack is enabled
- Reload world when the config changes
- Only apply Sodium mixins when Sodium is actually installed (Removes log spam)
This commit is contained in:
Martin Prokoph
2024-12-24 14:01:05 +01:00
parent 1ad05aa6bf
commit b8d2e6b4ab
22 changed files with 343 additions and 46 deletions

View File

@@ -12,8 +12,6 @@ dependencies {
// Do NOT use other classes from fabric loader
modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}"
modCompileOnlyApi "maven.modrinth:midnightlib:${rootProject.midnightlib_version}-fabric"
modCompileOnlyApi "maven.modrinth:sodium:${rootProject.sodium_version}"
// Remove the next line if you don't want to depend on the API
//modApi "dev.architectury:architectury:${rootProject.architectury_version}"
}

View File

@@ -0,0 +1,78 @@
package eu.midnightdust.cullleaves;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import eu.midnightdust.cullleaves.config.CullLeavesConfig;
import net.minecraft.block.BlockState;
import net.minecraft.block.LeavesBlock;
import net.minecraft.block.MangroveRootsBlock;
import net.minecraft.resource.ResourceManager;
import net.minecraft.resource.SynchronousResourceReloader;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.world.BlockView;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
public class CullLeavesClient {
public static final String MOD_ID = "cullleaves";
public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID);
public static boolean forceLeafCulling = false;
public static boolean forceHideInnerLeaves = false;
public static boolean shouldHideBlock(BlockView world, BlockPos pos) {
if (CullLeavesClient.forceHideInnerLeaves) {
boolean shouldForceCull = true;
for (Direction dir : Direction.values()) {
BlockState otherState = world.getBlockState(pos.offset(dir));
if (!(otherState.getBlock() instanceof LeavesBlock) &&
!otherState.isSideSolidFullSquare(world, pos, dir.getOpposite())) {
shouldForceCull = false;
break;
}
}
return shouldForceCull;
}
return false;
}
public static boolean isLeafSideInvisible(BlockState neighborState) {
if (CullLeavesConfig.enabled || CullLeavesClient.forceLeafCulling) {
return neighborState.getBlock() instanceof LeavesBlock;
}
else return false;
}
public static boolean isRootSideInvisible(BlockState neighborState) {
if (CullLeavesConfig.cullRoots) {
return neighborState.getBlock() instanceof MangroveRootsBlock;
}
else return false;
}
public static class ReloadListener implements SynchronousResourceReloader {
public static final ReloadListener INSTANCE = new ReloadListener();
private ReloadListener() {}
@Override
public void reload(ResourceManager manager) {
CullLeavesClient.forceLeafCulling = false;
CullLeavesClient.forceHideInnerLeaves = false;
manager.findResources("options", path -> path.toString().startsWith("cullleaves") && path.toString().endsWith("options.json")).forEach((id, resource) -> {
try {
JsonObject json = JsonParser.parseReader(resource.getReader()).getAsJsonObject();
if (json.has("forceLeafCulling")) {
CullLeavesClient.forceLeafCulling = json.get("forceLeafCulling").getAsBoolean();
LOGGER.info("Forcing leaf culling as requested by resourcepack");
}
if (json.has("forceHideInnerLeaves")) {
CullLeavesClient.forceHideInnerLeaves = json.get("forceHideInnerLeaves").getAsBoolean();
LOGGER.info("Not rendering inner leaves as requested by resourcepack");
}
} catch (IOException e) { throw new RuntimeException(e); }
});
}
}
}

View File

@@ -0,0 +1,32 @@
package eu.midnightdust.cullleaves;
import eu.midnightdust.lib.util.PlatformFunctions;
import org.objectweb.asm.tree.ClassNode;
import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin;
import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
import java.util.List;
import java.util.Set;
public class CullLeavesMixinPlugin implements IMixinConfigPlugin {
private String mixinPackage;
@Override
public void onLoad(String mixinPackage) {
this.mixinPackage = mixinPackage + ".";
}
@Override
public boolean shouldApplyMixin(String targetClassName, String mixinClassName) {
final String mixinName = mixinClassName.substring(this.mixinPackage.length());
if (mixinName.indexOf('.') < 0) return true;
return PlatformFunctions.isModLoaded(mixinName.substring(0, mixinName.indexOf('.')));
}
@Override public List<String> getMixins() { return null; }
@Override public String getRefMapperConfig() { return null; }
@Override public void acceptTargets(Set<String> myTargets, Set<String> otherTargets) {}
@Override public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) {}
@Override public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) {}
}

View File

@@ -1,10 +1,17 @@
package eu.midnightdust.cullleaves.config;
import eu.midnightdust.lib.config.MidnightConfig;
import net.minecraft.client.MinecraftClient;
public class CullLeavesConfig extends MidnightConfig {
@Entry // Enable/Disable the mod. Requires Chunk Reload (F3 + A).
public static boolean enabled = true;
@Entry // Enable/Disable culling on Mangrove Roots.
public static boolean cullRoots = true;
@Override
public void writeChanges(String modid) {
MinecraftClient.getInstance().worldRenderer.reload();
super.writeChanges(modid);
}
}

View File

@@ -0,0 +1,25 @@
package eu.midnightdust.cullleaves.mixin;
import eu.midnightdust.cullleaves.CullLeavesClient;
import net.minecraft.block.BlockState;
import net.minecraft.block.LeavesBlock;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.render.block.BlockModelRenderer;
import net.minecraft.client.render.model.BakedModel;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.random.Random;
import net.minecraft.world.BlockRenderView;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(BlockModelRenderer.class)
public class MixinBlockModelRenderer {
@Inject(at = @At("HEAD"), method = "render(Lnet/minecraft/world/BlockRenderView;Lnet/minecraft/client/render/model/BakedModel;Lnet/minecraft/block/BlockState;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumer;ZLnet/minecraft/util/math/random/Random;JI)V", cancellable = true)
private void cullleaves$cancelRendering(BlockRenderView world, BakedModel model, BlockState state, BlockPos pos, MatrixStack matrices, VertexConsumer vertexConsumer, boolean cull, Random random, long seed, int overlay, CallbackInfo ci) {
if (state.getBlock() instanceof LeavesBlock &&
CullLeavesClient.shouldHideBlock(world, pos)) ci.cancel();
}
}

View File

@@ -1,6 +1,6 @@
package eu.midnightdust.cullleaves.mixin;
import eu.midnightdust.cullleaves.config.CullLeavesConfig;
import eu.midnightdust.cullleaves.CullLeavesClient;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.block.Block;
@@ -18,11 +18,7 @@ public abstract class MixinLeavesBlock extends Block {
}
@Override
@SuppressWarnings("deprecation")
public boolean isSideInvisible(BlockState state, BlockState neighborState, Direction offset) {
if (CullLeavesConfig.enabled) {
return neighborState.getBlock() instanceof LeavesBlock;
}
else return false;
return CullLeavesClient.isLeafSideInvisible(neighborState);
}
}

View File

@@ -1,6 +1,6 @@
package eu.midnightdust.cullleaves.mixin;
import eu.midnightdust.cullleaves.config.CullLeavesConfig;
import eu.midnightdust.cullleaves.CullLeavesClient;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.block.*;
@@ -16,11 +16,7 @@ public abstract class MixinMangroveRootsBlock extends Block {
}
@Override
@SuppressWarnings("deprecation")
public boolean isSideInvisible(BlockState state, BlockState neighborState, Direction offset) {
if (CullLeavesConfig.cullRoots) {
return neighborState.getBlock() instanceof MangroveRootsBlock;
}
else return false;
return CullLeavesClient.isRootSideInvisible(neighborState);
}
}

View File

@@ -1,12 +1,13 @@
{
"required": true,
"package": "eu.midnightdust.cullleaves.mixin",
"plugin": "eu.midnightdust.cullleaves.CullLeavesMixinPlugin",
"compatibilityLevel": "JAVA_17",
"minVersion": "0.8",
"client": [
"MixinBlockModelRenderer",
"MixinLeavesBlock",
"MixinMangroveRootsBlock",
"MixinSodiumGameOptionPages"
"MixinMangroveRootsBlock"
],
"injectors": {
"defaultRequire": 1

View File

@@ -0,0 +1,3 @@
{
"forceLeafCulling": true
}

View File

@@ -9,8 +9,6 @@ architectury {
fabric()
}
loom {
}
repositories {
maven { url "https://api.modrinth.com/maven" }
}
@@ -29,9 +27,8 @@ dependencies {
modApi "net.fabricmc.fabric-api:fabric-api:${rootProject.fabric_api_version}"
// Remove the next line if you don't want to depend on the API
//modApi "dev.architectury:architectury-fabric:${rootProject.architectury_version}"
modImplementation "maven.modrinth:midnightlib:${rootProject.midnightlib_version}-fabric"
include "maven.modrinth:midnightlib:${rootProject.midnightlib_version}-fabric"
modRuntimeOnly "maven.modrinth:sodium:${rootProject.sodium_version}"
modImplementation include ("maven.modrinth:midnightlib:${rootProject.midnightlib_version}-fabric")
modImplementation "maven.modrinth:sodium:${rootProject.sodium_version}-fabric"
common(project(path: ":common", configuration: "namedElements")) { transitive false }
shadowCommon(project(path: ":common", configuration: "transformProductionFabric")) { transitive false }

View File

@@ -1,19 +1,33 @@
package eu.midnightdust.cullleaves.fabric;
import eu.midnightdust.cullleaves.CullLeavesClient;
import eu.midnightdust.cullleaves.config.CullLeavesConfig;
import eu.midnightdust.lib.config.MidnightConfig;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.resource.ResourceManagerHelper;
import net.fabricmc.fabric.api.resource.ResourcePackActivationType;
import net.fabricmc.fabric.api.resource.SimpleSynchronousResourceReloadListener;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.resource.ResourceManager;
import net.minecraft.resource.ResourceType;
import net.minecraft.util.Identifier;
public class CullLeavesClientFabric implements ClientModInitializer {
@Override
public void onInitializeClient() {
MidnightConfig.init("cullleaves", CullLeavesConfig.class);
MidnightConfig.init(CullLeavesClient.MOD_ID, CullLeavesConfig.class);
FabricLoader.getInstance().getModContainer("cullleaves").ifPresent(modContainer -> {
ResourceManagerHelper.registerBuiltinResourcePack(Identifier.of("cullleaves:smartleaves"), modContainer, ResourcePackActivationType.NORMAL);
ResourceManagerHelper.registerBuiltinResourcePack(Identifier.of(CullLeavesClient.MOD_ID,"smartleaves"), modContainer, ResourcePackActivationType.NORMAL);
});
ResourceManagerHelper.get(ResourceType.CLIENT_RESOURCES).registerReloadListener(new SimpleSynchronousResourceReloadListener() {
@Override
public Identifier getFabricId() {
return Identifier.of(CullLeavesClient.MOD_ID, "resourcepack_options");
}
@Override
public void reload(ResourceManager manager) {
CullLeavesClient.ReloadListener.INSTANCE.reload(manager);
}
});
}
}

View File

@@ -0,0 +1,22 @@
package eu.midnightdust.cullleaves.fabric.mixin.sodium;
import eu.midnightdust.cullleaves.CullLeavesClient;
import net.caffeinemc.mods.sodium.client.render.chunk.compile.pipeline.BlockRenderer;
import net.caffeinemc.mods.sodium.client.render.frapi.render.AbstractBlockRenderContext;
import net.minecraft.block.BlockState;
import net.minecraft.block.LeavesBlock;
import net.minecraft.client.render.model.BakedModel;
import net.minecraft.util.math.BlockPos;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(BlockRenderer.class)
public abstract class MixinBlockRenderer extends AbstractBlockRenderContext {
@Inject(at = @At("HEAD"), method = "renderModel", cancellable = true)
public void cullleaves$cancelRendering(BakedModel model, BlockState state, BlockPos pos, BlockPos origin, CallbackInfo ci) {
if (state.getBlock() instanceof LeavesBlock && CullLeavesClient.shouldHideBlock(this.level, pos))
ci.cancel();
}
}

View File

@@ -0,0 +1,55 @@
package eu.midnightdust.cullleaves.fabric.mixin.sodium;
import eu.midnightdust.cullleaves.config.CullLeavesConfig;
import net.caffeinemc.mods.sodium.client.gui.options.OptionFlag;
import net.caffeinemc.mods.sodium.client.gui.options.OptionGroup;
import net.caffeinemc.mods.sodium.client.gui.options.OptionImpact;
import net.caffeinemc.mods.sodium.client.gui.options.OptionImpl;
import net.caffeinemc.mods.sodium.client.gui.options.control.TickBoxControl;
import net.caffeinemc.mods.sodium.client.gui.options.storage.SodiumOptionsStorage;
import net.minecraft.text.Text;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyVariable;
import java.util.List;
@Mixin(value = net.caffeinemc.mods.sodium.client.gui.SodiumGameOptionPages.class, remap = false)
public class MixinSodiumGameOptionPages {
@Shadow @Final private static SodiumOptionsStorage sodiumOpts;
@ModifyVariable(method = "performance", at = @At(value = "INVOKE", target = "Lcom/google/common/collect/ImmutableList;copyOf(Ljava/util/Collection;)Lcom/google/common/collect/ImmutableList;"))
private static List<OptionGroup> cullleaves$addCullLeavesOption(List<OptionGroup> groups) {
groups.add(OptionGroup.createBuilder()
.add(OptionImpl.createBuilder(boolean.class, sodiumOpts)
.setName(Text.translatable("cullleaves.midnightconfig.enabled"))
.setTooltip(Text.translatable("cullleaves.midnightconfig.enabled.tooltip.sodium"))
.setControl(TickBoxControl::new)
.setBinding((opts, value) -> {
CullLeavesConfig.enabled = value;
CullLeavesConfig.write("cullleaves");
}, opts -> CullLeavesConfig.enabled)
.setFlags(OptionFlag.REQUIRES_RENDERER_RELOAD)
.setImpact(OptionImpact.MEDIUM)
.build()
).add(OptionImpl.createBuilder(boolean.class, sodiumOpts)
.setName(Text.translatable("cullleaves.midnightconfig.cullRoots"))
.setTooltip(Text.translatable("cullleaves.midnightconfig.cullRoots.tooltip.sodium"))
.setControl(TickBoxControl::new)
.setBinding((opts, value) -> {
CullLeavesConfig.cullRoots = value;
CullLeavesConfig.write("cullleaves");
}, opts -> CullLeavesConfig.cullRoots)
.setFlags(OptionFlag.REQUIRES_RENDERER_RELOAD)
.setImpact(OptionImpact.MEDIUM)
.build()
)
.build()
);
return groups;
}
}

View File

@@ -0,0 +1,14 @@
{
"required": true,
"package": "eu.midnightdust.cullleaves.fabric.mixin",
"plugin": "eu.midnightdust.cullleaves.CullLeavesMixinPlugin",
"compatibilityLevel": "JAVA_17",
"minVersion": "0.8",
"client": [
"sodium.MixinBlockRenderer",
"sodium.MixinSodiumGameOptionPages"
],
"injectors": {
"defaultRequire": 1
}
}

View File

@@ -27,13 +27,14 @@
"depends": {
"midnightlib": "*",
"minecraft": ">=1.19.3"
"minecraft": ">=1.21"
},
"breaks": {
"sodium": "<0.4.9"
"sodium": "<0.6.0"
},
"mixins": [
"cullleaves.mixins.json"
"cullleaves.mixins.json",
"cullleaves-fabric.mixins.json"
]
}

View File

@@ -1,23 +1,23 @@
org.gradle.jvmargs=-Xmx4096M
minecraft_version=1.21
yarn_mappings=1.21+build.2
minecraft_version=1.21.1
yarn_mappings=1.21.1+build.3
enabled_platforms=fabric,neoforge
archives_base_name=cullleaves
mod_version=3.4.0
mod_version=4.0.0
maven_group=eu.midnightdust
release_type=release
curseforge_id=423254
modrinth_id=GNxdLCoP
midnightlib_version=1.5.7
sodium_version=mc1.21-0.5.9
midnightlib_version=1.6.3
sodium_version=mc1.21.1-0.6.5
fabric_loader_version=0.15.11
fabric_api_version=0.100.1+1.21
fabric_loader_version=0.16.9
fabric_api_version=0.110.0+1.21.1
neoforge_version=21.0.14-beta
neoforge_version=21.1.82
yarn_mappings_patch_neoforge_version = 1.21+build.4
quilt_loader_version=0.18.9

View File

@@ -12,7 +12,15 @@ architectury {
loom {}
repositories {
maven { url "https://api.modrinth.com/maven" }
maven {url "https://maven.neoforged.net/releases"}
maven { url "https://maven.neoforged.net/releases" }
maven { url "https://maven.pkg.github.com/ims212/ForgifiedFabricAPI"
credentials {
username = "IMS212"
// Read only token
password = "ghp_" + "DEuGv0Z56vnSOYKLCXdsS9svK4nb9K39C1Hn"
}
}
maven{ url "https://maven.su5ed.dev/releases" }
}
configurations {
@@ -29,6 +37,7 @@ dependencies {
// Remove the next line if you don't want to depend on the API
//modApi "dev.architectury:architectury-forge:${rootProject.architectury_version}"
modImplementation "maven.modrinth:midnightlib:${rootProject.midnightlib_version}-neoforge"
modImplementation "maven.modrinth:sodium:${rootProject.sodium_version}-neoforge"
//include "maven.modrinth:midnightlib:${rootProject.midnightlib_version}-forge"
common(project(path: ":common", configuration: "namedElements")) { transitive false }

View File

@@ -1,5 +1,6 @@
package eu.midnightdust.cullleaves.neoforge;
import eu.midnightdust.cullleaves.CullLeavesClient;
import net.minecraft.resource.*;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
@@ -7,17 +8,18 @@ import net.neoforged.api.distmarker.Dist;
import net.neoforged.bus.api.SubscribeEvent;
import net.neoforged.fml.ModList;
import net.neoforged.fml.common.EventBusSubscriber;
import net.neoforged.neoforge.client.event.RegisterClientReloadListenersEvent;
import net.neoforged.neoforge.event.AddPackFindersEvent;
import net.neoforged.neoforgespi.locating.IModFile;
import java.util.Optional;
@EventBusSubscriber(modid = "cullleaves", bus = EventBusSubscriber.Bus.MOD, value = Dist.CLIENT)
@EventBusSubscriber(modid = CullLeavesClient.MOD_ID, bus = EventBusSubscriber.Bus.MOD, value = Dist.CLIENT)
public class CullLeavesClientEvents {
@SubscribeEvent
public static void addPackFinders(AddPackFindersEvent event) {
if (event.getPackType() == ResourceType.CLIENT_RESOURCES) {
registerResourcePack(event, Identifier.of("cullleaves", "smartleaves"), false);
registerResourcePack(event, Identifier.of(CullLeavesClient.MOD_ID, "smartleaves"), false);
}
}
private static void registerResourcePack(AddPackFindersEvent event, Identifier id, boolean alwaysEnabled) {
@@ -33,4 +35,8 @@ public class CullLeavesClientEvents {
} catch (NullPointerException e) {e.fillInStackTrace();}
}));
}
@SubscribeEvent
public static void onResourceReload(RegisterClientReloadListenersEvent event) {
event.registerReloadListener(CullLeavesClient.ReloadListener.INSTANCE);
}
}

View File

@@ -0,0 +1,26 @@
package eu.midnightdust.cullleaves.neoforge.mixin.sodium;
import eu.midnightdust.cullleaves.CullLeavesClient;
import net.caffeinemc.mods.sodium.client.render.chunk.compile.pipeline.BlockRenderer;
import net.minecraft.block.BlockState;
import net.minecraft.block.LeavesBlock;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.model.BakedModel;
import net.minecraft.util.math.BlockPos;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(BlockRenderer.class)
public abstract class MixinBlockRenderer {
// Unfortunately, we cannot use the level view from AbstractBlockRenderContext on NeoForge because of method conflicts
@Unique private static final MinecraftClient cullleaves$client = MinecraftClient.getInstance();
@Inject(at = @At("HEAD"), method = "renderModel", cancellable = true)
public void cullleaves$cancelRendering(BakedModel model, BlockState state, BlockPos pos, BlockPos origin, CallbackInfo ci) {
if (CullLeavesClient.forceHideInnerLeaves && state.getBlock() instanceof LeavesBlock && CullLeavesClient.shouldHideBlock(cullleaves$client.world, pos))
ci.cancel();
}
}

View File

@@ -1,13 +1,13 @@
package eu.midnightdust.cullleaves.mixin;
package eu.midnightdust.cullleaves.neoforge.mixin.sodium;
import eu.midnightdust.cullleaves.config.CullLeavesConfig;
import me.jellysquid.mods.sodium.client.gui.SodiumGameOptionPages;
import me.jellysquid.mods.sodium.client.gui.options.OptionFlag;
import me.jellysquid.mods.sodium.client.gui.options.OptionGroup;
import me.jellysquid.mods.sodium.client.gui.options.OptionImpact;
import me.jellysquid.mods.sodium.client.gui.options.OptionImpl;
import me.jellysquid.mods.sodium.client.gui.options.control.TickBoxControl;
import me.jellysquid.mods.sodium.client.gui.options.storage.SodiumOptionsStorage;
import net.caffeinemc.mods.sodium.client.gui.SodiumGameOptionPages;
import net.caffeinemc.mods.sodium.client.gui.options.OptionFlag;
import net.caffeinemc.mods.sodium.client.gui.options.OptionGroup;
import net.caffeinemc.mods.sodium.client.gui.options.OptionImpact;
import net.caffeinemc.mods.sodium.client.gui.options.OptionImpl;
import net.caffeinemc.mods.sodium.client.gui.options.control.TickBoxControl;
import net.caffeinemc.mods.sodium.client.gui.options.storage.SodiumOptionsStorage;
import net.minecraft.text.Text;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;

View File

@@ -16,6 +16,9 @@ logoFile = "icon.png"
[[mixins]]
config = "cullleaves.mixins.json"
[[mixins]]
config = "cullleaves-neoforge.mixins.json"
[[dependencies.cullleaves]]
modId = "neoforge"
required = true
@@ -26,7 +29,7 @@ side = "CLIENT"
[[dependencies.cullleaves]]
modId = "minecraft"
required = true
versionRange = "[1.20,)"
versionRange = "[1.21,)"
ordering = "NONE"
side = "CLIENT"

View File

@@ -0,0 +1,14 @@
{
"required": true,
"package": "eu.midnightdust.cullleaves.neoforge.mixin",
"plugin": "eu.midnightdust.cullleaves.CullLeavesMixinPlugin",
"compatibilityLevel": "JAVA_17",
"minVersion": "0.8",
"client": [
"sodium.MixinBlockRenderer",
"sodium.MixinSodiumGameOptionPages"
],
"injectors": {
"defaultRequire": 1
}
}