/* * Decompiled with CFR 0.152. */ package com.vinlanx.explosionoverhaul; import com.vinlanx.explosionoverhaul.BlockIndexManager; import com.vinlanx.explosionoverhaul.Config; import com.vinlanx.explosionoverhaul.ModSounds; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.Vec3i; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.sounds.SoundEvent; import net.minecraft.sounds.SoundSource; import net.minecraft.util.RandomSource; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.properties.BlockStateProperties; import net.minecraft.world.level.block.state.properties.Property; public class RedstoneLampEffects { private static final Map> perPlayerDelayedLampEffects = new ConcurrentHashMap>(); private static final int MIN_INITIAL_FLICKERS = 2; private static final int MAX_INITIAL_FLICKERS = 5; private static final int MIN_FLICKER_PHASE_TICKS = 1; private static final int MAX_FLICKER_PHASE_TICKS = 4; private static final int BASE_MIN_LONG_OFF_TICKS = 20; private static final int FINAL_FLICKER_COUNT = 4; private static final int LAMP_UPDATE_BATCH_SIZE_PER_EFFECT = 25; private static final int LAMP_UPDATE_BUDGET_PER_PLAYER_PER_TICK = 200; private static final int MAX_CONCURRENT_LAMP_GROUPS_PER_PLAYER = 250; /* * WARNING - Removed try catching itself - possible behaviour change. */ public static void triggerLampFlicker(ServerLevel level, ServerPlayer player, float explosionPower, long initialDelayTicks, double distanceToExplosionOrigin) { List playerSpecificEffects; if (!((Boolean)Config.COMMON.enableLampFlicker.get()).booleanValue()) { return; } RandomSource random = level.m_213780_(); BlockPos playerPos = player.m_20183_(); UUID playerId = player.m_20148_(); int lampEffectRadius = (Integer)Config.COMMON.lampFlickerSearchRadius.get(); long currentMinLongOffTicks = 20L; long currentMaxLongOffTicks = distanceToExplosionOrigin < 500.0 ? 200L : (distanceToExplosionOrigin < 1000.0 ? 100L : 60L); if (currentMinLongOffTicks > currentMaxLongOffTicks) { currentMinLongOffTicks = Math.max(1L, currentMaxLongOffTicks / 2L); } if (currentMaxLongOffTicks <= 0L) { currentMaxLongOffTicks = 1L; } long sharedLongOffDuration = currentMaxLongOffTicks <= currentMinLongOffTicks ? currentMinLongOffTicks : currentMinLongOffTicks + (long)random.m_188503_((int)(currentMaxLongOffTicks - currentMinLongOffTicks + 1L)); ArrayList potentialLampGroupsFoundThisExplosion = new ArrayList(); List allCandidates = BlockIndexManager.getNearby(level, playerPos, lampEffectRadius, BlockIndexManager.BlockType.LAMP); if (allCandidates.isEmpty()) { return; } ArrayList litLampCandidates = new ArrayList(); for (BlockPos pos : allCandidates) { Object state; if (!level.m_46749_(pos) || !(state = level.m_8055_(pos)).m_60713_(Blocks.f_50261_) || !((Boolean)state.m_61143_((Property)BlockStateProperties.f_61443_)).booleanValue()) continue; litLampCandidates.add(pos); } if (litLampCandidates.isEmpty()) { return; } HashSet lampsAlreadyInAGroup = new HashSet(); HashSet candidateSet = new HashSet(litLampCandidates); for (BlockPos startPos : litLampCandidates) { BlockState startState; if (lampsAlreadyInAGroup.contains(startPos) || !level.m_46749_(startPos) || !(startState = level.m_8055_(startPos)).m_60713_(Blocks.f_50261_)) continue; ArrayList currentLampGroupPositions = new ArrayList(); LinkedList toProcess = new LinkedList(); toProcess.add(startPos); lampsAlreadyInAGroup.add(startPos); while (!toProcess.isEmpty()) { BlockPos lampInQueue = (BlockPos)toProcess.poll(); currentLampGroupPositions.add(lampInQueue); for (Direction direction : Direction.values()) { BlockState neighborState; BlockPos neighborPos = lampInQueue.m_5484_(direction, 1); if (!candidateSet.contains(neighborPos) || lampsAlreadyInAGroup.contains(neighborPos) || !level.m_46749_(neighborPos) || !(neighborState = level.m_8055_(neighborPos)).m_60713_(Blocks.f_50261_) || !((Boolean)neighborState.m_61143_((Property)BlockStateProperties.f_61443_)).booleanValue()) continue; lampsAlreadyInAGroup.add(neighborPos); toProcess.add(neighborPos); } } if (currentLampGroupPositions.isEmpty()) continue; double distanceSq = startPos.m_123331_((Vec3i)playerPos); long lampSpecificInitialDelay = initialDelayTicks + (long)random.m_188503_(10); potentialLampGroupsFoundThisExplosion.add(new PotentialLampGroup(startPos, currentLampGroupPositions, true, sharedLongOffDuration, distanceSq, lampSpecificInitialDelay)); } if (potentialLampGroupsFoundThisExplosion.isEmpty()) { return; } potentialLampGroupsFoundThisExplosion.sort(Comparator.comparingDouble(PotentialLampGroup::distanceSqToPlayer)); List list = playerSpecificEffects = perPlayerDelayedLampEffects.computeIfAbsent(playerId, k -> Collections.synchronizedList(new ArrayList())); synchronized (list) { int currentEffectCountForPlayer = playerSpecificEffects.size(); int availableSlotsForThisPlayer = 250 - currentEffectCountForPlayer; if (availableSlotsForThisPlayer <= 0) { return; } int numToAdd = Math.min(potentialLampGroupsFoundThisExplosion.size(), availableSlotsForThisPlayer); for (int i = 0; i < numToAdd; ++i) { PotentialLampGroup chosenGroup = (PotentialLampGroup)potentialLampGroupsFoundThisExplosion.get(i); playerSpecificEffects.add(new DelayedLampEffect(level, chosenGroup.representativePos(), chosenGroup.groupPositions(), chosenGroup.initialDelay(), chosenGroup.originallyLit(), chosenGroup.predeterminedLongOffDuration(), random)); } } } /* * WARNING - Removed try catching itself - possible behaviour change. */ public static void onServerTick() { for (Map.Entry> entry : perPlayerDelayedLampEffects.entrySet()) { List playerEffects = entry.getValue(); int[] playerBudget = new int[]{200}; List list = playerEffects; synchronized (list) { Iterator effectIterator = playerEffects.iterator(); while (effectIterator.hasNext() && playerBudget[0] > 0) { DelayedLampEffect effect = effectIterator.next(); if (!effect.tick(playerBudget)) continue; effectIterator.remove(); } } if (!playerEffects.isEmpty()) continue; perPlayerDelayedLampEffects.remove(entry.getKey(), playerEffects); } } private record PotentialLampGroup(BlockPos representativePos, List groupPositions, boolean originallyLit, long predeterminedLongOffDuration, double distanceSqToPlayer, long initialDelay) { } private static class DelayedLampEffect { public ServerLevel level; public BlockPos representativeLampPos; public List groupLampPositions; public boolean originallyLit; private final RandomSource random; private LampEffectStage currentStage; private long ticksUntilNextStageAction; private int flickersRemainingInCycle; private long actualLongOffDuration; private int lampUpdateProgressIndex = 0; private boolean currentPhaseTargetLitState; public DelayedLampEffect(ServerLevel level, BlockPos representativeLampPos, List allGroupPositions, long initialDelayTicks, boolean lit, long predeterminedLongOffDuration, RandomSource randomSource) { this.level = level; this.representativeLampPos = representativeLampPos; this.groupLampPositions = new ArrayList(allGroupPositions); this.originallyLit = lit; this.actualLongOffDuration = predeterminedLongOffDuration; this.random = randomSource; this.currentStage = LampEffectStage.AWAITING_INITIAL_DELAY; this.ticksUntilNextStageAction = initialDelayTicks; } private void playFlickerSound() { if (!ModSounds.LAMP_FLICKER_SOUNDS.isEmpty() && !this.groupLampPositions.isEmpty()) { SoundEvent flickerSound = (SoundEvent)ModSounds.LAMP_FLICKER_SOUNDS.get(this.random.m_188503_(ModSounds.LAMP_FLICKER_SOUNDS.size())).get(); this.level.m_5594_(null, this.representativeLampPos, flickerSound, SoundSource.BLOCKS, 0.5f, 1.0f + (this.random.m_188501_() - 0.5f) * 0.3f); } } private int getRandomPhaseDuration(int minTicks, int maxTicks) { if (minTicks >= maxTicks) { return minTicks; } return this.random.m_188503_(maxTicks - minTicks + 1) + minTicks; } private boolean processLampUpdateBatch(int[] budget) { if (this.groupLampPositions.isEmpty()) { this.lampUpdateProgressIndex = 0; return true; } for (int processedInThisEffectCall = 0; this.lampUpdateProgressIndex < this.groupLampPositions.size() && processedInThisEffectCall < 25 && budget[0] > 0; ++processedInThisEffectCall) { boolean currentlyLit; BlockPos posInGroup = this.groupLampPositions.get(this.lampUpdateProgressIndex); BlockState currentBlockState = this.level.m_8055_(posInGroup); if (currentBlockState.m_60713_(Blocks.f_50261_) && (currentlyLit = ((Boolean)currentBlockState.m_61143_((Property)BlockStateProperties.f_61443_)).booleanValue()) != this.currentPhaseTargetLitState) { this.level.m_7731_(posInGroup, (BlockState)currentBlockState.m_61124_((Property)BlockStateProperties.f_61443_, (Comparable)Boolean.valueOf(this.currentPhaseTargetLitState)), 2); budget[0] = budget[0] - 1; } ++this.lampUpdateProgressIndex; } if (this.lampUpdateProgressIndex >= this.groupLampPositions.size()) { this.lampUpdateProgressIndex = 0; return true; } return false; } public boolean tick(int[] budget) { if (this.currentStage == LampEffectStage.FINISHED) { return true; } --this.ticksUntilNextStageAction; if (this.ticksUntilNextStageAction <= 0L) { LampEffectStage nextStageDecision = this.currentStage; boolean phaseUpdateCompleted = false; switch (this.currentStage) { case AWAITING_INITIAL_DELAY: { this.flickersRemainingInCycle = 2 + this.random.m_188503_(4); nextStageDecision = LampEffectStage.INITIAL_FLICKER_PREPARE_OFF; this.ticksUntilNextStageAction = 1L; break; } case INITIAL_FLICKER_PREPARE_OFF: { this.currentPhaseTargetLitState = false; this.lampUpdateProgressIndex = 0; this.playFlickerSound(); nextStageDecision = LampEffectStage.INITIAL_FLICKER_UPDATING_TO_OFF; this.ticksUntilNextStageAction = 1L; break; } case INITIAL_FLICKER_UPDATING_TO_OFF: { phaseUpdateCompleted = this.processLampUpdateBatch(budget); if (phaseUpdateCompleted) { nextStageDecision = LampEffectStage.INITIAL_FLICKER_WAITING_OFF; this.ticksUntilNextStageAction = this.getRandomPhaseDuration(1, 4); break; } this.ticksUntilNextStageAction = 1L; break; } case INITIAL_FLICKER_WAITING_OFF: { nextStageDecision = LampEffectStage.INITIAL_FLICKER_PREPARE_ON; this.ticksUntilNextStageAction = 1L; break; } case INITIAL_FLICKER_PREPARE_ON: { this.currentPhaseTargetLitState = true; this.lampUpdateProgressIndex = 0; this.playFlickerSound(); nextStageDecision = LampEffectStage.INITIAL_FLICKER_UPDATING_TO_ON; this.ticksUntilNextStageAction = 1L; break; } case INITIAL_FLICKER_UPDATING_TO_ON: { phaseUpdateCompleted = this.processLampUpdateBatch(budget); if (phaseUpdateCompleted) { --this.flickersRemainingInCycle; if (this.flickersRemainingInCycle > 0) { nextStageDecision = LampEffectStage.INITIAL_FLICKER_WAITING_ON; this.ticksUntilNextStageAction = this.getRandomPhaseDuration(1, 4); break; } nextStageDecision = LampEffectStage.LONG_OFF_PREPARE; this.ticksUntilNextStageAction = 1L; break; } this.ticksUntilNextStageAction = 1L; break; } case INITIAL_FLICKER_WAITING_ON: { nextStageDecision = LampEffectStage.INITIAL_FLICKER_PREPARE_OFF; this.ticksUntilNextStageAction = 1L; break; } case LONG_OFF_PREPARE: { this.currentPhaseTargetLitState = false; this.lampUpdateProgressIndex = 0; nextStageDecision = LampEffectStage.LONG_OFF_UPDATING_TO_OFF; this.ticksUntilNextStageAction = 1L; break; } case LONG_OFF_UPDATING_TO_OFF: { phaseUpdateCompleted = this.processLampUpdateBatch(budget); if (phaseUpdateCompleted) { nextStageDecision = LampEffectStage.LONG_OFF_WAITING; this.ticksUntilNextStageAction = this.actualLongOffDuration; break; } this.ticksUntilNextStageAction = 1L; break; } case LONG_OFF_WAITING: { this.flickersRemainingInCycle = 4; nextStageDecision = LampEffectStage.FINAL_FLICKER_PREPARE_ON; this.ticksUntilNextStageAction = 1L; break; } case FINAL_FLICKER_PREPARE_ON: { this.currentPhaseTargetLitState = true; this.lampUpdateProgressIndex = 0; this.playFlickerSound(); nextStageDecision = LampEffectStage.FINAL_FLICKER_UPDATING_TO_ON; this.ticksUntilNextStageAction = 1L; break; } case FINAL_FLICKER_UPDATING_TO_ON: { phaseUpdateCompleted = this.processLampUpdateBatch(budget); if (phaseUpdateCompleted) { nextStageDecision = LampEffectStage.FINAL_FLICKER_WAITING_ON; this.ticksUntilNextStageAction = this.getRandomPhaseDuration(1, 4); break; } this.ticksUntilNextStageAction = 1L; break; } case FINAL_FLICKER_WAITING_ON: { nextStageDecision = LampEffectStage.FINAL_FLICKER_PREPARE_OFF; this.ticksUntilNextStageAction = 1L; break; } case FINAL_FLICKER_PREPARE_OFF: { this.currentPhaseTargetLitState = false; this.lampUpdateProgressIndex = 0; this.playFlickerSound(); nextStageDecision = LampEffectStage.FINAL_FLICKER_UPDATING_TO_OFF; this.ticksUntilNextStageAction = 1L; break; } case FINAL_FLICKER_UPDATING_TO_OFF: { phaseUpdateCompleted = this.processLampUpdateBatch(budget); if (phaseUpdateCompleted) { --this.flickersRemainingInCycle; if (this.flickersRemainingInCycle > 0) { nextStageDecision = LampEffectStage.FINAL_FLICKER_WAITING_OFF; this.ticksUntilNextStageAction = this.getRandomPhaseDuration(1, 4); break; } nextStageDecision = LampEffectStage.RESTORING_PREPARE; this.ticksUntilNextStageAction = 1L; break; } this.ticksUntilNextStageAction = 1L; break; } case FINAL_FLICKER_WAITING_OFF: { nextStageDecision = LampEffectStage.FINAL_FLICKER_PREPARE_ON; this.ticksUntilNextStageAction = 1L; break; } case RESTORING_PREPARE: { this.currentPhaseTargetLitState = this.originallyLit; this.lampUpdateProgressIndex = 0; nextStageDecision = LampEffectStage.RESTORING_UPDATING_TO_ORIGINAL; this.ticksUntilNextStageAction = 1L; break; } case RESTORING_UPDATING_TO_ORIGINAL: { phaseUpdateCompleted = this.processLampUpdateBatch(budget); if (phaseUpdateCompleted) { nextStageDecision = LampEffectStage.FINISHED; break; } this.ticksUntilNextStageAction = 1L; break; } } this.currentStage = nextStageDecision; } return this.currentStage == LampEffectStage.FINISHED; } } private static enum LampEffectStage { AWAITING_INITIAL_DELAY, INITIAL_FLICKER_PREPARE_OFF, INITIAL_FLICKER_UPDATING_TO_OFF, INITIAL_FLICKER_WAITING_OFF, INITIAL_FLICKER_PREPARE_ON, INITIAL_FLICKER_UPDATING_TO_ON, INITIAL_FLICKER_WAITING_ON, LONG_OFF_PREPARE, LONG_OFF_UPDATING_TO_OFF, LONG_OFF_WAITING, FINAL_FLICKER_PREPARE_ON, FINAL_FLICKER_UPDATING_TO_ON, FINAL_FLICKER_WAITING_ON, FINAL_FLICKER_PREPARE_OFF, FINAL_FLICKER_UPDATING_TO_OFF, FINAL_FLICKER_WAITING_OFF, RESTORING_PREPARE, RESTORING_UPDATING_TO_ORIGINAL, FINISHED; } }