1.21.8
This commit is contained in:
@@ -11,11 +11,7 @@ base {
|
|||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
// Add repositories to retrieve artifacts from in here.
|
maven { url "https://maven.terraformersmc.com/releases/" }
|
||||||
// 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.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
loom {
|
loom {
|
||||||
@@ -39,6 +35,8 @@ dependencies {
|
|||||||
// Fabric API. This is technically optional, but you probably want it anyway.
|
// Fabric API. This is technically optional, but you probably want it anyway.
|
||||||
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_api_version}"
|
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 {
|
processResources {
|
||||||
|
|||||||
@@ -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<Boolean> cir) {
|
||||||
|
if (SddAnimator.shouldHideInChunk(pos, state)) {
|
||||||
|
// “true” para que el renderer no piense que falló
|
||||||
|
cir.setReturnValue(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<Anim> 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<Long> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<String> 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<Float> 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<Float> 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,11 +1,13 @@
|
|||||||
{
|
{
|
||||||
"required": true,
|
"required": true,
|
||||||
"package": "com.straice.smoothdoors.mixin.client",
|
"package": "com.straice.smoothdoors.mixin",
|
||||||
"compatibilityLevel": "JAVA_21",
|
"compatibilityLevel": "JAVA_21",
|
||||||
"client": [
|
"client": [
|
||||||
"ExampleClientMixin"
|
"client.ClientWorldMixin",
|
||||||
],
|
"client.BlockRenderManagerMixin",
|
||||||
"injectors": {
|
"client.WorldRendererMixin"
|
||||||
"defaultRequire": 1
|
],
|
||||||
}
|
"injectors": {
|
||||||
|
"defaultRequire": 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,8 @@ import net.fabricmc.api.ModInitializer;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.straice.smoothdoors.config.SddConfigManager;
|
||||||
|
|
||||||
public class SmoothDoubleDoors implements ModInitializer {
|
public class SmoothDoubleDoors implements ModInitializer {
|
||||||
public static final String MOD_ID = "smooth-double-doors";
|
public static final String MOD_ID = "smooth-double-doors";
|
||||||
|
|
||||||
@@ -15,10 +17,7 @@ public class SmoothDoubleDoors implements ModInitializer {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onInitialize() {
|
public void onInitialize() {
|
||||||
// This code runs as soon as Minecraft is in a mod-load-ready state.
|
SddConfigManager.load();
|
||||||
// However, some things (like resources) may still be uninitialized.
|
LOGGER.info("Smooth Double Doors loaded");
|
||||||
// Proceed with mild caution.
|
|
||||||
|
|
||||||
LOGGER.info("Hello Fabric world!");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
20
src/main/java/com/straice/smoothdoors/config/SddConfig.java
Normal file
20
src/main/java/com/straice/smoothdoors/config/SddConfig.java
Normal file
@@ -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;
|
||||||
|
}
|
||||||
@@ -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) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<ActionResult> 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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<ActionResult> 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<ActionResult> 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
126
src/main/java/com/straice/smoothdoors/util/ConnectedUtil.java
Normal file
126
src/main/java/com/straice/smoothdoors/util/ConnectedUtil.java
Normal file
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
16
src/main/java/com/straice/smoothdoors/util/SyncGuard.java
Normal file
16
src/main/java/com/straice/smoothdoors/util/SyncGuard.java
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package com.straice.smoothdoors.util;
|
||||||
|
|
||||||
|
public final class SyncGuard {
|
||||||
|
private static final ThreadLocal<Boolean> 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); }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,38 +1,31 @@
|
|||||||
{
|
{
|
||||||
"schemaVersion": 1,
|
"schemaVersion": 1,
|
||||||
"id": "smooth-double-doors",
|
"id": "smooth-double-doors",
|
||||||
"version": "${version}",
|
"version": "${version}",
|
||||||
"name": "Smooth Double Doors",
|
"name": "Smooth Double Doors",
|
||||||
"description": "This is an example description! Tell everyone what your mod is about!",
|
"description": "Double doors + double trapdoors + smooth animations (no resource pack).",
|
||||||
"authors": [
|
"authors": ["DekinDev"],
|
||||||
"Me!"
|
"contact": {
|
||||||
],
|
"sources": "https://git.straice.com/DekinDev/Smooth-Double-Doors",
|
||||||
"contact": {
|
"issues": "https://git.straice.com/DekinDev/Smooth-Double-Doors/issues"
|
||||||
"homepage": "https://fabricmc.net/",
|
},
|
||||||
"sources": "https://github.com/FabricMC/fabric-example-mod"
|
"license": "MPL-2.0",
|
||||||
},
|
"icon": "assets/smooth-double-doors/icon.png",
|
||||||
"license": "CC0-1.0",
|
"environment": "*",
|
||||||
"icon": "assets/smooth-double-doors/icon.png",
|
"entrypoints": {
|
||||||
"environment": "*",
|
"main": ["com.straice.smoothdoors.SmoothDoubleDoors"],
|
||||||
"entrypoints": {
|
"client": ["com.straice.smoothdoors.SmoothDoubleDoorsClient"],
|
||||||
"main": [
|
"modmenu": ["com.straice.smoothdoors.compat.ModMenuIntegration"]
|
||||||
"com.straice.smoothdoors.SmoothDoubleDoors"
|
},
|
||||||
],
|
"mixins": [
|
||||||
"client": [
|
"smooth-double-doors.mixins.json",
|
||||||
"com.straice.smoothdoors.SmoothDoubleDoorsClient"
|
"smooth-double-doors.client.mixins.json"
|
||||||
]
|
],
|
||||||
},
|
"depends": {
|
||||||
"mixins": [
|
"fabricloader": ">=0.18.4",
|
||||||
"smooth-double-doors.mixins.json",
|
"minecraft": ">=1.21.8 <=1.21.11",
|
||||||
{
|
"java": ">=21",
|
||||||
"config": "smooth-double-doors.client.mixins.json",
|
"fabric-api": "*",
|
||||||
"environment": "client"
|
"modmenu": "*"
|
||||||
}
|
}
|
||||||
],
|
|
||||||
"depends": {
|
|
||||||
"fabricloader": ">=0.18.4",
|
|
||||||
"minecraft": "~1.21.8",
|
|
||||||
"java": ">=21",
|
|
||||||
"fabric-api": "*"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,14 +1,13 @@
|
|||||||
{
|
{
|
||||||
"required": true,
|
"required": true,
|
||||||
"package": "com.straice.smoothdoors.mixin",
|
"package": "com.straice.smoothdoors.mixin",
|
||||||
"compatibilityLevel": "JAVA_21",
|
"compatibilityLevel": "JAVA_21",
|
||||||
"mixins": [
|
"mixins": [
|
||||||
"ExampleMixin"
|
"DoorBlockMixin",
|
||||||
],
|
"TrapdoorBlockMixin",
|
||||||
"injectors": {
|
"FenceGateBlockMixin"
|
||||||
"defaultRequire": 1
|
],
|
||||||
},
|
"injectors": {
|
||||||
"overwrites": {
|
"defaultRequire": 1
|
||||||
"requireAnnotations": true
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user