/* * Decompiled with CFR 0.152. */ package com.vinlanx.explosionoverhaul; import com.vinlanx.explosionoverhaul.Config; import com.vinlanx.explosionoverhaul.CraterDeformer; import com.vinlanx.explosionoverhaul.ExplosionOverhaul; import java.lang.invoke.LambdaMetafactory; import java.util.ArrayList; import java.util.Comparator; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import java.util.function.Function; import net.minecraft.core.BlockPos; import net.minecraft.core.Position; import net.minecraft.core.SectionPos; import net.minecraft.core.Vec3i; import net.minecraft.core.particles.ParticleOptions; import net.minecraft.core.particles.ParticleTypes; import net.minecraft.resources.ResourceKey; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerLevel; import net.minecraft.tags.BlockTags; import net.minecraft.util.RandomSource; import net.minecraft.world.entity.item.FallingBlockEntity; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.levelgen.Heightmap; import net.minecraft.world.phys.Vec3; public class AsyncCraterManager { private static ExecutorService executor; private static final ConcurrentLinkedQueue activeJobs; private static final AtomicLong globalEpoch; private static final ConcurrentHashMap, AtomicLong> dimensionEpochs; private static AtomicLong getDimensionEpoch(ResourceKey dimension) { return dimensionEpochs.computeIfAbsent(dimension, d -> new AtomicLong(0L)); } private static long incrementDimensionEpoch(ResourceKey dimension) { return AsyncCraterManager.getDimensionEpoch(dimension).incrementAndGet(); } public static synchronized void ensureExecutor() { if (executor != null && !executor.isShutdown()) { return; } int configured = (Integer)Config.COMMON.craterMaxThreads.get(); int threads = configured == 0 ? Runtime.getRuntime().availableProcessors() : Math.max(1, configured); threads = Math.min(threads, 32); executor = Executors.newFixedThreadPool(threads, r -> { Thread t = new Thread(r, "EO-AsyncCrater"); t.setDaemon(true); return t; }); } public static void submit(ServerLevel level, Vec3 pos, float power) { if (!((Boolean)Config.COMMON.enableCraterDestruction.get()).booleanValue()) { return; } AsyncCraterManager.ensureExecutor(); ResourceKey dim = level.m_46472_(); Job job = new Job(level, pos, power, globalEpoch.get(), AsyncCraterManager.getDimensionEpoch((ResourceKey)dim).get()); ((CompletableFuture)CompletableFuture.runAsync(job::compute, executor).thenRun(() -> { if (!job.isCancelled()) { activeJobs.add(job); } })).exceptionally(throwable -> { ExplosionOverhaul.LOGGER.warn("AsyncCraterManager: failed to compute crater job: {}", (Object)throwable.toString()); return null; }); } public static void onServerTick(MinecraftServer server) { Job job; if (activeJobs.isEmpty()) { return; } int size = activeJobs.size(); for (int i = 0; i < size && (job = activeJobs.poll()) != null; ++i) { if (job.isCancelled()) continue; job.applyBatch(); if (job.isDone()) continue; activeJobs.add(job); } } public static void onLevelUnload(ServerLevel level) { if (level == null) { return; } ResourceKey dim = level.m_46472_(); AsyncCraterManager.incrementDimensionEpoch((ResourceKey)dim); activeJobs.removeIf(job -> job.isForDimension((ResourceKey)dim)); } public static synchronized void shutdown() { globalEpoch.incrementAndGet(); dimensionEpochs.clear(); activeJobs.clear(); if (executor != null) { executor.shutdownNow(); try { executor.awaitTermination(1L, TimeUnit.SECONDS); } catch (InterruptedException interruptedException) { // empty catch block } executor = null; } } static { activeJobs = new ConcurrentLinkedQueue(); globalEpoch = new AtomicLong(0L); dimensionEpochs = new ConcurrentHashMap(); } private static class Job { private final Vec3 explosionPos; private final float power; private final ResourceKey dimensionKey; private final long submitGlobalEpoch; private final long submitDimensionEpoch; private volatile List candidates; private volatile boolean computed; private final ServerLevel levelRef; private int appliedIndex; private final List ordered; private final RandomSource random; private float[] rayEnergy; private boolean[] rayDepleted; private int rayCount; private Set destroyedPositions = new HashSet(); Job(ServerLevel level, Vec3 pos, float power, long submitGlobalEpoch, long submitDimensionEpoch) { this.levelRef = level; this.dimensionKey = level.m_46472_(); this.submitGlobalEpoch = submitGlobalEpoch; this.submitDimensionEpoch = submitDimensionEpoch; this.explosionPos = pos; this.power = power; this.candidates = new ArrayList(); this.ordered = new ArrayList(); this.random = RandomSource.m_216327_(); } /* * WARNING - Removed try catching itself - possible behaviour change. */ void compute() { try { double multiplier = (Double)Config.COMMON.craterSizeMultiplier.get(); double baseRadius = CraterDeformer.calculateRadius(this.power); double finalRadius = baseRadius * multiplier; if (finalRadius <= 0.0) { this.computed = true; return; } boolean large = this.power >= 40.0f; double coreRatio = (Double)Config.COMMON.craterCoreRatio.get(); double coreRadius = large ? finalRadius * coreRatio : 0.0; int searchRadius = (int)Math.ceil(finalRadius); BlockPos centerPos = BlockPos.m_274446_((Position)this.explosionPos); RandomSource rnd = RandomSource.m_216327_(); if (large) { HashSet corePositions = new HashSet(); for (int x = -searchRadius; x <= searchRadius; ++x) { for (int y = -searchRadius; y <= searchRadius; ++y) { for (int z = -searchRadius; z <= searchRadius; ++z) { double noise; BlockPos p = centerPos.m_7918_(x, y, z); double dist = Math.sqrt(p.m_123331_((Vec3i)centerPos)); if (!(dist <= coreRadius * (noise = 1.0 - rnd.m_188500_() * 0.3)) || !corePositions.add(p)) continue; this.candidates.add(new Candidate(p, -1, 0, true)); } } } } int numberOfRays = (int)Math.max(200.0, Math.min(large ? 25000.0 : 75000.0, finalRadius * (double)(large ? 75 : 150))); double goldenRatio = (1.0 + Math.sqrt(5.0)) / 2.0; double angleIncrement = Math.PI * 2 * goldenRatio; double stepIncrement = 0.4; double startStep = large ? coreRadius * 0.8 : 0.0; this.rayCount = numberOfRays; for (int i = 0; i < numberOfRays; ++i) { double t = (double)i / (double)numberOfRays; double inclination = Math.acos(1.0 - 2.0 * t); double azimuth = angleIncrement * (double)i; Vec3 dir = new Vec3(Math.cos(azimuth) * Math.sin(inclination), Math.sin(azimuth) * Math.sin(inclination), Math.cos(inclination)).m_82541_(); int stepIndex = 0; BlockPos lastPos = null; for (double step = startStep; step < finalRadius; step += stepIncrement) { BlockPos p = BlockPos.m_274446_((Position)this.explosionPos.m_82549_(dir.m_82490_(step))); if (p.equals(lastPos)) continue; lastPos = p; this.candidates.add(new Candidate(p, i, stepIndex, false)); ++stepIndex; } } ArrayList coreBlocks = new ArrayList(); ArrayList rayBlocks = new ArrayList(); for (Candidate c : this.candidates) { if (c.core) { coreBlocks.add(c); continue; } rayBlocks.add(c); } rayBlocks.sort((a, b) -> { int cmp = Integer.compare(a.rayId, b.rayId); if (cmp != 0) { return cmp; } return Integer.compare(a.stepIndex, b.stepIndex); }); this.ordered.addAll(coreBlocks); this.ordered.addAll(rayBlocks); } finally { this.computed = true; } } boolean isCancelled() { long currentDimEpoch; if (this.submitGlobalEpoch != globalEpoch.get()) { return true; } AtomicLong dimEpoch = dimensionEpochs.get(this.dimensionKey); long l = currentDimEpoch = dimEpoch == null ? 0L : dimEpoch.get(); if (this.submitDimensionEpoch != currentDimEpoch) { return true; } return this.levelRef.m_7654_() == null; } boolean isForDimension(ResourceKey dim) { return this.dimensionKey.equals(dim); } boolean isDone() { return this.computed && this.appliedIndex >= this.ordered.size(); } /* * Unable to fully structure code */ void applyBatch() { block37: { block38: { if (!this.computed) { return; } if (this.isCancelled()) { this.appliedIndex = this.ordered.size(); return; } budget = Math.max(100, (Integer)Config.COMMON.craterApplyBlocksPerTick.get()); maxFallingPerTick = Math.max(0, (Integer)Config.COMMON.craterMaxFallingBlocksPerTick.get()); spawnedFalling = 0; if (this.rayEnergy == null) { multiplier = (Double)Config.COMMON.craterSizeMultiplier.get(); large = this.power >= 40.0f; base = (float)((double)(this.power * (large != false ? 4.0f : 7.5f)) * multiplier); this.rayEnergy = new float[Math.max(1, this.rayCount)]; this.rayDepleted = new boolean[Math.max(1, this.rayCount)]; rnd = this.levelRef.m_213780_(); for (i = 0; i < this.rayEnergy.length; ++i) { this.rayEnergy[i] = base * (0.75f + rnd.m_188501_() * 0.5f); } } end = Math.min(this.appliedIndex + budget, this.ordered.size()); direct = (Boolean)Config.COMMON.enableDirectChunkWrites.get(); if (direct) break block38; while (this.appliedIndex < end) { block39: { block41: { block40: { c = this.ordered.get(this.appliedIndex); pos = c.pos; if (!this.levelRef.m_46749_(pos) || this.levelRef.m_151570_(pos) || !c.core && this.rayDepleted[rayId = Math.max(0, c.rayId)] || this.destroyedPositions.contains(pos) || (state = this.levelRef.m_8055_(pos)).m_60795_()) break block39; if (!((Boolean)Config.COMMON.enableCraterDestruction.get()).booleanValue()) { this.appliedIndex = this.ordered.size(); break block37; } resistance = state.m_60800_((BlockGetter)this.levelRef, pos); if (resistance < 0.0f) break block39; if (!ExplosionOverhaul.isBlockStateBlacklisted(state) && !state.m_60734_().m_204297_().m_203656_(BlockTags.f_13070_) && !state.m_60713_(Blocks.f_50722_)) break block40; if (!c.core) { rayId = Math.max(0, c.rayId); this.rayDepleted[rayId] = true; } break block39; } if (!(resistance > Job.calculateMaxResistance(this.power))) break block41; if (!c.core) { rayId = Math.max(0, c.rayId); this.rayDepleted[rayId] = true; } break block39; } if (c.core) ** GOTO lbl-1000 v0 = rayId = Math.max(0, c.rayId); this.rayEnergy[v0] = this.rayEnergy[v0] - (resistance + 0.3f); if (this.rayEnergy[rayId] <= 0.0f) { this.rayDepleted[rayId] = true; } else lbl-1000: // 2 sources { v1 = allowFalling = this.power <= 20.0f && (Boolean)Config.COMMON.enableFallingBlocks.get() != false && spawnedFalling < maxFallingPerTick; if (allowFalling) { inner = c.core != false || c.stepIndex <= 5; v2 = chance = inner != false ? 0.35 : 0.15; if (this.random.m_188500_() < chance) { falling = FallingBlockEntity.m_201971_((Level)this.levelRef, (BlockPos)pos, (BlockState)state); falling.m_6034_((double)pos.m_123341_() + 0.5, (double)pos.m_123342_() + 0.5, (double)pos.m_123343_() + 0.5); motionDir = Vec3.m_82512_((Vec3i)pos).m_82546_(this.explosionPos).m_82541_(); powerMul = 0.4 + this.random.m_188500_() * 0.6; falling.m_20334_(motionDir.f_82479_ * powerMul + (this.random.m_188500_() - 0.5) * 0.3, motionDir.f_82480_ * powerMul + 0.4 + this.random.m_188500_() * 0.3, motionDir.f_82481_ * powerMul + (this.random.m_188500_() - 0.5) * 0.3); ++spawnedFalling; } } this.levelRef.m_7731_(pos, Blocks.f_50016_.m_49966_(), 2); this.destroyedPositions.add(pos); } } ++this.appliedIndex; } break block37; } maxChunks = Math.max(1, (Integer)Config.COMMON.craterChunksPerTick.get()); byChunk = new LinkedHashMap(); i = this.appliedIndex; while (i < end && byChunk.size() < maxChunks) { c = this.ordered.get(i); pos = c.pos; if (!this.levelRef.m_46749_(pos) || this.levelRef.m_151570_(pos)) { ++i; continue; } chunk = this.levelRef.m_46745_(pos); byChunk.computeIfAbsent(chunk, (Function)LambdaMetafactory.metafactory(null, null, null, (Ljava/lang/Object;)Ljava/lang/Object;, lambda$applyBatch$1(net.minecraft.world.level.chunk.LevelChunk ), (Lnet/minecraft/world/level/chunk/LevelChunk;)Ljava/util/List;)()).add(c); ++i; } for (Map.Entry e : byChunk.entrySet()) { chunk = (LevelChunk)e.getKey(); list = (List)e.getValue(); list.sort((Comparator)LambdaMetafactory.metafactory(null, null, null, (Ljava/lang/Object;Ljava/lang/Object;)I, lambda$applyBatch$2(com.vinlanx.explosionoverhaul.AsyncCraterManager$Job$Candidate com.vinlanx.explosionoverhaul.AsyncCraterManager$Job$Candidate ), (Lcom/vinlanx/explosionoverhaul/AsyncCraterManager$Job$Candidate;Lcom/vinlanx/explosionoverhaul/AsyncCraterManager$Job$Candidate;)I)()); modifiedSections = new HashSet(); toRelight = new ArrayList(); relightBudget = Math.min(256, list.size()); for (Candidate c : list) { if (!c.core && this.rayDepleted[rayId = Math.max(0, c.rayId)]) { ++this.appliedIndex; continue; } pos = c.pos; if (this.destroyedPositions.contains(pos)) { ++this.appliedIndex; continue; } if (!((Boolean)Config.COMMON.enableCraterDestruction.get()).booleanValue()) { this.appliedIndex = this.ordered.size(); break; } state = this.levelRef.m_8055_(pos); if (state.m_60795_()) { ++this.appliedIndex; continue; } resistance = state.m_60800_((BlockGetter)this.levelRef, pos); if (resistance < 0.0f) { ++this.appliedIndex; continue; } if (ExplosionOverhaul.isBlockStateBlacklisted(state) || state.m_60734_().m_204297_().m_203656_(BlockTags.f_13070_) || state.m_60713_(Blocks.f_50722_)) { if (!c.core) { rayId = Math.max(0, c.rayId); this.rayDepleted[rayId] = true; } ++this.appliedIndex; continue; } if (resistance > Job.calculateMaxResistance(this.power)) { if (!c.core) { rayId = Math.max(0, c.rayId); this.rayDepleted[rayId] = true; } ++this.appliedIndex; continue; } if (!c.core) { v3 = rayId = Math.max(0, c.rayId); this.rayEnergy[v3] = this.rayEnergy[v3] - (resistance + 0.3f); if (this.rayEnergy[rayId] <= 0.0f) { this.rayDepleted[rayId] = true; ++this.appliedIndex; continue; } } if (state.m_155947_()) { this.levelRef.m_46961_(pos, true); ++this.appliedIndex; continue; } secIdx = chunk.m_151564_(pos.m_123342_()); if (secIdx < 0 || secIdx >= chunk.m_7103_().length) { ++this.appliedIndex; continue; } section = chunk.m_7103_()[secIdx]; old = section.m_62982_(lx = pos.m_123341_() & 15, ly = pos.m_123342_() & 15, lz = pos.m_123343_() & 15); if (old.m_60795_()) { ++this.appliedIndex; continue; } section.m_62986_(lx, ly, lz, Blocks.f_50016_.m_49966_()); this.destroyedPositions.add(pos); for (Map.Entry m : chunk.m_6890_()) { type = (Heightmap.Types)m.getKey(); if (type != Heightmap.Types.MOTION_BLOCKING && type != Heightmap.Types.MOTION_BLOCKING_NO_LEAVES && type != Heightmap.Types.OCEAN_FLOOR && type != Heightmap.Types.WORLD_SURFACE) continue; ((Heightmap)m.getValue()).m_64249_(lx, pos.m_123342_(), lz, Blocks.f_50016_.m_49966_()); } this.levelRef.m_7260_(pos, old, Blocks.f_50016_.m_49966_(), 2); modifiedSections.add(SectionPos.m_123199_((BlockPos)pos)); if (toRelight.size() < relightBudget) { toRelight.add(pos.m_7949_()); } v4 = allowFalling = this.power <= 20.0f && (Boolean)Config.COMMON.enableFallingBlocks.get() != false && spawnedFalling < maxFallingPerTick; if (allowFalling) { inner = c.core != false || c.stepIndex <= 5; v5 = chance = inner != false ? 0.35 : 0.15; if (this.random.m_188500_() < chance) { falling = FallingBlockEntity.m_201971_((Level)this.levelRef, (BlockPos)pos, (BlockState)old); falling.m_6034_((double)pos.m_123341_() + 0.5, (double)pos.m_123342_() + 0.5, (double)pos.m_123343_() + 0.5); motionDir = Vec3.m_82512_((Vec3i)pos).m_82546_(this.explosionPos).m_82541_(); powerMul = 0.4 + this.random.m_188500_() * 0.6; falling.m_20334_(motionDir.f_82479_ * powerMul + (this.random.m_188500_() - 0.5) * 0.3, motionDir.f_82480_ * powerMul + 0.4 + this.random.m_188500_() * 0.3, motionDir.f_82481_ * powerMul + (this.random.m_188500_() - 0.5) * 0.3); ++spawnedFalling; } } ++this.appliedIndex; } chunk.m_8092_(true); light = this.levelRef.m_7726_().m_7827_(); for (SectionPos sp : modifiedSections) { sec = chunk.m_7103_()[chunk.m_151564_(sp.m_123206_() << 4)]; empty = sec.m_188008_(); light.m_6191_(sp, empty); } for (BlockPos rp : toRelight) { light.m_7174_(rp); light.m_7174_(rp.m_7494_()); } light.m_9353_((ChunkAccess)chunk, true); } } if (this.isDone()) { this.levelRef.m_8767_((ParticleOptions)ParticleTypes.f_123812_, this.explosionPos.f_82479_, this.explosionPos.f_82480_, this.explosionPos.f_82481_, 1, 0.0, 0.0, 0.0, 0.0); } } private static float calculateMaxResistance(float power) { float maxRes = power <= 4.0f ? 10.0f : (power <= 10.0f ? 10.0f + (power - 4.0f) * 10.0f / 6.0f : (power <= 25.0f ? 20.0f + (power - 10.0f) * 10.0f / 15.0f : (power <= 40.0f ? 30.0f + (power - 25.0f) * 10.0f / 15.0f : (power <= 70.0f ? 40.0f + (power - 40.0f) * 10.0f / 30.0f : Float.MAX_VALUE)))); return maxRes; } private static /* synthetic */ int lambda$applyBatch$2(Candidate a, Candidate b) { if (a.core && !b.core) { return -1; } if (!a.core && b.core) { return 1; } if (a.core && b.core) { return 0; } int cmp = Integer.compare(a.rayId, b.rayId); if (cmp != 0) { return cmp; } return Integer.compare(a.stepIndex, b.stepIndex); } private static /* synthetic */ List lambda$applyBatch$1(LevelChunk k) { return new ArrayList(); } private static class Candidate { final BlockPos pos; final int rayId; final int stepIndex; final boolean core; Candidate(BlockPos pos, int rayId, int stepIndex, boolean core) { this.pos = pos; this.rayId = rayId; this.stepIndex = stepIndex; this.core = core; } } } }