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:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user