Files
Explosion-Overhaul/build/decompiled/com/vinlanx/explosionoverhaul/RedstoneLampEffects.java
2026-05-04 10:03:58 +00:00

396 lines
20 KiB
Java

/*
* 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<UUID, List<DelayedLampEffect>> perPlayerDelayedLampEffects = new ConcurrentHashMap<UUID, List<DelayedLampEffect>>();
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<PotentialLampGroup> potentialLampGroupsFoundThisExplosion = new ArrayList<PotentialLampGroup>();
List<BlockPos> allCandidates = BlockIndexManager.getNearby(level, playerPos, lampEffectRadius, BlockIndexManager.BlockType.LAMP);
if (allCandidates.isEmpty()) {
return;
}
ArrayList<BlockPos> litLampCandidates = new ArrayList<BlockPos>();
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<BlockPos> lampsAlreadyInAGroup = new HashSet<BlockPos>();
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<BlockPos> currentLampGroupPositions = new ArrayList<BlockPos>();
LinkedList<BlockPos> toProcess = new LinkedList<BlockPos>();
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<UUID, List<DelayedLampEffect>> entry : perPlayerDelayedLampEffects.entrySet()) {
List<DelayedLampEffect> playerEffects = entry.getValue();
int[] playerBudget = new int[]{200};
List<DelayedLampEffect> list = playerEffects;
synchronized (list) {
Iterator<DelayedLampEffect> 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<BlockPos> groupPositions, boolean originallyLit, long predeterminedLongOffDuration, double distanceSqToPlayer, long initialDelay) {
}
private static class DelayedLampEffect {
public ServerLevel level;
public BlockPos representativeLampPos;
public List<BlockPos> 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<BlockPos> allGroupPositions, long initialDelayTicks, boolean lit, long predeterminedLongOffDuration, RandomSource randomSource) {
this.level = level;
this.representativeLampPos = representativeLampPos;
this.groupLampPositions = new ArrayList<BlockPos>(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;
}
}