2025-11-24 22:52:51 +03:00

471 lines
23 KiB
Java

/*
* Decompiled with CFR 0.152.
*
* Could not load the following classes:
* com.google.common.collect.ImmutableMap
* com.google.common.collect.Maps
* com.mojang.serialization.MapCodec
* org.jspecify.annotations.Nullable
*/
package net.minecraft.world.level.block;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.mojang.serialization.MapCodec;
import java.util.Map;
import java.util.function.Function;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.Vec3i;
import net.minecraft.core.particles.DustParticleOptions;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.ARGB;
import net.minecraft.util.Mth;
import net.minecraft.util.RandomSource;
import net.minecraft.util.Util;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.flag.FeatureFlags;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.ScheduledTickAccess;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.Mirror;
import net.minecraft.world.level.block.ObserverBlock;
import net.minecraft.world.level.block.RepeaterBlock;
import net.minecraft.world.level.block.Rotation;
import net.minecraft.world.level.block.TrapDoorBlock;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.EnumProperty;
import net.minecraft.world.level.block.state.properties.IntegerProperty;
import net.minecraft.world.level.block.state.properties.RedstoneSide;
import net.minecraft.world.level.redstone.DefaultRedstoneWireEvaluator;
import net.minecraft.world.level.redstone.ExperimentalRedstoneUtils;
import net.minecraft.world.level.redstone.ExperimentalRedstoneWireEvaluator;
import net.minecraft.world.level.redstone.Orientation;
import net.minecraft.world.level.redstone.RedstoneWireEvaluator;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;
import org.jspecify.annotations.Nullable;
public class RedStoneWireBlock
extends Block {
public static final MapCodec<RedStoneWireBlock> CODEC = RedStoneWireBlock.simpleCodec(RedStoneWireBlock::new);
public static final EnumProperty<RedstoneSide> NORTH = BlockStateProperties.NORTH_REDSTONE;
public static final EnumProperty<RedstoneSide> EAST = BlockStateProperties.EAST_REDSTONE;
public static final EnumProperty<RedstoneSide> SOUTH = BlockStateProperties.SOUTH_REDSTONE;
public static final EnumProperty<RedstoneSide> WEST = BlockStateProperties.WEST_REDSTONE;
public static final IntegerProperty POWER = BlockStateProperties.POWER;
public static final Map<Direction, EnumProperty<RedstoneSide>> PROPERTY_BY_DIRECTION = ImmutableMap.copyOf((Map)Maps.newEnumMap(Map.of(Direction.NORTH, NORTH, Direction.EAST, EAST, Direction.SOUTH, SOUTH, Direction.WEST, WEST)));
private static final int[] COLORS = Util.make(new int[16], list -> {
for (int i = 0; i <= 15; ++i) {
float power;
float red = power * 0.6f + ((power = (float)i / 15.0f) > 0.0f ? 0.4f : 0.3f);
float green = Mth.clamp(power * power * 0.7f - 0.5f, 0.0f, 1.0f);
float blue = Mth.clamp(power * power * 0.6f - 0.7f, 0.0f, 1.0f);
list[i] = ARGB.colorFromFloat(1.0f, red, green, blue);
}
});
private static final float PARTICLE_DENSITY = 0.2f;
private final Function<BlockState, VoxelShape> shapes;
private final BlockState crossState;
private final RedstoneWireEvaluator evaluator = new DefaultRedstoneWireEvaluator(this);
private boolean shouldSignal = true;
public MapCodec<RedStoneWireBlock> codec() {
return CODEC;
}
public RedStoneWireBlock(BlockBehaviour.Properties properties) {
super(properties);
this.registerDefaultState((BlockState)((BlockState)((BlockState)((BlockState)((BlockState)((BlockState)this.stateDefinition.any()).setValue(NORTH, RedstoneSide.NONE)).setValue(EAST, RedstoneSide.NONE)).setValue(SOUTH, RedstoneSide.NONE)).setValue(WEST, RedstoneSide.NONE)).setValue(POWER, 0));
this.shapes = this.makeShapes();
this.crossState = (BlockState)((BlockState)((BlockState)((BlockState)this.defaultBlockState().setValue(NORTH, RedstoneSide.SIDE)).setValue(EAST, RedstoneSide.SIDE)).setValue(SOUTH, RedstoneSide.SIDE)).setValue(WEST, RedstoneSide.SIDE);
}
private Function<BlockState, VoxelShape> makeShapes() {
boolean height = true;
int width = 10;
VoxelShape dot = Block.column(10.0, 0.0, 1.0);
Map<Direction, VoxelShape> floor = Shapes.rotateHorizontal(Block.boxZ(10.0, 0.0, 1.0, 0.0, 8.0));
Map<Direction, VoxelShape> up = Shapes.rotateHorizontal(Block.boxZ(10.0, 16.0, 0.0, 1.0));
return this.getShapeForEachState(state -> {
VoxelShape shape = dot;
for (Map.Entry<Direction, EnumProperty<RedstoneSide>> entry : PROPERTY_BY_DIRECTION.entrySet()) {
shape = switch ((RedstoneSide)state.getValue(entry.getValue())) {
default -> throw new MatchException(null, null);
case RedstoneSide.UP -> Shapes.or(shape, (VoxelShape)floor.get(entry.getKey()), (VoxelShape)up.get(entry.getKey()));
case RedstoneSide.SIDE -> Shapes.or(shape, (VoxelShape)floor.get(entry.getKey()));
case RedstoneSide.NONE -> shape;
};
}
return shape;
}, POWER);
}
@Override
protected VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
return this.shapes.apply(state);
}
@Override
public BlockState getStateForPlacement(BlockPlaceContext context) {
return this.getConnectionState(context.getLevel(), this.crossState, context.getClickedPos());
}
private BlockState getConnectionState(BlockGetter level, BlockState state, BlockPos pos) {
boolean eastWestEmpty;
boolean wasDot = RedStoneWireBlock.isDot(state);
state = this.getMissingConnections(level, (BlockState)this.defaultBlockState().setValue(POWER, state.getValue(POWER)), pos);
if (wasDot && RedStoneWireBlock.isDot(state)) {
return state;
}
boolean north = state.getValue(NORTH).isConnected();
boolean south = state.getValue(SOUTH).isConnected();
boolean east = state.getValue(EAST).isConnected();
boolean west = state.getValue(WEST).isConnected();
boolean northSouthEmpty = !north && !south;
boolean bl = eastWestEmpty = !east && !west;
if (!west && northSouthEmpty) {
state = (BlockState)state.setValue(WEST, RedstoneSide.SIDE);
}
if (!east && northSouthEmpty) {
state = (BlockState)state.setValue(EAST, RedstoneSide.SIDE);
}
if (!north && eastWestEmpty) {
state = (BlockState)state.setValue(NORTH, RedstoneSide.SIDE);
}
if (!south && eastWestEmpty) {
state = (BlockState)state.setValue(SOUTH, RedstoneSide.SIDE);
}
return state;
}
private BlockState getMissingConnections(BlockGetter level, BlockState state, BlockPos pos) {
boolean canConnectUp = !level.getBlockState(pos.above()).isRedstoneConductor(level, pos);
for (Direction direction : Direction.Plane.HORIZONTAL) {
if (((RedstoneSide)state.getValue(PROPERTY_BY_DIRECTION.get(direction))).isConnected()) continue;
RedstoneSide sideConnection = this.getConnectingSide(level, pos, direction, canConnectUp);
state = (BlockState)state.setValue(PROPERTY_BY_DIRECTION.get(direction), sideConnection);
}
return state;
}
@Override
protected BlockState updateShape(BlockState state, LevelReader level, ScheduledTickAccess ticks, BlockPos pos, Direction directionToNeighbour, BlockPos neighbourPos, BlockState neighbourState, RandomSource random) {
if (directionToNeighbour == Direction.DOWN) {
if (!this.canSurviveOn(level, neighbourPos, neighbourState)) {
return Blocks.AIR.defaultBlockState();
}
return state;
}
if (directionToNeighbour == Direction.UP) {
return this.getConnectionState(level, state, pos);
}
RedstoneSide sideConnection = this.getConnectingSide(level, pos, directionToNeighbour);
if (sideConnection.isConnected() == ((RedstoneSide)state.getValue(PROPERTY_BY_DIRECTION.get(directionToNeighbour))).isConnected() && !RedStoneWireBlock.isCross(state)) {
return (BlockState)state.setValue(PROPERTY_BY_DIRECTION.get(directionToNeighbour), sideConnection);
}
return this.getConnectionState(level, (BlockState)((BlockState)this.crossState.setValue(POWER, state.getValue(POWER))).setValue(PROPERTY_BY_DIRECTION.get(directionToNeighbour), sideConnection), pos);
}
private static boolean isCross(BlockState state) {
return state.getValue(NORTH).isConnected() && state.getValue(SOUTH).isConnected() && state.getValue(EAST).isConnected() && state.getValue(WEST).isConnected();
}
private static boolean isDot(BlockState state) {
return !state.getValue(NORTH).isConnected() && !state.getValue(SOUTH).isConnected() && !state.getValue(EAST).isConnected() && !state.getValue(WEST).isConnected();
}
@Override
protected void updateIndirectNeighbourShapes(BlockState state, LevelAccessor level, BlockPos pos, @Block.UpdateFlags int updateFlags, int updateLimit) {
BlockPos.MutableBlockPos blockPos = new BlockPos.MutableBlockPos();
for (Direction direction : Direction.Plane.HORIZONTAL) {
RedstoneSide value = (RedstoneSide)state.getValue(PROPERTY_BY_DIRECTION.get(direction));
if (value == RedstoneSide.NONE || level.getBlockState(blockPos.setWithOffset((Vec3i)pos, direction)).is(this)) continue;
blockPos.move(Direction.DOWN);
BlockState blockStateDown = level.getBlockState(blockPos);
if (blockStateDown.is(this)) {
Vec3i neighborPos = blockPos.relative(direction.getOpposite());
level.neighborShapeChanged(direction.getOpposite(), blockPos, (BlockPos)neighborPos, level.getBlockState((BlockPos)neighborPos), updateFlags, updateLimit);
}
blockPos.setWithOffset((Vec3i)pos, direction).move(Direction.UP);
BlockState blockStateUp = level.getBlockState(blockPos);
if (!blockStateUp.is(this)) continue;
Vec3i neighborPos = blockPos.relative(direction.getOpposite());
level.neighborShapeChanged(direction.getOpposite(), blockPos, (BlockPos)neighborPos, level.getBlockState((BlockPos)neighborPos), updateFlags, updateLimit);
}
}
private RedstoneSide getConnectingSide(BlockGetter level, BlockPos pos, Direction direction) {
return this.getConnectingSide(level, pos, direction, !level.getBlockState(pos.above()).isRedstoneConductor(level, pos));
}
private RedstoneSide getConnectingSide(BlockGetter level, BlockPos pos, Direction direction, boolean canConnectUp) {
BlockPos relativePos = pos.relative(direction);
BlockState relativeState = level.getBlockState(relativePos);
if (canConnectUp) {
boolean isPlaceableAbove;
boolean bl = isPlaceableAbove = relativeState.getBlock() instanceof TrapDoorBlock || this.canSurviveOn(level, relativePos, relativeState);
if (isPlaceableAbove && RedStoneWireBlock.shouldConnectTo(level.getBlockState(relativePos.above()))) {
if (relativeState.isFaceSturdy(level, relativePos, direction.getOpposite())) {
return RedstoneSide.UP;
}
return RedstoneSide.SIDE;
}
}
if (RedStoneWireBlock.shouldConnectTo(relativeState, direction) || !relativeState.isRedstoneConductor(level, relativePos) && RedStoneWireBlock.shouldConnectTo(level.getBlockState(relativePos.below()))) {
return RedstoneSide.SIDE;
}
return RedstoneSide.NONE;
}
@Override
protected boolean canSurvive(BlockState state, LevelReader level, BlockPos pos) {
BlockPos below = pos.below();
BlockState belowState = level.getBlockState(below);
return this.canSurviveOn(level, below, belowState);
}
private boolean canSurviveOn(BlockGetter level, BlockPos relativePos, BlockState relativeState) {
return relativeState.isFaceSturdy(level, relativePos, Direction.UP) || relativeState.is(Blocks.HOPPER);
}
private void updatePowerStrength(Level level, BlockPos pos, BlockState state, @Nullable Orientation orientation, boolean shapeUpdateWiresAroundInitialPosition) {
if (RedStoneWireBlock.useExperimentalEvaluator(level)) {
new ExperimentalRedstoneWireEvaluator(this).updatePowerStrength(level, pos, state, orientation, shapeUpdateWiresAroundInitialPosition);
} else {
this.evaluator.updatePowerStrength(level, pos, state, orientation, shapeUpdateWiresAroundInitialPosition);
}
}
public int getBlockSignal(Level level, BlockPos pos) {
this.shouldSignal = false;
int blockSignal = level.getBestNeighborSignal(pos);
this.shouldSignal = true;
return blockSignal;
}
private void checkCornerChangeAt(Level level, BlockPos pos) {
if (!level.getBlockState(pos).is(this)) {
return;
}
level.updateNeighborsAt(pos, this);
for (Direction direction : Direction.values()) {
level.updateNeighborsAt(pos.relative(direction), this);
}
}
@Override
protected void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean movedByPiston) {
if (oldState.is(state.getBlock()) || level.isClientSide()) {
return;
}
this.updatePowerStrength(level, pos, state, null, true);
for (Direction direction : Direction.Plane.VERTICAL) {
level.updateNeighborsAt(pos.relative(direction), this);
}
this.updateNeighborsOfNeighboringWires(level, pos);
}
@Override
protected void affectNeighborsAfterRemoval(BlockState state, ServerLevel level, BlockPos pos, boolean movedByPiston) {
if (movedByPiston) {
return;
}
for (Direction direction : Direction.values()) {
level.updateNeighborsAt(pos.relative(direction), this);
}
this.updatePowerStrength(level, pos, state, null, false);
this.updateNeighborsOfNeighboringWires(level, pos);
}
private void updateNeighborsOfNeighboringWires(Level level, BlockPos pos) {
for (Direction direction : Direction.Plane.HORIZONTAL) {
this.checkCornerChangeAt(level, pos.relative(direction));
}
for (Direction direction : Direction.Plane.HORIZONTAL) {
BlockPos target = pos.relative(direction);
if (level.getBlockState(target).isRedstoneConductor(level, target)) {
this.checkCornerChangeAt(level, target.above());
continue;
}
this.checkCornerChangeAt(level, target.below());
}
}
@Override
protected void neighborChanged(BlockState state, Level level, BlockPos pos, Block block, @Nullable Orientation orientation, boolean movedByPiston) {
if (level.isClientSide()) {
return;
}
if (block == this && RedStoneWireBlock.useExperimentalEvaluator(level)) {
return;
}
if (state.canSurvive(level, pos)) {
this.updatePowerStrength(level, pos, state, orientation, false);
} else {
RedStoneWireBlock.dropResources(state, level, pos);
level.removeBlock(pos, false);
}
}
private static boolean useExperimentalEvaluator(Level level) {
return level.enabledFeatures().contains(FeatureFlags.REDSTONE_EXPERIMENTS);
}
@Override
protected int getDirectSignal(BlockState state, BlockGetter level, BlockPos pos, Direction direction) {
if (!this.shouldSignal) {
return 0;
}
return state.getSignal(level, pos, direction);
}
@Override
protected int getSignal(BlockState state, BlockGetter level, BlockPos pos, Direction direction) {
if (!this.shouldSignal || direction == Direction.DOWN) {
return 0;
}
int power = state.getValue(POWER);
if (power == 0) {
return 0;
}
if (direction == Direction.UP || ((RedstoneSide)this.getConnectionState(level, state, pos).getValue(PROPERTY_BY_DIRECTION.get(direction.getOpposite()))).isConnected()) {
return power;
}
return 0;
}
protected static boolean shouldConnectTo(BlockState blockState) {
return RedStoneWireBlock.shouldConnectTo(blockState, null);
}
protected static boolean shouldConnectTo(BlockState blockState, @Nullable Direction direction) {
if (blockState.is(Blocks.REDSTONE_WIRE)) {
return true;
}
if (blockState.is(Blocks.REPEATER)) {
Direction repeaterDirection = (Direction)blockState.getValue(RepeaterBlock.FACING);
return repeaterDirection == direction || repeaterDirection.getOpposite() == direction;
}
if (blockState.is(Blocks.OBSERVER)) {
return direction == blockState.getValue(ObserverBlock.FACING);
}
return blockState.isSignalSource() && direction != null;
}
@Override
protected boolean isSignalSource(BlockState state) {
return this.shouldSignal;
}
public static int getColorForPower(int power) {
return COLORS[power];
}
private static void spawnParticlesAlongLine(Level level, RandomSource random, BlockPos pos, int color, Direction side, Direction along, float from, float to) {
float span = to - from;
if (random.nextFloat() >= 0.2f * span) {
return;
}
float sideOfBlock = 0.4375f;
float positionOnLine = from + span * random.nextFloat();
double x = 0.5 + (double)(0.4375f * (float)side.getStepX()) + (double)(positionOnLine * (float)along.getStepX());
double y = 0.5 + (double)(0.4375f * (float)side.getStepY()) + (double)(positionOnLine * (float)along.getStepY());
double z = 0.5 + (double)(0.4375f * (float)side.getStepZ()) + (double)(positionOnLine * (float)along.getStepZ());
level.addParticle(new DustParticleOptions(color, 1.0f), (double)pos.getX() + x, (double)pos.getY() + y, (double)pos.getZ() + z, 0.0, 0.0, 0.0);
}
@Override
public void animateTick(BlockState state, Level level, BlockPos pos, RandomSource random) {
int power = state.getValue(POWER);
if (power == 0) {
return;
}
block4: for (Direction horizontal : Direction.Plane.HORIZONTAL) {
RedstoneSide connection = (RedstoneSide)state.getValue(PROPERTY_BY_DIRECTION.get(horizontal));
switch (connection) {
case UP: {
RedStoneWireBlock.spawnParticlesAlongLine(level, random, pos, COLORS[power], horizontal, Direction.UP, -0.5f, 0.5f);
}
case SIDE: {
RedStoneWireBlock.spawnParticlesAlongLine(level, random, pos, COLORS[power], Direction.DOWN, horizontal, 0.0f, 0.5f);
continue block4;
}
}
RedStoneWireBlock.spawnParticlesAlongLine(level, random, pos, COLORS[power], Direction.DOWN, horizontal, 0.0f, 0.3f);
}
}
@Override
protected BlockState rotate(BlockState state, Rotation rotation) {
switch (rotation) {
case CLOCKWISE_180: {
return (BlockState)((BlockState)((BlockState)((BlockState)state.setValue(NORTH, state.getValue(SOUTH))).setValue(EAST, state.getValue(WEST))).setValue(SOUTH, state.getValue(NORTH))).setValue(WEST, state.getValue(EAST));
}
case COUNTERCLOCKWISE_90: {
return (BlockState)((BlockState)((BlockState)((BlockState)state.setValue(NORTH, state.getValue(EAST))).setValue(EAST, state.getValue(SOUTH))).setValue(SOUTH, state.getValue(WEST))).setValue(WEST, state.getValue(NORTH));
}
case CLOCKWISE_90: {
return (BlockState)((BlockState)((BlockState)((BlockState)state.setValue(NORTH, state.getValue(WEST))).setValue(EAST, state.getValue(NORTH))).setValue(SOUTH, state.getValue(EAST))).setValue(WEST, state.getValue(SOUTH));
}
}
return state;
}
@Override
protected BlockState mirror(BlockState state, Mirror mirror) {
switch (mirror) {
case LEFT_RIGHT: {
return (BlockState)((BlockState)state.setValue(NORTH, state.getValue(SOUTH))).setValue(SOUTH, state.getValue(NORTH));
}
case FRONT_BACK: {
return (BlockState)((BlockState)state.setValue(EAST, state.getValue(WEST))).setValue(WEST, state.getValue(EAST));
}
}
return super.mirror(state, mirror);
}
@Override
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
builder.add(NORTH, EAST, SOUTH, WEST, POWER);
}
@Override
protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult hitResult) {
if (!player.getAbilities().mayBuild) {
return InteractionResult.PASS;
}
if (RedStoneWireBlock.isCross(state) || RedStoneWireBlock.isDot(state)) {
BlockState newState = RedStoneWireBlock.isCross(state) ? this.defaultBlockState() : this.crossState;
newState = (BlockState)newState.setValue(POWER, state.getValue(POWER));
if ((newState = this.getConnectionState(level, newState, pos)) != state) {
level.setBlock(pos, newState, 3);
this.updatesOnShapeChange(level, pos, state, newState);
return InteractionResult.SUCCESS;
}
}
return InteractionResult.PASS;
}
private void updatesOnShapeChange(Level level, BlockPos pos, BlockState oldState, BlockState newState) {
Orientation orientation = ExperimentalRedstoneUtils.initialOrientation(level, null, Direction.UP);
for (Direction direction : Direction.Plane.HORIZONTAL) {
BlockPos relativePos = pos.relative(direction);
if (((RedstoneSide)oldState.getValue(PROPERTY_BY_DIRECTION.get(direction))).isConnected() == ((RedstoneSide)newState.getValue(PROPERTY_BY_DIRECTION.get(direction))).isConnected() || !level.getBlockState(relativePos).isRedstoneConductor(level, relativePos)) continue;
level.updateNeighborsAtExceptFromFacing(relativePos, newState.getBlock(), direction.getOpposite(), ExperimentalRedstoneUtils.withFront(orientation, direction));
}
}
}