From aabd5958165af50427b4272ea45efcaaae235619 Mon Sep 17 00:00:00 2001 From: DekinDev Date: Thu, 1 Jan 2026 04:47:41 +0100 Subject: [PATCH] 1.21.8 --- build.gradle | 8 +- .../client/BlockRenderManagerMixin.java | 28 ++ .../smoothdoors/client/ClientWorldMixin.java | 19 ++ .../client/WorldRendererMixin.java | 27 ++ .../smoothdoors/client/anim/SddAnimator.java | 303 ++++++++++++++++++ .../client/ui/SddConfigScreen.java | 159 +++++++++ .../compat/ModMenuIntegration.java | 12 + .../smooth-double-doors.client.mixins.json | 20 +- .../smoothdoors/SmoothDoubleDoors.java | 9 +- .../straice/smoothdoors/config/SddConfig.java | 20 ++ .../smoothdoors/config/SddConfigManager.java | 40 +++ .../smoothdoors/mixin/DoorBlockMixin.java | 63 ++++ .../smoothdoors/mixin/ExampleMixin.java | 15 - .../mixin/FenceGateBlockMixin.java | 61 ++++ .../smoothdoors/mixin/TrapdoorBlockMixin.java | 61 ++++ .../smoothdoors/util/ConnectedUtil.java | 126 ++++++++ .../straice/smoothdoors/util/SyncGuard.java | 16 + src/main/resources/fabric.mod.json | 65 ++-- .../resources/smooth-double-doors.mixins.json | 23 +- 19 files changed, 993 insertions(+), 82 deletions(-) create mode 100644 src/client/java/com/straice/smoothdoors/client/BlockRenderManagerMixin.java create mode 100644 src/client/java/com/straice/smoothdoors/client/ClientWorldMixin.java create mode 100644 src/client/java/com/straice/smoothdoors/client/WorldRendererMixin.java create mode 100644 src/client/java/com/straice/smoothdoors/client/anim/SddAnimator.java create mode 100644 src/client/java/com/straice/smoothdoors/client/ui/SddConfigScreen.java create mode 100644 src/client/java/com/straice/smoothdoors/compat/ModMenuIntegration.java create mode 100644 src/main/java/com/straice/smoothdoors/config/SddConfig.java create mode 100644 src/main/java/com/straice/smoothdoors/config/SddConfigManager.java create mode 100644 src/main/java/com/straice/smoothdoors/mixin/DoorBlockMixin.java delete mode 100644 src/main/java/com/straice/smoothdoors/mixin/ExampleMixin.java create mode 100644 src/main/java/com/straice/smoothdoors/mixin/FenceGateBlockMixin.java create mode 100644 src/main/java/com/straice/smoothdoors/mixin/TrapdoorBlockMixin.java create mode 100644 src/main/java/com/straice/smoothdoors/util/ConnectedUtil.java create mode 100644 src/main/java/com/straice/smoothdoors/util/SyncGuard.java diff --git a/build.gradle b/build.gradle index f36ca4b..c24568e 100644 --- a/build.gradle +++ b/build.gradle @@ -11,11 +11,7 @@ base { } repositories { - // Add repositories to retrieve artifacts from in here. - // You should only use this when depending on other mods because - // Loom adds the essential maven repositories to download Minecraft and libraries from automatically. - // See https://docs.gradle.org/current/userguide/declaring_repositories.html - // for more information about repositories. + maven { url "https://maven.terraformersmc.com/releases/" } } loom { @@ -39,6 +35,8 @@ dependencies { // Fabric API. This is technically optional, but you probably want it anyway. modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_api_version}" + modCompileOnly "com.terraformersmc:modmenu:15.0.0" + modRuntimeOnly "com.terraformersmc:modmenu:15.0.0" } processResources { diff --git a/src/client/java/com/straice/smoothdoors/client/BlockRenderManagerMixin.java b/src/client/java/com/straice/smoothdoors/client/BlockRenderManagerMixin.java new file mode 100644 index 0000000..7638b41 --- /dev/null +++ b/src/client/java/com/straice/smoothdoors/client/BlockRenderManagerMixin.java @@ -0,0 +1,28 @@ +package com.straice.smoothdoors.client; + +import com.straice.smoothdoors.client.anim.SddAnimator; +import net.minecraft.block.BlockState; +import net.minecraft.client.render.VertexConsumer; +import net.minecraft.client.render.block.BlockRenderManager; +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.CallbackInfoReturnable; + +@Mixin(BlockRenderManager.class) +public class BlockRenderManagerMixin { + + @Inject(method = "renderBlock", at = @At("HEAD"), cancellable = true) + private void sdd$renderBlock(BlockState state, BlockPos pos, BlockRenderView world, MatrixStack matrices, + VertexConsumer vertexConsumer, boolean cull, Random random, + CallbackInfoReturnable cir) { + if (SddAnimator.shouldHideInChunk(pos, state)) { + // “true” para que el renderer no piense que falló + cir.setReturnValue(true); + } + } +} diff --git a/src/client/java/com/straice/smoothdoors/client/ClientWorldMixin.java b/src/client/java/com/straice/smoothdoors/client/ClientWorldMixin.java new file mode 100644 index 0000000..946ced2 --- /dev/null +++ b/src/client/java/com/straice/smoothdoors/client/ClientWorldMixin.java @@ -0,0 +1,19 @@ +package com.straice.smoothdoors.client; + +import com.straice.smoothdoors.client.anim.SddAnimator; +import net.minecraft.block.BlockState; +import net.minecraft.client.world.ClientWorld; +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(ClientWorld.class) +public class ClientWorldMixin { + + @Inject(method = "updateListeners", at = @At("TAIL")) + private void sdd$updateListeners(BlockPos pos, BlockState oldState, BlockState newState, int flags, CallbackInfo ci) { + SddAnimator.onBlockUpdate(pos, oldState, newState); + } +} diff --git a/src/client/java/com/straice/smoothdoors/client/WorldRendererMixin.java b/src/client/java/com/straice/smoothdoors/client/WorldRendererMixin.java new file mode 100644 index 0000000..cd1bd02 --- /dev/null +++ b/src/client/java/com/straice/smoothdoors/client/WorldRendererMixin.java @@ -0,0 +1,27 @@ +package com.straice.smoothdoors.client; + +import com.straice.smoothdoors.client.anim.SddAnimator; +import net.minecraft.client.render.Camera; +import net.minecraft.client.render.GameRenderer; +import net.minecraft.client.render.RenderTickCounter; +import net.minecraft.client.render.WorldRenderer; +import net.minecraft.client.util.ObjectAllocator; +import net.minecraft.util.math.Vec3d; +import org.joml.Matrix4f; +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(WorldRenderer.class) +public class WorldRendererMixin { + + @Inject(method = "render", at = @At("TAIL")) + private void sdd$renderTail(ObjectAllocator allocator, RenderTickCounter tickCounter, boolean renderBlockOutline, + Camera camera, GameRenderer gameRenderer, Matrix4f positionMatrix, Matrix4f projectionMatrix, + CallbackInfo ci) { + Vec3d camPos = camera.getPos(); + float tickDelta = tickCounter.getTickProgress(true); + SddAnimator.renderAll(camPos, tickDelta); + } +} diff --git a/src/client/java/com/straice/smoothdoors/client/anim/SddAnimator.java b/src/client/java/com/straice/smoothdoors/client/anim/SddAnimator.java new file mode 100644 index 0000000..60de2d1 --- /dev/null +++ b/src/client/java/com/straice/smoothdoors/client/anim/SddAnimator.java @@ -0,0 +1,303 @@ +package com.straice.smoothdoors.client.anim; + +import com.straice.smoothdoors.config.SddConfig; +import com.straice.smoothdoors.config.SddConfigManager; +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.DoorBlock; +import net.minecraft.block.FenceGateBlock; +import net.minecraft.block.TrapdoorBlock; +import net.minecraft.block.enums.BlockHalf; +import net.minecraft.block.enums.DoorHinge; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.render.OverlayTexture; +import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.render.WorldRenderer; +import net.minecraft.client.render.block.BlockRenderManager; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.state.property.Properties; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.util.math.RotationAxis; +import net.minecraft.util.math.Vec3d; + +import java.util.ArrayList; + +public final class SddAnimator { + + private static final Long2ObjectOpenHashMap ANIMS = new Long2ObjectOpenHashMap<>(); + + private SddAnimator() {} + + private enum Kind { DOOR, TRAPDOOR, FENCE_GATE } + + private static final class Anim { + final BlockPos pos; + final long startNanos; + final long durationNanos; + final float fromDeg; + final float toDeg; + + Anim(BlockPos pos, long startNanos, long durationNanos, float fromDeg, float toDeg) { + this.pos = pos; + this.startNanos = startNanos; + this.durationNanos = durationNanos; + this.fromDeg = fromDeg; + this.toDeg = toDeg; + } + } + + public static boolean isAnimating(BlockPos pos) { + return ANIMS.containsKey(pos.asLong()); + } + + /** Called on client when a block state update happens. */ + public static void onBlockUpdate(BlockPos pos, BlockState oldState, BlockState newState) { + MinecraftClient client = MinecraftClient.getInstance(); + if (client.world == null) return; + + Kind kind = getKind(oldState, newState); + if (kind == null) return; + + SddConfig cfg = SddConfigManager.get(); + if (!isAnimEnabled(cfg, kind)) return; + + // Only animate OPEN changes + Boolean oldOpen = getOpenNullable(oldState); + Boolean newOpen = getOpenNullable(newState); + if (oldOpen == null || newOpen == null) return; + if (oldOpen.equals(newOpen)) return; + + float from = targetAngleDeg(kind, oldState); + float to = targetAngleDeg(kind, newState); + + long now = System.nanoTime(); + long dur = (long) (baseDurationSeconds(kind) / getSpeed(cfg, kind) * 1_000_000_000L); + if (dur < 30_000_000L) dur = 30_000_000L; // 30ms min + + ANIMS.put(pos.asLong(), new Anim(pos.toImmutable(), now, dur, from, to)); + + // Force rerender so the chunk can "forget" the vanilla block draw (we'll hide it) + client.worldRenderer.scheduleBlockRenders(pos.getX(), pos.getY(), pos.getZ(), pos.getX(), pos.getY(), pos.getZ()); + } + + /** Used by BlockRenderManagerMixin to hide the vanilla block while animating. */ + public static boolean shouldHideInChunk(BlockPos pos, BlockState state) { + if (!isAnimating(pos)) return false; + + Kind k = getKind(state, state); + if (k == null) return false; + + SddConfig cfg = SddConfigManager.get(); + return isAnimEnabled(cfg, k); + } + + /** Render all active animations (called each frame from WorldRendererMixin). */ + public static void renderAll(Vec3d cameraPos, float tickDeltaIgnored) { + MinecraftClient client = MinecraftClient.getInstance(); + if (client.world == null) { + ANIMS.clear(); + return; + } + if (ANIMS.isEmpty()) return; + + VertexConsumerProvider.Immediate consumers = client.getBufferBuilders().getEntityVertexConsumers(); + BlockRenderManager brm = client.getBlockRenderManager(); + + long now = System.nanoTime(); + ArrayList done = new ArrayList<>(); + + for (var e : ANIMS.long2ObjectEntrySet()) { + long key = e.getLongKey(); + Anim anim = e.getValue(); + + BlockState state = client.world.getBlockState(anim.pos); + Kind kind = getKind(state, state); + if (kind == null) { + done.add(key); + continue; + } + + SddConfig cfg = SddConfigManager.get(); + if (!isAnimEnabled(cfg, kind)) { + done.add(key); + client.worldRenderer.scheduleBlockRenders(anim.pos.getX(), anim.pos.getY(), anim.pos.getZ(), + anim.pos.getX(), anim.pos.getY(), anim.pos.getZ()); + continue; + } + + float t = (float) (now - anim.startNanos) / (float) anim.durationNanos; + if (t >= 1.0f) { + done.add(key); + client.worldRenderer.scheduleBlockRenders(anim.pos.getX(), anim.pos.getY(), anim.pos.getZ(), + anim.pos.getX(), anim.pos.getY(), anim.pos.getZ()); + continue; + } + + float eased = smoothstep(clamp01(t)); + float angle = lerp(anim.fromDeg, anim.toDeg, eased); + + // Render the "closed" state model and rotate it ourselves + BlockState renderState = forceClosed(state); + + int light = WorldRenderer.getLightmapCoordinates(client.world, anim.pos); + + MatrixStack ms = new MatrixStack(); + ms.push(); + + ms.translate(anim.pos.getX() - cameraPos.x, anim.pos.getY() - cameraPos.y, anim.pos.getZ() - cameraPos.z); + applyTransform(ms, renderState, kind, angle); + + brm.renderBlockAsEntity(renderState, ms, consumers, light, OverlayTexture.DEFAULT_UV); + + ms.pop(); + } + + consumers.draw(); + + for (Long k : done) ANIMS.remove(k.longValue()); + } + + // =================== helpers =================== + + private static float clamp01(float v) { return v < 0 ? 0 : (v > 1 ? 1 : v); } + private static float lerp(float a, float b, float t) { return a + (b - a) * t; } + private static float smoothstep(float t) { return t * t * (3f - 2f * t); } + + private static boolean isAnimEnabled(SddConfig cfg, Kind k) { + return switch (k) { + case DOOR -> cfg.animateDoors; + case TRAPDOOR -> cfg.animateTrapdoors; + case FENCE_GATE -> cfg.animateFenceGates; + }; + } + + private static float getSpeed(SddConfig cfg, Kind k) { + float v = switch (k) { + case DOOR -> cfg.doorSpeed; + case TRAPDOOR -> cfg.trapdoorSpeed; + case FENCE_GATE -> cfg.fenceGateSpeed; + }; + if (v < 0.2f) v = 0.2f; + if (v > 3.0f) v = 3.0f; + return v; + } + + private static float baseDurationSeconds(Kind k) { + return switch (k) { + case DOOR -> 0.18f; + case TRAPDOOR -> 0.16f; + case FENCE_GATE -> 0.18f; + }; + } + + private static Kind getKind(BlockState a, BlockState b) { + if (a.getBlock() instanceof DoorBlock || b.getBlock() instanceof DoorBlock) return Kind.DOOR; + if (a.getBlock() instanceof TrapdoorBlock || b.getBlock() instanceof TrapdoorBlock) return Kind.TRAPDOOR; + if (a.getBlock() instanceof FenceGateBlock || b.getBlock() instanceof FenceGateBlock) return Kind.FENCE_GATE; + return null; + } + + private static Boolean getOpenNullable(BlockState s) { + Block b = s.getBlock(); + if ((b instanceof DoorBlock || b instanceof TrapdoorBlock || b instanceof FenceGateBlock) && s.contains(Properties.OPEN)) { + return s.get(Properties.OPEN); + } + return null; + } + + private static BlockState forceClosed(BlockState s) { + if (s.contains(Properties.OPEN)) return s.with(Properties.OPEN, false); + return s; + } + + /** Target angle for the given state (0 or +/- 90 degrees). */ + private static float targetAngleDeg(Kind k, BlockState state) { + Boolean open = getOpenNullable(state); + if (open == null || !open) return 0f; + + return switch (k) { + case DOOR -> { + DoorHinge hinge = state.get(Properties.DOOR_HINGE); + yield (hinge == DoorHinge.LEFT) ? -90f : 90f; + } + case FENCE_GATE -> -90f; + case TRAPDOOR -> trapdoorOpenAngle(state); + }; + } + + private static float trapdoorOpenAngle(BlockState state) { + Direction f = state.get(Properties.HORIZONTAL_FACING); + BlockHalf half = state.get(Properties.BLOCK_HALF); + + float angle; + switch (f) { + case NORTH -> angle = -90f; + case SOUTH -> angle = 90f; + case WEST -> angle = 90f; + case EAST -> angle = -90f; + default -> angle = 90f; + } + + if (half == BlockHalf.TOP) angle = -angle; + return angle; + } + + private static void applyTransform(MatrixStack ms, BlockState state, Kind kind, float angleDeg) { + switch (kind) { + case DOOR -> applyDoorTransform(ms, state, angleDeg); + case TRAPDOOR -> applyTrapdoorTransform(ms, state, angleDeg); + case FENCE_GATE -> applyFenceGateTransform(ms, state, angleDeg); + } + } + + private static void applyDoorTransform(MatrixStack ms, BlockState state, float angleDeg) { + Direction facing = state.get(Properties.HORIZONTAL_FACING); + DoorHinge hinge = state.get(Properties.DOOR_HINGE); + + Direction hingeDir = (hinge == DoorHinge.LEFT) ? facing.rotateYCounterclockwise() : facing.rotateYClockwise(); + + double px = 0.5; + double pz = 0.5; + if (hingeDir == Direction.WEST) px = 0.0; + if (hingeDir == Direction.EAST) px = 1.0; + if (hingeDir == Direction.NORTH) pz = 0.0; + if (hingeDir == Direction.SOUTH) pz = 1.0; + + ms.translate(px, 0.0, pz); + ms.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(angleDeg)); + ms.translate(-px, 0.0, -pz); + } + + private static void applyFenceGateTransform(MatrixStack ms, BlockState state, float angleDeg) { + ms.translate(0.5, 0.0, 0.5); + ms.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(angleDeg)); + ms.translate(-0.5, 0.0, -0.5); + } + + private static void applyTrapdoorTransform(MatrixStack ms, BlockState state, float angleDeg) { + Direction f = state.get(Properties.HORIZONTAL_FACING); + BlockHalf half = state.get(Properties.BLOCK_HALF); + + double py = (half == BlockHalf.TOP) ? 1.0 : 0.0; + + double px = 0.5; + double pz = 0.5; + + if (f == Direction.NORTH) pz = 0.0; + if (f == Direction.SOUTH) pz = 1.0; + if (f == Direction.WEST) px = 0.0; + if (f == Direction.EAST) px = 1.0; + + ms.translate(px, py, pz); + + if (f == Direction.NORTH || f == Direction.SOUTH) { + ms.multiply(RotationAxis.POSITIVE_X.rotationDegrees(angleDeg)); + } else { + ms.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(angleDeg)); + } + + ms.translate(-px, -py, -pz); + } +} diff --git a/src/client/java/com/straice/smoothdoors/client/ui/SddConfigScreen.java b/src/client/java/com/straice/smoothdoors/client/ui/SddConfigScreen.java new file mode 100644 index 0000000..12a5831 --- /dev/null +++ b/src/client/java/com/straice/smoothdoors/client/ui/SddConfigScreen.java @@ -0,0 +1,159 @@ +package com.straice.smoothdoors.client.ui; + +import com.straice.smoothdoors.config.SddConfig; +import com.straice.smoothdoors.config.SddConfigManager; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.widget.ButtonWidget; +import net.minecraft.client.gui.widget.SliderWidget; +import net.minecraft.text.Text; + +import java.util.Locale; + +public class SddConfigScreen extends Screen { + private final Screen parent; + private final SddConfig cfg; + + public SddConfigScreen(Screen parent) { + super(Text.literal("Smooth Double Doors")); + this.parent = parent; + this.cfg = SddConfigManager.get(); + } + + @Override + protected void init() { + int centerX = this.width / 2; + + int colW = 118; + int gap = 4; + int leftX = centerX - colW - gap; + int rightX = centerX + gap; + + int y = this.height / 4; + int h = 20; + + // ===== DOUBLE SYNC (2 columns) ===== + + // Doors row + addDrawableChild(toggle(leftX, y, colW, h, + () -> "Doors | Use: " + (cfg.connectDoors ? "ON" : "OFF"), + () -> cfg.connectDoors = !cfg.connectDoors)); + + addDrawableChild(toggle(rightX, y, colW, h, + () -> "Doors | Redstone: " + (cfg.redstoneDoubleDoors ? "ON" : "OFF"), + () -> cfg.redstoneDoubleDoors = !cfg.redstoneDoubleDoors)); + + y += 24; + + // Trapdoors row + addDrawableChild(toggle(leftX, y, colW, h, + () -> "Traps | Use: " + (cfg.connectTrapdoors ? "ON" : "OFF"), + () -> cfg.connectTrapdoors = !cfg.connectTrapdoors)); + + addDrawableChild(toggle(rightX, y, colW, h, + () -> "Traps | Redstone: " + (cfg.redstoneDoubleTrapdoors ? "ON" : "OFF"), + () -> cfg.redstoneDoubleTrapdoors = !cfg.redstoneDoubleTrapdoors)); + + y += 24; + + // Fence gates row + addDrawableChild(toggle(leftX, y, colW, h, + () -> "Fences | Use: " + (cfg.connectFenceGates ? "ON" : "OFF"), + () -> cfg.connectFenceGates = !cfg.connectFenceGates)); + + addDrawableChild(toggle(rightX, y, colW, h, + () -> "Fences | Redstone: " + (cfg.redstoneDoubleFenceGates ? "ON" : "OFF"), + () -> cfg.redstoneDoubleFenceGates = !cfg.redstoneDoubleFenceGates)); + + y += 34; + + // ===== ANIMATIONS ===== + addDrawableChild(toggle(centerX - 120, y, 240, h, + () -> "Animation Doors: " + (cfg.animateDoors ? "ON" : "OFF"), + () -> cfg.animateDoors = !cfg.animateDoors)); + y += 24; + + addDrawableChild(toggle(centerX - 120, y, 240, h, + () -> "Animation Traps: " + (cfg.animateTrapdoors ? "ON" : "OFF"), + () -> cfg.animateTrapdoors = !cfg.animateTrapdoors)); + y += 24; + + addDrawableChild(toggle(centerX - 120, y, 240, h, + () -> "Animation Fences: " + (cfg.animateFenceGates ? "ON" : "OFF"), + () -> cfg.animateFenceGates = !cfg.animateFenceGates)); + y += 34; + + // ===== SPEED ===== + addDrawableChild(new SpeedSlider(centerX - 120, y, 240, h, + "Speed Doors", cfg.doorSpeed, v -> { + cfg.doorSpeed = v; + SddConfigManager.save(); + })); + y += 24; + + addDrawableChild(new SpeedSlider(centerX - 120, y, 240, h, + "Speed Traps", cfg.trapdoorSpeed, v -> { + cfg.trapdoorSpeed = v; + SddConfigManager.save(); + })); + y += 24; + + addDrawableChild(new SpeedSlider(centerX - 120, y, 240, h, + "Speed Fences", cfg.fenceGateSpeed, v -> { + cfg.fenceGateSpeed = v; + SddConfigManager.save(); + })); + y += 40; + + addDrawableChild(ButtonWidget.builder(Text.literal("Save and close"), btn -> { + SddConfigManager.save(); + if (this.client != null) this.client.setScreen(parent); + }).dimensions(centerX - 120, y, 240, h).build()); + } + + private ButtonWidget toggle(int x, int y, int w, int h, java.util.function.Supplier label, + Runnable action) { + return ButtonWidget.builder(Text.literal(label.get()), btn -> { + action.run(); + btn.setMessage(Text.literal(label.get())); + SddConfigManager.save(); + }).dimensions(x, y, w, h).build(); + } + + @Override + public void close() { + SddConfigManager.save(); + if (this.client != null) this.client.setScreen(parent); + } + + private static class SpeedSlider extends SliderWidget { + private final String label; + private final java.util.function.Consumer onApply; + + private static float toSpeed(double v) { return (float) (0.2 + v * 2.8); } + private static double toValue(float speed) { + double v = (speed - 0.2) / 2.8; + if (v < 0) v = 0; + if (v > 1) v = 1; + return v; + } + + SpeedSlider(int x, int y, int w, int h, String label, float speed, + java.util.function.Consumer onApply) { + super(x, y, w, h, Text.literal(label), toValue(speed)); + this.label = label; + this.onApply = onApply; + updateMessage(); + } + + @Override + protected void updateMessage() { + float sp = toSpeed(this.value); + setMessage(Text.literal(label + ": " + String.format(Locale.ROOT, "%.2f", sp) + "x")); + } + + @Override + protected void applyValue() { + onApply.accept(toSpeed(this.value)); + } + } +} \ No newline at end of file diff --git a/src/client/java/com/straice/smoothdoors/compat/ModMenuIntegration.java b/src/client/java/com/straice/smoothdoors/compat/ModMenuIntegration.java new file mode 100644 index 0000000..b23c122 --- /dev/null +++ b/src/client/java/com/straice/smoothdoors/compat/ModMenuIntegration.java @@ -0,0 +1,12 @@ +package com.straice.smoothdoors.compat; + +import com.straice.smoothdoors.client.ui.SddConfigScreen; +import com.terraformersmc.modmenu.api.ModMenuApi; +import com.terraformersmc.modmenu.api.ConfigScreenFactory; + +public class ModMenuIntegration implements ModMenuApi { + @Override + public ConfigScreenFactory getModConfigScreenFactory() { + return parent -> new SddConfigScreen(parent); + } +} diff --git a/src/client/resources/smooth-double-doors.client.mixins.json b/src/client/resources/smooth-double-doors.client.mixins.json index 4de7db3..97f37d5 100644 --- a/src/client/resources/smooth-double-doors.client.mixins.json +++ b/src/client/resources/smooth-double-doors.client.mixins.json @@ -1,11 +1,13 @@ { - "required": true, - "package": "com.straice.smoothdoors.mixin.client", - "compatibilityLevel": "JAVA_21", - "client": [ - "ExampleClientMixin" - ], - "injectors": { - "defaultRequire": 1 - } + "required": true, + "package": "com.straice.smoothdoors.mixin", + "compatibilityLevel": "JAVA_21", + "client": [ + "client.ClientWorldMixin", + "client.BlockRenderManagerMixin", + "client.WorldRendererMixin" + ], + "injectors": { + "defaultRequire": 1 + } } \ No newline at end of file diff --git a/src/main/java/com/straice/smoothdoors/SmoothDoubleDoors.java b/src/main/java/com/straice/smoothdoors/SmoothDoubleDoors.java index 1dea172..eca0478 100644 --- a/src/main/java/com/straice/smoothdoors/SmoothDoubleDoors.java +++ b/src/main/java/com/straice/smoothdoors/SmoothDoubleDoors.java @@ -5,6 +5,8 @@ import net.fabricmc.api.ModInitializer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.straice.smoothdoors.config.SddConfigManager; + public class SmoothDoubleDoors implements ModInitializer { public static final String MOD_ID = "smooth-double-doors"; @@ -15,10 +17,7 @@ public class SmoothDoubleDoors implements ModInitializer { @Override public void onInitialize() { - // This code runs as soon as Minecraft is in a mod-load-ready state. - // However, some things (like resources) may still be uninitialized. - // Proceed with mild caution. - - LOGGER.info("Hello Fabric world!"); + SddConfigManager.load(); + LOGGER.info("Smooth Double Doors loaded"); } } \ No newline at end of file diff --git a/src/main/java/com/straice/smoothdoors/config/SddConfig.java b/src/main/java/com/straice/smoothdoors/config/SddConfig.java new file mode 100644 index 0000000..e68ae71 --- /dev/null +++ b/src/main/java/com/straice/smoothdoors/config/SddConfig.java @@ -0,0 +1,20 @@ +package com.straice.smoothdoors.config; + +public class SddConfig { + public boolean connectDoors = true; + public boolean redstoneDoubleDoors = true; + + public boolean connectTrapdoors = false; + public boolean redstoneDoubleTrapdoors = true; + + public boolean connectFenceGates = true; + public boolean redstoneDoubleFenceGates = true; + + public boolean animateDoors = true; + public boolean animateTrapdoors = true; + public boolean animateFenceGates = true; + + public float doorSpeed = 1.0f; + public float trapdoorSpeed = 1.0f; + public float fenceGateSpeed = 1.0f; +} diff --git a/src/main/java/com/straice/smoothdoors/config/SddConfigManager.java b/src/main/java/com/straice/smoothdoors/config/SddConfigManager.java new file mode 100644 index 0000000..9213773 --- /dev/null +++ b/src/main/java/com/straice/smoothdoors/config/SddConfigManager.java @@ -0,0 +1,40 @@ +package com.straice.smoothdoors.config; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import net.fabricmc.loader.api.FabricLoader; + +import java.nio.file.Files; +import java.nio.file.Path; + +public final class SddConfigManager { + private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); + private static final Path PATH = FabricLoader.getInstance().getConfigDir().resolve("smooth-double-doors.json"); + + private static SddConfig config = new SddConfig(); + + private SddConfigManager() {} + + public static SddConfig get() { return config; } + + public static void load() { + try { + if (Files.exists(PATH)) { + String json = Files.readString(PATH); + SddConfig read = GSON.fromJson(json, SddConfig.class); + if (read != null) config = read; + } else { + save(); + } + } catch (Exception ignored) { + // si falla, usamos defaults + } + } + + public static void save() { + try { + Files.createDirectories(PATH.getParent()); + Files.writeString(PATH, GSON.toJson(config)); + } catch (Exception ignored) {} + } +} diff --git a/src/main/java/com/straice/smoothdoors/mixin/DoorBlockMixin.java b/src/main/java/com/straice/smoothdoors/mixin/DoorBlockMixin.java new file mode 100644 index 0000000..c0b27e1 --- /dev/null +++ b/src/main/java/com/straice/smoothdoors/mixin/DoorBlockMixin.java @@ -0,0 +1,63 @@ +package com.straice.smoothdoors.mixin; + +import com.straice.smoothdoors.config.SddConfigManager; +import com.straice.smoothdoors.util.ConnectedUtil; +import com.straice.smoothdoors.util.SyncGuard; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.DoorBlock; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.state.property.Properties; +import net.minecraft.util.ActionResult; +import net.minecraft.util.hit.BlockHitResult; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import net.minecraft.world.block.WireOrientation; +import org.jetbrains.annotations.Nullable; +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 org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(DoorBlock.class) +public class DoorBlockMixin { + + @Inject(method = "onUse", at = @At("TAIL")) + private void sdd$onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, BlockHitResult hit, + CallbackInfoReturnable cir) { + if (world.isClient) return; + if (!SddConfigManager.get().connectDoors) return; + if (SyncGuard.isIn()) return; + + BlockPos lower = ConnectedUtil.getDoorLowerPos(pos, state); + BlockState lowerNow = world.getBlockState(lower); + if (!lowerNow.contains(Properties.OPEN)) return; + + boolean openNow = lowerNow.get(Properties.OPEN); + BlockPos mateLower = ConnectedUtil.findPairedDoorLower(world, lower, lowerNow); + if (mateLower == null) return; + + SyncGuard.run(() -> ConnectedUtil.setDoorOpen(world, mateLower, openNow)); + } + + // 1.21.8+: neighborUpdate(..., @Nullable WireOrientation wireOrientation, boolean notify) + @Inject(method = "neighborUpdate", at = @At("TAIL")) + private void sdd$neighborUpdate(BlockState state, World world, BlockPos pos, Block block, + @Nullable WireOrientation wireOrientation, boolean notify, CallbackInfo ci) { + if (world.isClient) return; + if (!SddConfigManager.get().connectDoors) return; + if (!SddConfigManager.get().redstoneDoubleDoors) return; + if (SyncGuard.isIn()) return; + + BlockPos lower = ConnectedUtil.getDoorLowerPos(pos, state); + BlockState lowerNow = world.getBlockState(lower); + if (!lowerNow.contains(Properties.OPEN)) return; + + boolean openNow = lowerNow.get(Properties.OPEN); + BlockPos mateLower = ConnectedUtil.findPairedDoorLower(world, lower, lowerNow); + if (mateLower == null) return; + + SyncGuard.run(() -> ConnectedUtil.setDoorOpen(world, mateLower, openNow)); + } +} diff --git a/src/main/java/com/straice/smoothdoors/mixin/ExampleMixin.java b/src/main/java/com/straice/smoothdoors/mixin/ExampleMixin.java deleted file mode 100644 index c0881df..0000000 --- a/src/main/java/com/straice/smoothdoors/mixin/ExampleMixin.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.straice.smoothdoors.mixin; - -import net.minecraft.server.MinecraftServer; -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(MinecraftServer.class) -public class ExampleMixin { - @Inject(at = @At("HEAD"), method = "loadWorld") - private void init(CallbackInfo info) { - // This code is injected into the start of MinecraftServer.loadWorld()V - } -} \ No newline at end of file diff --git a/src/main/java/com/straice/smoothdoors/mixin/FenceGateBlockMixin.java b/src/main/java/com/straice/smoothdoors/mixin/FenceGateBlockMixin.java new file mode 100644 index 0000000..5be8351 --- /dev/null +++ b/src/main/java/com/straice/smoothdoors/mixin/FenceGateBlockMixin.java @@ -0,0 +1,61 @@ +package com.straice.smoothdoors.mixin; + +import com.straice.smoothdoors.config.SddConfigManager; +import com.straice.smoothdoors.util.ConnectedUtil; +import com.straice.smoothdoors.util.SyncGuard; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.FenceGateBlock; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.state.property.Properties; +import net.minecraft.util.ActionResult; +import net.minecraft.util.hit.BlockHitResult; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import net.minecraft.world.block.WireOrientation; +import org.jetbrains.annotations.Nullable; +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 org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(FenceGateBlock.class) +public class FenceGateBlockMixin { + + @Inject(method = "onUse", at = @At("TAIL")) + private void sdd$onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, BlockHitResult hit, + CallbackInfoReturnable cir) { + if (world.isClient) return; + if (!SddConfigManager.get().connectFenceGates) return; + if (SyncGuard.isIn()) return; + + BlockState now = world.getBlockState(pos); + if (!now.contains(Properties.OPEN)) return; + + boolean openNow = now.get(Properties.OPEN); + BlockPos mate = ConnectedUtil.findPairedFenceGate(world, pos, now); + if (mate == null) return; + + SyncGuard.run(() -> ConnectedUtil.setFenceGateOpen(world, mate, openNow)); + } + + // 1.21.8+: neighborUpdate(..., @Nullable WireOrientation wireOrientation, boolean notify) + @Inject(method = "neighborUpdate", at = @At("TAIL")) + private void sdd$neighborUpdate(BlockState state, World world, BlockPos pos, Block block, + @Nullable WireOrientation wireOrientation, boolean notify, CallbackInfo ci) { + if (world.isClient) return; + if (!SddConfigManager.get().connectFenceGates) return; + if (!SddConfigManager.get().redstoneDoubleFenceGates) return; + if (SyncGuard.isIn()) return; + + BlockState now = world.getBlockState(pos); + if (!now.contains(Properties.OPEN)) return; + + boolean openNow = now.get(Properties.OPEN); + BlockPos mate = ConnectedUtil.findPairedFenceGate(world, pos, now); + if (mate == null) return; + + SyncGuard.run(() -> ConnectedUtil.setFenceGateOpen(world, mate, openNow)); + } +} diff --git a/src/main/java/com/straice/smoothdoors/mixin/TrapdoorBlockMixin.java b/src/main/java/com/straice/smoothdoors/mixin/TrapdoorBlockMixin.java new file mode 100644 index 0000000..82385ec --- /dev/null +++ b/src/main/java/com/straice/smoothdoors/mixin/TrapdoorBlockMixin.java @@ -0,0 +1,61 @@ +package com.straice.smoothdoors.mixin; + +import com.straice.smoothdoors.config.SddConfigManager; +import com.straice.smoothdoors.util.ConnectedUtil; +import com.straice.smoothdoors.util.SyncGuard; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.TrapdoorBlock; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.state.property.Properties; +import net.minecraft.util.ActionResult; +import net.minecraft.util.hit.BlockHitResult; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import net.minecraft.world.block.WireOrientation; +import org.jetbrains.annotations.Nullable; +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 org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(TrapdoorBlock.class) +public class TrapdoorBlockMixin { + + @Inject(method = "onUse", at = @At("TAIL")) + private void sdd$onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, BlockHitResult hit, + CallbackInfoReturnable cir) { + if (world.isClient) return; + if (!SddConfigManager.get().connectTrapdoors) return; + if (SyncGuard.isIn()) return; + + BlockState now = world.getBlockState(pos); + if (!now.contains(Properties.OPEN)) return; + + boolean openNow = now.get(Properties.OPEN); + BlockPos mate = ConnectedUtil.findPairedTrapdoor(world, pos, now); + if (mate == null) return; + + SyncGuard.run(() -> ConnectedUtil.setTrapdoorOpen(world, mate, openNow)); + } + + // 1.21.8+: neighborUpdate(..., @Nullable WireOrientation wireOrientation, boolean notify) + @Inject(method = "neighborUpdate", at = @At("TAIL")) + private void sdd$neighborUpdate(BlockState state, World world, BlockPos pos, Block block, + @Nullable WireOrientation wireOrientation, boolean notify, CallbackInfo ci) { + if (world.isClient) return; + if (!SddConfigManager.get().connectTrapdoors) return; + if (!SddConfigManager.get().redstoneDoubleTrapdoors) return; + if (SyncGuard.isIn()) return; + + BlockState now = world.getBlockState(pos); + if (!now.contains(Properties.OPEN)) return; + + boolean openNow = now.get(Properties.OPEN); + BlockPos mate = ConnectedUtil.findPairedTrapdoor(world, pos, now); + if (mate == null) return; + + SyncGuard.run(() -> ConnectedUtil.setTrapdoorOpen(world, mate, openNow)); + } +} diff --git a/src/main/java/com/straice/smoothdoors/util/ConnectedUtil.java b/src/main/java/com/straice/smoothdoors/util/ConnectedUtil.java new file mode 100644 index 0000000..7e802af --- /dev/null +++ b/src/main/java/com/straice/smoothdoors/util/ConnectedUtil.java @@ -0,0 +1,126 @@ +package com.straice.smoothdoors.util; + +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.DoorBlock; +import net.minecraft.block.FenceGateBlock; +import net.minecraft.block.TrapdoorBlock; +import net.minecraft.block.enums.BlockHalf; +import net.minecraft.block.enums.DoorHinge; +import net.minecraft.block.enums.DoubleBlockHalf; +import net.minecraft.state.property.Properties; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.world.World; + +public final class ConnectedUtil { + private ConnectedUtil() {} + + // ===== DOORS ===== + + public static BlockPos getDoorLowerPos(BlockPos pos, BlockState state) { + DoubleBlockHalf half = state.get(Properties.DOUBLE_BLOCK_HALF); + return half == DoubleBlockHalf.LOWER ? pos : pos.down(); + } + + public static BlockPos findPairedDoorLower(World world, BlockPos lowerPos, BlockState lowerState) { + if (!(lowerState.getBlock() instanceof DoorBlock)) return null; + + Direction facing = lowerState.get(Properties.HORIZONTAL_FACING); + DoorHinge hinge = lowerState.get(Properties.DOOR_HINGE); + + Direction side = (hinge == DoorHinge.LEFT) + ? facing.rotateYClockwise() + : facing.rotateYCounterclockwise(); + + BlockPos otherLower = lowerPos.offset(side); + BlockState other = world.getBlockState(otherLower); + + if (other.getBlock() != lowerState.getBlock()) return null; + if (!(other.getBlock() instanceof DoorBlock)) return null; + if (other.get(Properties.DOUBLE_BLOCK_HALF) != DoubleBlockHalf.LOWER) return null; + if (other.get(Properties.HORIZONTAL_FACING) != facing) return null; + if (other.get(Properties.DOOR_HINGE) == hinge) return null; + + return otherLower; + } + + public static void setDoorOpen(World world, BlockPos doorLower, boolean open) { + BlockState lower = world.getBlockState(doorLower); + if (!(lower.getBlock() instanceof DoorBlock)) return; + + BlockPos upperPos = doorLower.up(); + BlockState upper = world.getBlockState(upperPos); + + world.setBlockState(doorLower, lower.with(Properties.OPEN, open), Block.NOTIFY_LISTENERS); + + if (upper.getBlock() == lower.getBlock()) { + world.setBlockState(upperPos, upper.with(Properties.OPEN, open), Block.NOTIFY_LISTENERS); + } + } + + // ===== TRAPDOORS ===== + + public static BlockPos findPairedTrapdoor(World world, BlockPos pos, BlockState state) { + if (!(state.getBlock() instanceof TrapdoorBlock)) return null; + + Direction facing = state.get(Properties.HORIZONTAL_FACING); + BlockHalf half = state.get(Properties.BLOCK_HALF); + + BlockPos left = pos.offset(facing.rotateYCounterclockwise()); + BlockPos right = pos.offset(facing.rotateYClockwise()); + + BlockPos mate = matchTrapdoor(world, state, left, facing, half); + if (mate != null) return mate; + return matchTrapdoor(world, state, right, facing, half); + } + + private static BlockPos matchTrapdoor(World world, BlockState a, BlockPos candidate, Direction facing, BlockHalf half) { + BlockState b = world.getBlockState(candidate); + if (b.getBlock() != a.getBlock()) return null; + if (!(b.getBlock() instanceof TrapdoorBlock)) return null; + + if (b.get(Properties.HORIZONTAL_FACING) != facing) return null; + if (b.get(Properties.BLOCK_HALF) != half) return null; + + return candidate; + } + + public static void setTrapdoorOpen(World world, BlockPos pos, boolean open) { + BlockState st = world.getBlockState(pos); + if (!(st.getBlock() instanceof TrapdoorBlock)) return; + + world.setBlockState(pos, st.with(Properties.OPEN, open), Block.NOTIFY_LISTENERS); + } + + // ===== FENCE GATES ===== + + public static BlockPos findPairedFenceGate(World world, BlockPos pos, BlockState state) { + if (!(state.getBlock() instanceof FenceGateBlock)) return null; + + Direction facing = state.get(Properties.HORIZONTAL_FACING); + + BlockPos left = pos.offset(facing.rotateYCounterclockwise()); + BlockPos right = pos.offset(facing.rotateYClockwise()); + + BlockPos mate = matchFenceGate(world, state, left, facing); + if (mate != null) return mate; + return matchFenceGate(world, state, right, facing); + } + + private static BlockPos matchFenceGate(World world, BlockState a, BlockPos candidate, Direction facing) { + BlockState b = world.getBlockState(candidate); + if (b.getBlock() != a.getBlock()) return null; + if (!(b.getBlock() instanceof FenceGateBlock)) return null; + + if (b.get(Properties.HORIZONTAL_FACING) != facing) return null; + return candidate; + } + + public static void setFenceGateOpen(World world, BlockPos pos, boolean open) { + BlockState st = world.getBlockState(pos); + if (!(st.getBlock() instanceof FenceGateBlock)) return; + + world.setBlockState(pos, st.with(Properties.OPEN, open), Block.NOTIFY_LISTENERS); + } +} diff --git a/src/main/java/com/straice/smoothdoors/util/SyncGuard.java b/src/main/java/com/straice/smoothdoors/util/SyncGuard.java new file mode 100644 index 0000000..a8e5305 --- /dev/null +++ b/src/main/java/com/straice/smoothdoors/util/SyncGuard.java @@ -0,0 +1,16 @@ +package com.straice.smoothdoors.util; + +public final class SyncGuard { + private static final ThreadLocal IN = ThreadLocal.withInitial(() -> false); + + private SyncGuard() {} + + public static boolean isIn() { return IN.get(); } + + public static void run(Runnable r) { + if (isIn()) return; + IN.set(true); + try { r.run(); } + finally { IN.set(false); } + } +} diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 52041fd..a616814 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -1,38 +1,31 @@ { - "schemaVersion": 1, - "id": "smooth-double-doors", - "version": "${version}", - "name": "Smooth Double Doors", - "description": "This is an example description! Tell everyone what your mod is about!", - "authors": [ - "Me!" - ], - "contact": { - "homepage": "https://fabricmc.net/", - "sources": "https://github.com/FabricMC/fabric-example-mod" - }, - "license": "CC0-1.0", - "icon": "assets/smooth-double-doors/icon.png", - "environment": "*", - "entrypoints": { - "main": [ - "com.straice.smoothdoors.SmoothDoubleDoors" - ], - "client": [ - "com.straice.smoothdoors.SmoothDoubleDoorsClient" - ] - }, - "mixins": [ - "smooth-double-doors.mixins.json", - { - "config": "smooth-double-doors.client.mixins.json", - "environment": "client" - } - ], - "depends": { - "fabricloader": ">=0.18.4", - "minecraft": "~1.21.8", - "java": ">=21", - "fabric-api": "*" - } + "schemaVersion": 1, + "id": "smooth-double-doors", + "version": "${version}", + "name": "Smooth Double Doors", + "description": "Double doors + double trapdoors + smooth animations (no resource pack).", + "authors": ["DekinDev"], + "contact": { + "sources": "https://git.straice.com/DekinDev/Smooth-Double-Doors", + "issues": "https://git.straice.com/DekinDev/Smooth-Double-Doors/issues" + }, + "license": "MPL-2.0", + "icon": "assets/smooth-double-doors/icon.png", + "environment": "*", + "entrypoints": { + "main": ["com.straice.smoothdoors.SmoothDoubleDoors"], + "client": ["com.straice.smoothdoors.SmoothDoubleDoorsClient"], + "modmenu": ["com.straice.smoothdoors.compat.ModMenuIntegration"] + }, + "mixins": [ + "smooth-double-doors.mixins.json", + "smooth-double-doors.client.mixins.json" + ], + "depends": { + "fabricloader": ">=0.18.4", + "minecraft": ">=1.21.8 <=1.21.11", + "java": ">=21", + "fabric-api": "*", + "modmenu": "*" + } } \ No newline at end of file diff --git a/src/main/resources/smooth-double-doors.mixins.json b/src/main/resources/smooth-double-doors.mixins.json index c99a263..25eb904 100644 --- a/src/main/resources/smooth-double-doors.mixins.json +++ b/src/main/resources/smooth-double-doors.mixins.json @@ -1,14 +1,13 @@ { - "required": true, - "package": "com.straice.smoothdoors.mixin", - "compatibilityLevel": "JAVA_21", - "mixins": [ - "ExampleMixin" - ], - "injectors": { - "defaultRequire": 1 - }, - "overwrites": { - "requireAnnotations": true - } + "required": true, + "package": "com.straice.smoothdoors.mixin", + "compatibilityLevel": "JAVA_21", + "mixins": [ + "DoorBlockMixin", + "TrapdoorBlockMixin", + "FenceGateBlockMixin" + ], + "injectors": { + "defaultRequire": 1 + } } \ No newline at end of file