package net.minecraft.world.level.block; import com.google.common.collect.Lists; import com.mojang.serialization.MapCodec; import java.util.List; import java.util.Map; import java.util.WeakHashMap; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.particles.DustParticleOptions; import net.minecraft.server.level.ServerLevel; import net.minecraft.util.RandomSource; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.Level; 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.BooleanProperty; import net.minecraft.world.level.redstone.ExperimentalRedstoneUtils; import net.minecraft.world.level.redstone.Orientation; import org.jetbrains.annotations.Nullable; public class RedstoneTorchBlock extends BaseTorchBlock { public static final MapCodec CODEC = simpleCodec(RedstoneTorchBlock::new); public static final BooleanProperty LIT = BlockStateProperties.LIT; private static final Map> RECENT_TOGGLES = new WeakHashMap(); public static final int RECENT_TOGGLE_TIMER = 60; public static final int MAX_RECENT_TOGGLES = 8; public static final int RESTART_DELAY = 160; private static final int TOGGLE_DELAY = 2; @Override public MapCodec codec() { return CODEC; } protected RedstoneTorchBlock(BlockBehaviour.Properties properties) { super(properties); this.registerDefaultState(this.stateDefinition.any().setValue(LIT, true)); } @Override protected void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean movedByPiston) { this.notifyNeighbors(level, pos, state); } private void notifyNeighbors(Level level, BlockPos blockPos, BlockState blockState) { Orientation orientation = this.randomOrientation(level, blockState); for (Direction direction : Direction.values()) { level.updateNeighborsAt(blockPos.relative(direction), this, ExperimentalRedstoneUtils.withFront(orientation, direction)); } } @Override protected void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean movedByPiston) { if (!movedByPiston) { this.notifyNeighbors(level, pos, state); } } @Override protected int getSignal(BlockState state, BlockGetter level, BlockPos pos, Direction direction) { return state.getValue(LIT) && Direction.UP != direction ? 15 : 0; } protected boolean hasNeighborSignal(Level level, BlockPos pos, BlockState state) { return level.hasSignal(pos.below(), Direction.DOWN); } @Override protected void tick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) { boolean bl = this.hasNeighborSignal(level, pos, state); List list = (List)RECENT_TOGGLES.get(level); while (list != null && !list.isEmpty() && level.getGameTime() - ((RedstoneTorchBlock.Toggle)list.get(0)).when > 60L) { list.remove(0); } if ((Boolean)state.getValue(LIT)) { if (bl) { level.setBlock(pos, state.setValue(LIT, false), 3); if (isToggledTooFrequently(level, pos, true)) { level.levelEvent(1502, pos, 0); level.scheduleTick(pos, level.getBlockState(pos).getBlock(), 160); } } } else if (!bl && !isToggledTooFrequently(level, pos, false)) { level.setBlock(pos, state.setValue(LIT, true), 3); } } @Override protected void neighborChanged(BlockState blockState, Level level, BlockPos blockPos, Block block, @Nullable Orientation orientation, boolean bl) { if ((Boolean)blockState.getValue(LIT) == this.hasNeighborSignal(level, blockPos, blockState) && !level.getBlockTicks().willTickThisTick(blockPos, this)) { level.scheduleTick(blockPos, this, 2); } } @Override protected int getDirectSignal(BlockState state, BlockGetter level, BlockPos pos, Direction direction) { return direction == Direction.DOWN ? state.getSignal(level, pos, direction) : 0; } @Override protected boolean isSignalSource(BlockState state) { return true; } @Override public void animateTick(BlockState state, Level level, BlockPos pos, RandomSource random) { if ((Boolean)state.getValue(LIT)) { double d = pos.getX() + 0.5 + (random.nextDouble() - 0.5) * 0.2; double e = pos.getY() + 0.7 + (random.nextDouble() - 0.5) * 0.2; double f = pos.getZ() + 0.5 + (random.nextDouble() - 0.5) * 0.2; level.addParticle(DustParticleOptions.REDSTONE, d, e, f, 0.0, 0.0, 0.0); } } @Override protected void createBlockStateDefinition(StateDefinition.Builder builder) { builder.add(LIT); } private static boolean isToggledTooFrequently(Level level, BlockPos pos, boolean logToggle) { List list = (List)RECENT_TOGGLES.computeIfAbsent(level, blockGetter -> Lists.newArrayList()); if (logToggle) { list.add(new RedstoneTorchBlock.Toggle(pos.immutable(), level.getGameTime())); } int i = 0; for (RedstoneTorchBlock.Toggle toggle : list) { if (toggle.pos.equals(pos)) { if (++i >= 8) { return true; } } } return false; } @Nullable protected Orientation randomOrientation(Level level, BlockState blockState) { return ExperimentalRedstoneUtils.initialOrientation(level, null, Direction.UP); } public static class Toggle { final BlockPos pos; final long when; public Toggle(BlockPos pos, long when) { this.pos = pos; this.when = when; } } }