diff --git a/build-data/sakura.at b/build-data/sakura.at index d6f2a0c..f1eb370 100644 --- a/build-data/sakura.at +++ b/build-data/sakura.at @@ -22,6 +22,7 @@ public net.minecraft.world.entity.Entity addMovementThisTick(Lnet/minecraft/worl public net.minecraft.world.entity.Entity stuckSpeedMultiplier public net.minecraft.world.entity.Entity$Movement public net.minecraft.world.level.Level neighborUpdater +public net.minecraft.world.level.block.RedStoneWireBlock shouldConnectTo(Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/core/Direction;)Z public net.minecraft.world.level.block.RedStoneWireBlock turbo public net.minecraft.world.level.entity.EntityTickList entities public net.minecraft.world.level.material.FlowingFluid getLegacyLevel(Lnet/minecraft/world/level/material/FluidState;)I diff --git a/sakura-server/minecraft-patches/features/0017-Configure-cannon-physics.patch b/sakura-server/minecraft-patches/features/0017-Configure-cannon-physics.patch index 9145c58..19dafdb 100644 --- a/sakura-server/minecraft-patches/features/0017-Configure-cannon-physics.patch +++ b/sakura-server/minecraft-patches/features/0017-Configure-cannon-physics.patch @@ -543,7 +543,7 @@ index 41c036324499d4e408030d1e19f78468b6b3d8a6..ba2e7efe147b26bea5d73eac1f4b2fe6 if (!visitor.visit(blockPos, 0)) { return false; diff --git a/net/minecraft/world/level/ServerExplosion.java b/net/minecraft/world/level/ServerExplosion.java -index 16575c1222b28f7809db39d320848153e3b97044..a24e728476a64e49f2e5139fc103e19d5650d6e1 100644 +index b13aae809e974b42bc1b17f4f4455999adca6f5f..0a279388e965a8d059189a8dfdbec3ff951af876 100644 --- a/net/minecraft/world/level/ServerExplosion.java +++ b/net/minecraft/world/level/ServerExplosion.java @@ -378,6 +378,17 @@ public class ServerExplosion implements Explosion { @@ -798,15 +798,26 @@ index 0b1ec05fcd799536c8d7393bb42dc4b2f46ca48b..5968a30d21367b882501bbb70029b9e0 if (org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFormEvent(level, pos, block.defaultBlockState(), 3)) { this.fizz(level, pos); diff --git a/net/minecraft/world/level/block/RedStoneWireBlock.java b/net/minecraft/world/level/block/RedStoneWireBlock.java -index 7390a21f7c6991d3fb62fda9e88bc9212159177b..497cc0156cbd87e591f1f50090fc5fe055b71916 100644 +index 4c6c68eedba2d5acdde6cf760e53b8e81f75f001..ce27dfb14974d452f50c191a3eeec65d3deff6a3 100644 --- a/net/minecraft/world/level/block/RedStoneWireBlock.java +++ b/net/minecraft/world/level/block/RedStoneWireBlock.java -@@ -542,7 +542,7 @@ public class RedStoneWireBlock extends Block { +@@ -322,6 +322,10 @@ public class RedStoneWireBlock extends Block { + private void updatePowerStrength(Level level, BlockPos pos, BlockState state, @Nullable Orientation orientation, boolean updateShape) { + if (useExperimentalEvaluator(level)) { + new ExperimentalRedstoneWireEvaluator(this).updatePowerStrength(level, pos, state, orientation, updateShape); ++ // Sakura start - configure server mechanics ++ } else if (level.localConfig().at(pos).mechanicsTarget.isLegacy()) { ++ new me.samsuik.sakura.redstone.PandaRedstoneWireEvaluator(this).updatePowerStrength(level, pos, state, orientation, updateShape); ++ // Sakura end - configure server mechanics + } else { + this.evaluator.updatePowerStrength(level, pos, state, orientation, updateShape); + } +@@ -542,7 +546,7 @@ public class RedStoneWireBlock extends Block { @Override protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult hitResult) { - if (!player.getAbilities().mayBuild) { -+ if (!player.getAbilities().mayBuild || level.localConfig().at(pos).mechanicsTarget.before(me.samsuik.sakura.mechanics.MechanicVersion.v1_16)) { // Sakura - configure server mechanics ++ if (!player.getAbilities().mayBuild || isCross(state) && level.localConfig().at(pos).mechanicsTarget.before(me.samsuik.sakura.mechanics.MechanicVersion.v1_16)) { // Sakura - configure server mechanics return InteractionResult.PASS; } else { if (isCross(state) || isDot(state)) { @@ -916,10 +927,10 @@ index 72e39ceeca8d684569c4250263b41034362f8abd..0f723f8eb814931f949b1a467b79266e } diff --git a/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java b/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java -index 4c4dbd7ade2b8836064242bb43eeb871cac023ce..c1afb74f19ad627b02bed2e3ffec67b877625c9f 100644 +index 4c4dbd7ade2b8836064242bb43eeb871cac023ce..7816e0ab95634d93ced2dea47aa58edc087f2caa 100644 --- a/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java +++ b/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java -@@ -64,6 +64,163 @@ public class PistonMovingBlockEntity extends BlockEntity { +@@ -64,6 +64,165 @@ public class PistonMovingBlockEntity extends BlockEntity { this.isSourcePiston = isSourcePiston; } @@ -1034,6 +1045,7 @@ index 4c4dbd7ade2b8836064242bb43eeb871cac023ce..c1afb74f19ad627b02bed2e3ffec67b8 + relativeProgress * (float) direction.getStepZ() + ); + entity.move(MoverType.PISTON, pistonMovement); ++ entity.applyEffectsFromBlocks(entity.position(), entity.position()); + } + } + } @@ -1077,13 +1089,14 @@ index 4c4dbd7ade2b8836064242bb43eeb871cac023ce..c1afb74f19ad627b02bed2e3ffec67b8 + moveZ * direction.getStepZ() + ); + entity.move(MoverType.PISTON, pistonMovement); ++ entity.applyEffectsFromBlocks(entity.position(), entity.position()); + } + // Sakura end - configure server mechanics + @Override public CompoundTag getUpdateTag(HolderLookup.Provider registries) { return this.saveCustomOnly(registries); -@@ -168,6 +325,12 @@ public class PistonMovingBlockEntity extends BlockEntity { +@@ -168,6 +327,12 @@ public class PistonMovingBlockEntity extends BlockEntity { double d4 = 0.0; @@ -1096,7 +1109,7 @@ index 4c4dbd7ade2b8836064242bb43eeb871cac023ce..c1afb74f19ad627b02bed2e3ffec67b8 for (AABB aabb1 : list) { AABB movementArea = PistonMath.getMovementArea(moveByPositionAndProgress(pos, aabb1, piston), movementDirection, d); AABB boundingBox = entity.getBoundingBox(); -@@ -195,6 +358,11 @@ public class PistonMovingBlockEntity extends BlockEntity { +@@ -195,6 +360,11 @@ public class PistonMovingBlockEntity extends BlockEntity { NOCLIP.set(noClipDirection); Vec3 vec3 = entity.position(); entity.move(MoverType.PISTON, new Vec3(progress * direction.getStepX(), progress * direction.getStepY(), progress * direction.getStepZ())); @@ -1108,7 +1121,7 @@ index 4c4dbd7ade2b8836064242bb43eeb871cac023ce..c1afb74f19ad627b02bed2e3ffec67b8 entity.applyEffectsFromBlocks(vec3, entity.position()); entity.removeLatestMovementRecording(); NOCLIP.set(null); -@@ -307,12 +475,21 @@ public class PistonMovingBlockEntity extends BlockEntity { +@@ -307,12 +477,21 @@ public class PistonMovingBlockEntity extends BlockEntity { } public static void tick(Level level, BlockPos pos, BlockState state, PistonMovingBlockEntity blockEntity) { @@ -1130,7 +1143,7 @@ index 4c4dbd7ade2b8836064242bb43eeb871cac023ce..c1afb74f19ad627b02bed2e3ffec67b8 level.removeBlockEntity(pos); blockEntity.setRemoved(); if (level.getBlockState(pos).is(Blocks.MOVING_PISTON)) { -@@ -334,12 +511,22 @@ public class PistonMovingBlockEntity extends BlockEntity { +@@ -334,12 +513,22 @@ public class PistonMovingBlockEntity extends BlockEntity { } } else { float f = blockEntity.progress + 0.5F; diff --git a/sakura-server/minecraft-patches/features/0028-Cache-vanilla-and-eigencraft-redstone-wires.patch b/sakura-server/minecraft-patches/features/0028-Cache-vanilla-and-eigencraft-redstone-wires.patch index 155f941..b256ef1 100644 --- a/sakura-server/minecraft-patches/features/0028-Cache-vanilla-and-eigencraft-redstone-wires.patch +++ b/sakura-server/minecraft-patches/features/0028-Cache-vanilla-and-eigencraft-redstone-wires.patch @@ -33,7 +33,7 @@ index ff747a1ecdf3c888bca0d69de4f85dcd810b6139..d90f6aa4557b5863eba6a206226f763c } diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java -index aca28d1e2530d239b0e2b55f75c6d0e398841ea1..ed50677f6b0445ac1c7246697c5f8d949bb59437 100644 +index d9958a1358bd949b302c852bef096eb690acf702..2bc7ad463b21faed3ba25e6e127925e6b5a8cf95 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java @@ -711,6 +711,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe @@ -57,7 +57,7 @@ index 7aa5ffc32835fab4a91db464d9112785475a67e9..6804055e9344eb2ea0b2dd6318231963 protected Level( WritableLevelData levelData, diff --git a/net/minecraft/world/level/block/RedStoneWireBlock.java b/net/minecraft/world/level/block/RedStoneWireBlock.java -index 497cc0156cbd87e591f1f50090fc5fe055b71916..b124e28cefac098d42fef55b699f4db398dfa1ba 100644 +index ce27dfb14974d452f50c191a3eeec65d3deff6a3..8a53126b192dca8ee86b47a50da8273df58f31f0 100644 --- a/net/minecraft/world/level/block/RedStoneWireBlock.java +++ b/net/minecraft/world/level/block/RedStoneWireBlock.java @@ -306,6 +306,12 @@ public class RedStoneWireBlock extends Block { @@ -81,7 +81,7 @@ index 497cc0156cbd87e591f1f50090fc5fe055b71916..b124e28cefac098d42fef55b699f4db3 } } return state; -@@ -398,7 +405,7 @@ public class RedStoneWireBlock extends Block { +@@ -402,7 +409,7 @@ public class RedStoneWireBlock extends Block { @Override protected void neighborChanged(BlockState state, Level level, BlockPos pos, Block neighborBlock, @Nullable Orientation orientation, boolean movedByPiston) { @@ -90,7 +90,7 @@ index 497cc0156cbd87e591f1f50090fc5fe055b71916..b124e28cefac098d42fef55b699f4db3 // Paper start - optimize redstone (Alternate Current) // Alternate Current handles breaking of redstone wires in the WireHandler. if (level.localConfig().at(pos).paperRedstoneImplementation() == io.papermc.paper.configuration.WorldConfiguration.Misc.RedstoneImplementation.ALTERNATE_CURRENT) { // Sakura - redstone implementation api -@@ -432,8 +439,14 @@ public class RedStoneWireBlock extends Block { +@@ -436,8 +443,14 @@ public class RedStoneWireBlock extends Block { if (powerValue == 0) { return 0; } else { diff --git a/sakura-server/src/main/java/me/samsuik/sakura/mechanics/LegacyExplosionBlockClipping.java b/sakura-server/src/main/java/me/samsuik/sakura/mechanics/LegacyExplosionBlockClipping.java index 2d69bd3..d3d86d8 100644 --- a/sakura-server/src/main/java/me/samsuik/sakura/mechanics/LegacyExplosionBlockClipping.java +++ b/sakura-server/src/main/java/me/samsuik/sakura/mechanics/LegacyExplosionBlockClipping.java @@ -22,27 +22,27 @@ public final class LegacyExplosionBlockClipping { private Vec3 currentPos; private final Vec3 endPos; - private final int toX; - private final int toY; - private final int toZ; + private final int endX; + private final int endY; + private final int endZ; private LegacyExplosionBlockClipping(final Vec3 currentPos, final Vec3 endPos) { this.currentPos = currentPos; this.endPos = endPos; - this.toX = Mth.floor(endPos.x); - this.toY = Mth.floor(endPos.y); - this.toZ = Mth.floor(endPos.z); + this.endX = Mth.floor(endPos.x); + this.endY = Mth.floor(endPos.y); + this.endZ = Mth.floor(endPos.z); } - public static BlockHitResult.Type clip(final Level level, final Vec3 currentPos, final Vec3 endPos) { - final LegacyExplosionBlockClipping clipDetection = new LegacyExplosionBlockClipping(currentPos, endPos); - final BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos(); + public static BlockHitResult.Type clip(final Level level, final Vec3 startPos, final Vec3 endPos) { + final LegacyExplosionBlockClipping clipDetection = new LegacyExplosionBlockClipping(startPos, endPos); + final BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos(startPos.x(), startPos.y(), startPos.z()); LevelChunk chunk = null; int steps = 0; do { - final int chunkX = Mth.floor(currentPos.x) >> 4; - final int chunkZ = Mth.floor(currentPos.z) >> 4; + final int chunkX = mutableBlockPos.getX() >> 4; + final int chunkZ = mutableBlockPos.getZ() >> 4; if (chunk == null || chunkX != chunk.locX || chunkZ != chunk.locZ) { chunk = level.getChunkIfLoaded(chunkX, chunkZ); if (chunk == null) break; @@ -51,7 +51,7 @@ public final class LegacyExplosionBlockClipping { final BlockState state = chunk.getBlockState(mutableBlockPos); final VoxelShape shape = state.getShape(level, mutableBlockPos); for (final AABB shapeBB : shape.toAabbs()) { - if (clip(shapeBB, mutableBlockPos, currentPos, endPos)) { + if (clip(shapeBB, mutableBlockPos, clipDetection.currentPos, endPos)) { return HitResult.Type.BLOCK; } } @@ -64,9 +64,9 @@ public final class LegacyExplosionBlockClipping { final int currX = mutableBlockPos.getX(); final int currY = mutableBlockPos.getY(); final int currZ = mutableBlockPos.getZ(); - final int toX = this.toX; - final int toY = this.toY; - final int toZ = this.toZ; + final int toX = this.endX; + final int toY = this.endY; + final int toZ = this.endZ; if (currX == toX && currY == toY && currZ == toZ) { return false; @@ -135,9 +135,9 @@ public final class LegacyExplosionBlockClipping { } mutableBlockPos.set( - Mth.floor(currPos.x) - (moveDir == Direction.EAST ? 1 : 0), - Mth.floor(currPos.y) - (moveDir == Direction.UP ? 1 : 0), - Mth.floor(currPos.z) - (moveDir == Direction.SOUTH ? 1 : 0) + Mth.floor(newCurrentPos.x) - (moveDir == Direction.EAST ? 1 : 0), + Mth.floor(newCurrentPos.y) - (moveDir == Direction.UP ? 1 : 0), + Mth.floor(newCurrentPos.z) - (moveDir == Direction.SOUTH ? 1 : 0) ); this.currentPos = newCurrentPos; diff --git a/sakura-server/src/main/java/me/samsuik/sakura/redstone/PandaRedstoneWireEvaluator.java b/sakura-server/src/main/java/me/samsuik/sakura/redstone/PandaRedstoneWireEvaluator.java new file mode 100644 index 0000000..129dade --- /dev/null +++ b/sakura-server/src/main/java/me/samsuik/sakura/redstone/PandaRedstoneWireEvaluator.java @@ -0,0 +1,358 @@ +package me.samsuik.sakura.redstone; + +import com.google.common.collect.Sets; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.Vec3i; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.*; +import net.minecraft.world.level.block.piston.PistonBaseBlock; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.RedstoneSide; +import net.minecraft.world.level.redstone.Orientation; +import net.minecraft.world.level.redstone.RedstoneWireEvaluator; +import org.apache.commons.lang3.ArrayUtils; +import org.bukkit.craftbukkit.block.CraftBlock; +import org.bukkit.event.block.BlockRedstoneEvent; +import org.jetbrains.annotations.Nullable; +import org.jspecify.annotations.NullMarked; + +import java.util.ArrayDeque; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.Set; + +/** + * This is a PandaWire implementation for modern Minecraft. + *
+ * Not to be confused with {@link net.minecraft.world.level.redstone.ExperimentalRedstoneWireEvaluator}
+ *
+ * @see The original Pandawire gist
+ */
+@NullMarked
+public final class PandaRedstoneWireEvaluator extends RedstoneWireEvaluator {
+ /*
+ * I considered replacing the lists with LinkedHashSets for faster lookup,
+ * but an artificial test showed barely any difference in performance
+ */
+ /** Positions that need to be turned off **/
+ private final ArrayDeque
+ * Why is this extra check needed?
+ * 1. It makes sure that many old behaviors still work (QC + Pistons).
+ * 2. It prevents updates from "jumping".
+ * Or rather it prevents this wire to update a block that would get powered by the next one of the same line.
+ * This is to prefer as it makes understanding the update order of the wire really easy. The signal "travels" from the power source.
+ *
+ * @author panda
+ *
+ * @param state State of the block
+ * @param side Side from which it gets powered
+ * @param isWire True if it's powered by a wire directly, False if through a block
+ * @return True if the block can change based on the power level it gets on the given side, false otherwise
+ */
+ private boolean canBlockBePoweredFromSide(final BlockState state, final Direction side, final boolean isWire) {
+ Block block = state.getBlock();
+ if (state.isAir()) return false;
+ if (block instanceof PistonBaseBlock && state.getValue(PistonBaseBlock.FACING) == side.getOpposite()) return false;
+ if (block instanceof DiodeBlock && state.getValue(DiodeBlock.FACING) != side.getOpposite())
+ return isWire && block instanceof ComparatorBlock && state.getValue(ComparatorBlock.FACING).getAxis() != side.getAxis() && side.getAxis().isHorizontal();
+ return !(state.getBlock() instanceof RedstoneWallTorchBlock) || (!isWire && state.getValue(RedstoneWallTorchBlock.FACING) == side);
+ }
+
+ /**
+ * Creates a list of all horizontal sides that can get powered by a wire.
+ * The list is ordered the same as the facingsHorizontal.
+ *
+ * @param level World
+ * @param pos Position of the wire
+ * @return List of all facings that can get powered by this wire
+ */
+ private Set