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

524 lines
26 KiB
Java

/*
* 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<Job> activeJobs;
private static final AtomicLong globalEpoch;
private static final ConcurrentHashMap<ResourceKey<Level>, AtomicLong> dimensionEpochs;
private static AtomicLong getDimensionEpoch(ResourceKey<Level> dimension) {
return dimensionEpochs.computeIfAbsent(dimension, d -> new AtomicLong(0L));
}
private static long incrementDimensionEpoch(ResourceKey<Level> 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<Level>)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<Level>)dim);
activeJobs.removeIf(job -> job.isForDimension((ResourceKey<Level>)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<Level> dimensionKey;
private final long submitGlobalEpoch;
private final long submitDimensionEpoch;
private volatile List<Candidate> candidates;
private volatile boolean computed;
private final ServerLevel levelRef;
private int appliedIndex;
private final List<Candidate> ordered;
private final RandomSource random;
private float[] rayEnergy;
private boolean[] rayDepleted;
private int rayCount;
private Set<BlockPos> destroyedPositions = new HashSet<BlockPos>();
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<Candidate>();
this.ordered = new ArrayList<Candidate>();
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<BlockPos> corePositions = new HashSet<BlockPos>();
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<Candidate> coreBlocks = new ArrayList<Candidate>();
ArrayList<Candidate> rayBlocks = new ArrayList<Candidate>();
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<Level> 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<LevelChunk, List>();
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<LevelChunk, List>)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<K, V> 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<SectionPos>();
toRelight = new ArrayList<BlockPos>();
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;
}
}
}
}