9
0
mirror of https://github.com/Samsuik/Sakura.git synced 2025-12-19 14:59:30 +00:00

Fix issues with legacy mechanics version and implement panda wire

This commit is contained in:
Samsuik
2025-09-27 21:27:43 +01:00
parent 632be894a4
commit be7650ff6d
5 changed files with 404 additions and 32 deletions

View File

@@ -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

View File

@@ -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;

View File

@@ -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 {

View File

@@ -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;

View File

@@ -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.
* <p>
* Not to be confused with {@link net.minecraft.world.level.redstone.ExperimentalRedstoneWireEvaluator}
*
* @see <a href="https://gist.github.com/Panda4994/70ed6d39c89396570e062e4404a8d518">The original Pandawire gist</a>
*/
@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<BlockPos> turnOff = new ArrayDeque<>();
/** Positions that need to be checked to be turned on **/
private final ArrayDeque<BlockPos> turnOn = new ArrayDeque<>();
/** Positions of wire that was updated already (Ordering determines update order and is therefore required!) **/
private final LinkedHashSet<BlockPos> updatedRedstoneWire = new LinkedHashSet<>();
/** Ordered arrays of the facings; Needed for the update order.
* I went with a vertical-first order here, but vertical last would work to.
* However it should be avoided to update the vertical axis between the horizontal ones as this would cause unneeded directional behavior. **/
private static final Direction[] FACINGS_HORIZONTAL = {Direction.WEST, Direction.EAST, Direction.NORTH, Direction.SOUTH};
private static final Direction[] FACINGS_VERTICAL = {Direction.DOWN, Direction.UP};
private static final Direction[] FACINGS = ArrayUtils.addAll(FACINGS_VERTICAL, FACINGS_HORIZONTAL);
/** Offsets for all surrounding blocks that need to receive updates **/
private static final Vec3i[] SURROUNDING_BLOCKS_OFFSET;
static {
final Set<Vec3i> set = Sets.newLinkedHashSet();
for (final Direction facing : FACINGS) {
set.add(facing.getUnitVec3i());
}
final Set<Vec3i> offsets = Sets.newLinkedHashSet(set);
for (final Vec3i neighborOffset : offsets) {
for (final Vec3i adjacentOffset : offsets) {
set.add(neighborOffset.offset(adjacentOffset));
}
}
set.remove(Vec3i.ZERO);
SURROUNDING_BLOCKS_OFFSET = set.toArray(new Vec3i[0]);
}
public PandaRedstoneWireEvaluator(final RedStoneWireBlock wireBlock) {
super(wireBlock);
}
@Override
public void updatePowerStrength(
final Level level,
final BlockPos pos,
final BlockState state,
final @Nullable Orientation orientation,
final boolean updateShape
) {
// Recalculate the connected wires
this.calculateCurrentChanges(level, pos, level.getBlockState(pos));
// Set to collect all the updates, to only execute them once. Ordering required.
final Set<BlockPos> blocksNeedingUpdate = Sets.newLinkedHashSet();
// Add the needed updates
for (final BlockPos updatedWirePos : this.updatedRedstoneWire) {
this.addBlocksNeedingUpdate(level, updatedWirePos, blocksNeedingUpdate);
}
// Add all other updates to keep known behaviors
// They are added in a backwards order because it preserves a commonly used behavior with the update order
for (final BlockPos updatedWirePos : this.updatedRedstoneWire.reversed()) {
this.addAllSurroundingBlocks(updatedWirePos, blocksNeedingUpdate);
}
// Remove updates on the wires as they just were updated
blocksNeedingUpdate.removeAll(this.updatedRedstoneWire);
/*
* Avoid unnecessary updates on the just updated wires
* A huge scale test showed about 40% more ticks per second
* It's probably less in normal usage but likely still worth it
*/
this.updatedRedstoneWire.clear();
// Execute updates
for (final BlockPos neighborPos : blocksNeedingUpdate) {
level.neighborChanged(neighborPos, this.wireBlock, null);
}
}
/**
* Turns on or off all connected wires
*
* @param level World
* @param sourcePos Position of the wire that received the update
* @param sourceState Current state of this block
*/
private void calculateCurrentChanges(final Level level, final BlockPos sourcePos, final BlockState sourceState) {
// Turn off all connected wires first if needed
if (sourceState.is(this.wireBlock)) {
this.turnOff.add(sourcePos);
} else {
// In case this wire was removed, check the surrounding wires
this.checkSurroundingWires(level, sourcePos);
}
while (!this.turnOff.isEmpty()) {
final BlockPos pos = this.turnOff.poll();
final BlockState state = level.getBlockState(pos);
final int oldPower = state.getValue(RedStoneWireBlock.POWER);
final int blockPower = this.getBlockSignal(level, pos);
final int wirePower = this.getIncomingWireSignal(level, pos);
final int newPower = Math.max(blockPower, wirePower);
// Power lowered?
if (newPower < oldPower) {
// If it's still powered by a direct source (but weaker) mark for turn on
if (blockPower > 0 && !this.turnOn.contains(pos)) {
this.turnOn.add(pos);
}
// Set all the way to off for now, because wires that were powered by this need to update first
this.setWireState(level, pos, state, 0);
// Power rose?
} else if (newPower > oldPower) {
// Set new Power
this.setWireState(level, pos, state, newPower);
}
// Check if surrounding wires need to change based on the current/new state and add them to the lists
this.checkSurroundingWires(level, pos);
}
while (!this.turnOn.isEmpty()) {
final BlockPos pos = this.turnOn.poll();
final BlockState state = level.getBlockState(pos);
final int oldPower = state.getValue(RedStoneWireBlock.POWER);
final int blockPower = this.getBlockSignal(level, pos);
final int wirePower = this.getIncomingWireSignal(level, pos);
int newPower = Math.max(blockPower, wirePower);
if (oldPower != newPower) {
final BlockRedstoneEvent event = new BlockRedstoneEvent(CraftBlock.at(level, pos), oldPower, newPower);
newPower = event.callEvent() ? event.getNewCurrent() : oldPower;
}
if (newPower > oldPower) {
this.setWireState(level, pos, state, newPower);
}
// Check if surrounding wires need to change based on the current/new state and add them to the lists
this.checkSurroundingWires(level, pos);
}
this.turnOff.clear();
}
/**
* Checks if an wire needs to be marked for update depending on the power next to it
*
* @author panda
*
* @param level World
* @param pos Position of the wire that might need to change
* @param otherPower Power of the wire next to it
*/
private void addWireToList(final Level level, final BlockPos pos, final int otherPower) {
final BlockState state = level.getBlockState(pos);
if (state.is(this.wireBlock)) {
final int power = state.getValue(RedStoneWireBlock.POWER);
// Could get powered stronger by the neighbor?
if (power < otherPower - 1 && !this.turnOn.contains(pos)) {
// Mark for turn on check.
this.turnOn.add(pos);
}
// Should have powered the neighbor? Probably was powered by it and is in turn off phase.
if (power > otherPower && !this.turnOff.contains(pos)) {
// Mark for turn off check.
this.turnOff.add(pos);
}
}
}
/**
* Checks if the wires around need to get updated depending on this wires state.
* Checks all wires below before the same layer before on top to keep
* some more rotational symmetry around the y-axis.
*
* @author panda
*
* @param level World
* @param pos Position of the wire
*/
private void checkSurroundingWires(final Level level, final BlockPos pos) {
final BlockState state = level.getBlockState(pos);
int ownPower = 0;
if (state.is(this.wireBlock)) {
ownPower = state.getValue(RedStoneWireBlock.POWER);
}
// Check wires on the same layer first as they appear closer to the wire
for (final Direction facingHorizontal : FACINGS_HORIZONTAL) {
this.addWireToList(level, pos.relative(facingHorizontal), ownPower);
}
for (final Direction facingVertical : FACINGS_VERTICAL) {
final BlockPos offsetPos = pos.relative(facingVertical);
final boolean solidBlock = level.getBlockState(offsetPos).isRedstoneConductor(level, offsetPos);
for (final Direction facingHorizontal : FACINGS_HORIZONTAL) {
final BlockPos adjacentPos = offsetPos.relative(facingHorizontal);
// wire can travel upwards if the block on top doesn't cut the wire (is non-solid)
// it can travel down if the block below is solid and the block "diagonal" doesn't cut off the wire (is non-solid)
if (facingVertical == Direction.UP && !solidBlock) {
this.addWireToList(level, adjacentPos, ownPower);
} else if (facingVertical == Direction.DOWN && solidBlock && !level.getBlockState(adjacentPos).isRedstoneConductor(level, adjacentPos)) {
this.addWireToList(level, adjacentPos, ownPower);
}
}
}
}
/**
* Adds all blocks that need to receive an update from a redstone change in this position.
* This means only blocks that actually could change.
*
* @author panda
*
* @param level World
* @param pos Position of the wire
* @param set Set to add the update positions too
*/
private void addBlocksNeedingUpdate(final Level level, final BlockPos pos, final Set<BlockPos> set) {
final Set<Direction> connectedSides = this.getSidesToPower(level, pos);
// Add the blocks next to the wire first (closest first order)
for (final Direction facing : FACINGS) {
final BlockPos offsetPos = pos.relative(facing);
final BlockState state = level.getBlockState(offsetPos);
// canConnectTo() is not the nicest solution here as it returns true for e.g. the front of a repeater
// canBlockBePowereFromSide catches these cases
final boolean flag = connectedSides.contains(facing.getOpposite()) || facing == Direction.DOWN;
if (flag || (facing.getAxis().isHorizontal() && RedStoneWireBlock.shouldConnectTo(state, facing))) {
if (canBlockBePoweredFromSide(state, facing, true)) {
set.add(offsetPos);
}
}
// Later add blocks around the surrounding blocks that get powered
if (flag && state.isRedstoneConductor(level, offsetPos)) {
for (final Direction adjacentFacing : FACINGS) {
final BlockPos adjacentPos = offsetPos.relative(adjacentFacing);
if (canBlockBePoweredFromSide(level.getBlockState(adjacentPos), adjacentFacing, false)) {
set.add(adjacentPos);
}
}
}
}
}
/**
* Checks if a block can get powered from a side.
* This behavior would better be implemented per block type as follows:
* - return false as default. (blocks that are not affected by redstone don't need to be updated, it doesn't really hurt if they are either)
* - return true for all blocks that can get powered from all side and change based on it (doors, fence gates, trap doors, note blocks, lamps, dropper, hopper, TNT, rails, possibly more)
* - implement own logic for pistons, repeaters, comparators and redstone torches
* The current implementation was chosen to keep everything in one class.
* <p>
* 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<Direction> getSidesToPower(final Level level, final BlockPos pos) {
final Set<Direction> sidesToPower = new HashSet<>();
final BlockState state = level.getBlockState(pos);
for (final Direction direction : FACINGS_HORIZONTAL) {
final RedstoneSide side = state.getValue(RedStoneWireBlock.PROPERTY_BY_DIRECTION.get(direction));
if (side.isConnected()) {
sidesToPower.add(direction);
}
}
return sidesToPower;
}
/**
* Adds all surrounding positions to a set.
* This is the neighbor blocks, as well as their neighbors
*
* @param pos
* @param set
*/
private void addAllSurroundingBlocks(final BlockPos pos, final Set<BlockPos> set) {
for (final Vec3i vect : SURROUNDING_BLOCKS_OFFSET) {
set.add(pos.offset(vect));
}
}
/**
* Sets the block state of a wire with a new power level and marks for updates
*
* @author panda
*
* @param level World
* @param pos Position at which the state needs to be set
* @param state Old state
* @param power Power it should get set to
*/
private void setWireState(final Level level, final BlockPos pos, final BlockState state, final int power) {
final BlockState newState = state.setValue(RedStoneWireBlock.POWER, power);
level.setBlock(pos, newState, Block.UPDATE_CLIENTS | Block.UPDATE_KNOWN_SHAPE);
this.updatedRedstoneWire.add(pos);
}
}