stonecutter: setup multiversion build

This commit is contained in:
Martin Prokoph
2025-11-19 23:21:49 +01:00
parent ff60cf4873
commit 084c892e44
72 changed files with 694 additions and 864 deletions

View File

@@ -0,0 +1,135 @@
package eu.midnightdust.cullleaves;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import eu.midnightdust.cullleaves.config.CullLeavesConfig;
import eu.midnightdust.lib.config.MidnightConfig;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.PackType;
import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraft.server.packs.resources.ResourceManagerReloadListener;
import net.minecraft.world.level.BlockAndTintGetter;
import net.minecraft.world.level.block.LeavesBlock;
import net.minecraft.world.level.block.MangroveRootsBlock;
import net.minecraft.world.level.block.state.BlockState;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
//? fabric {
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.v1.ResourceLoader;
import net.fabricmc.loader.api.FabricLoader;
//?} else if neoforge {
/*import net.minecraft.network.chat.Component;
import net.minecraft.server.packs.repository.Pack;
import net.minecraft.server.packs.repository.PackSource;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.bus.api.SubscribeEvent;
import net.neoforged.fml.common.EventBusSubscriber;
import net.neoforged.fml.common.Mod;
import net.neoforged.neoforge.client.event.AddClientReloadListenersEvent;
import net.neoforged.neoforge.event.AddPackFindersEvent;
*///?}
//? neoforge
/*@Mod(CullLeavesClient.MOD_ID)*/
public class CullLeavesClient /*? fabric {*/ implements ClientModInitializer /*?}*/ {
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(BlockAndTintGetter world, BlockPos pos) {
if (CullLeavesClient.forceHideInnerLeaves) {
boolean shouldForceCull = true;
for (Direction dir : Direction.values()) {
BlockState otherState = world.getBlockState(pos.relative(dir));
if (!(otherState.getBlock() instanceof LeavesBlock) &&
!otherState.isFaceSturdy(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 ResourceManagerReloadListener {
public static final ReloadListener INSTANCE = new ReloadListener();
private ReloadListener() {
}
@Override
public void onResourceManagerReload(ResourceManager manager) {
CullLeavesClient.forceLeafCulling = false;
CullLeavesClient.forceHideInnerLeaves = false;
manager.listResources("options", path -> path.toString().startsWith("cullleaves") && path.toString().endsWith("options.json")).forEach((id, resource) -> {
try {
JsonObject json = JsonParser.parseReader(resource.openAsReader()).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);
}
});
}
}
//? fabric {
@Override
public void onInitializeClient() {
MidnightConfig.init(CullLeavesClient.MOD_ID, CullLeavesConfig.class);
FabricLoader.getInstance().getModContainer("cullleaves").ifPresent(modContainer -> {
ResourceManagerHelper.registerBuiltinResourcePack(ResourceLocation.fromNamespaceAndPath(CullLeavesClient.MOD_ID, "smartleaves"), modContainer, ResourcePackActivationType.NORMAL);
});
ResourceLoader.get(PackType.CLIENT_RESOURCES).registerReloader(ResourceLocation.fromNamespaceAndPath(CullLeavesClient.MOD_ID, "resourcepack_options"), CullLeavesClient.ReloadListener.INSTANCE);
}
//?} else if neoforge {
/*public CullLeavesClient() {
MidnightConfig.init(CullLeavesClient.MOD_ID, CullLeavesConfig.class);
}
@EventBusSubscriber(modid = CullLeavesClient.MOD_ID, value = Dist.CLIENT)
public class CullLeavesClientEvents {
@SubscribeEvent
public static void addPackFinders(AddPackFindersEvent event) {
if (event.getPackType() == PackType.CLIENT_RESOURCES) {
event.addPackFinders(ResourceLocation.fromNamespaceAndPath(CullLeavesClient.MOD_ID, "resourcepacks/smartleaves"), PackType.CLIENT_RESOURCES, Component.literal("cullleaves/smartleaves"), PackSource.BUILT_IN, false, Pack.Position.TOP);
}
}
@SubscribeEvent
public static void onResourceReload(AddClientReloadListenersEvent event) {
event.addListener(ResourceLocation.fromNamespaceAndPath(CullLeavesClient.MOD_ID, "resourcepack_options"), CullLeavesClient.ReloadListener.INSTANCE);
}
}
*///?}
}

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

@@ -0,0 +1,18 @@
package eu.midnightdust.cullleaves.config;
import eu.midnightdust.lib.config.MidnightConfig;
import net.minecraft.client.Minecraft;
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() {
var client = Minecraft.getInstance();
if (client != null && client.level != null) client.levelRenderer.needsUpdate();
super.writeChanges();
}
}

View File

@@ -0,0 +1,26 @@
package eu.midnightdust.cullleaves.mixin;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import eu.midnightdust.cullleaves.CullLeavesClient;
import net.minecraft.client.renderer.block.ModelBlockRenderer;
import net.minecraft.client.renderer.block.model.BlockModelPart;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.BlockAndTintGetter;
import net.minecraft.world.level.block.LeavesBlock;
import net.minecraft.world.level.block.state.BlockState;
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;
import java.util.List;
@Mixin(ModelBlockRenderer.class)
public class MixinBlockModelRenderer {
@Inject(at = @At("HEAD"), method = "tesselateBlock", cancellable = true)
private void cullleaves$cancelRendering(BlockAndTintGetter world, List<BlockModelPart> parts, BlockState state, BlockPos pos, PoseStack matrices, VertexConsumer vertexConsumer, boolean cull, int overlay, CallbackInfo ci) {
if (state.getBlock() instanceof LeavesBlock &&
CullLeavesClient.shouldHideBlock(world, pos)) ci.cancel();
}
}

View File

@@ -0,0 +1,20 @@
package eu.midnightdust.cullleaves.mixin;
import eu.midnightdust.cullleaves.CullLeavesClient;
import net.minecraft.core.Direction;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.LeavesBlock;
import net.minecraft.world.level.block.state.BlockState;
import org.spongepowered.asm.mixin.Mixin;
@Mixin(value = LeavesBlock.class, priority = 1900)
public abstract class MixinLeavesBlock extends Block {
public MixinLeavesBlock(Properties properties) {
super(properties);
}
@Override
public boolean skipRendering(BlockState state, BlockState neighborState, Direction offset) {
return CullLeavesClient.isLeafSideInvisible(neighborState);
}
}

View File

@@ -0,0 +1,20 @@
package eu.midnightdust.cullleaves.mixin;
import eu.midnightdust.cullleaves.CullLeavesClient;
import net.minecraft.core.Direction;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.MangroveRootsBlock;
import net.minecraft.world.level.block.state.BlockState;
import org.spongepowered.asm.mixin.Mixin;
@Mixin(value = MangroveRootsBlock.class, priority = 1900)
public abstract class MixinMangroveRootsBlock extends Block {
public MixinMangroveRootsBlock(Properties properties) {
super(properties);
}
@Override
public boolean skipRendering(BlockState state, BlockState neighborState, Direction offset) {
return CullLeavesClient.isRootSideInvisible(neighborState);
}
}

View File

@@ -0,0 +1,22 @@
package eu.midnightdust.cullleaves.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.client.renderer.block.model.BlockStateModel;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.block.LeavesBlock;
import net.minecraft.world.level.block.state.BlockState;
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(BlockStateModel 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,56 @@
package eu.midnightdust.cullleaves.mixin.sodium;
import eu.midnightdust.cullleaves.config.CullLeavesConfig;
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.network.chat.Component;
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 = 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(Component.translatable("cullleaves.midnightconfig.enabled"))
.setTooltip(Component.translatable("cullleaves.midnightconfig.enabled.tooltip"))
.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(Component.translatable("cullleaves.midnightconfig.cullRoots"))
.setTooltip(Component.translatable("cullleaves.midnightconfig.cullRoots.tooltip"))
.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;
}
}