mirror of
https://github.com/HbmMods/Hbm-s-Nuclear-Tech-GIT.git
synced 2026-01-25 10:32:49 +00:00
Feat: accumulatedDestruction
This commit is contained in:
parent
9d3860df64
commit
1a5fda1847
@ -27,6 +27,7 @@ public class BombConfig {
|
|||||||
public static int limitExplosionLifespan = 0;
|
public static int limitExplosionLifespan = 0;
|
||||||
public static boolean chunkloading = true;
|
public static boolean chunkloading = true;
|
||||||
public static boolean parallelization = true;
|
public static boolean parallelization = true;
|
||||||
|
public static boolean accumulatedDestruction = true;
|
||||||
|
|
||||||
public static void loadFromConfig(Configuration config) {
|
public static void loadFromConfig(Configuration config) {
|
||||||
|
|
||||||
@ -96,5 +97,6 @@ public class BombConfig {
|
|||||||
|
|
||||||
chunkloading = CommonConfig.createConfigBool(config, CATEGORY_NUKE, "6.XX_enableChunkLoading", "Allows all types of procedural explosions to keep the central chunk loaded.", true);
|
chunkloading = CommonConfig.createConfigBool(config, CATEGORY_NUKE, "6.XX_enableChunkLoading", "Allows all types of procedural explosions to keep the central chunk loaded.", true);
|
||||||
parallelization = CommonConfig.createConfigBool(config, CATEGORY_NUKE, "6.XX_enableParallelization", "Allows explosions to use multiple threads.", true);
|
parallelization = CommonConfig.createConfigBool(config, CATEGORY_NUKE, "6.XX_enableParallelization", "Allows explosions to use multiple threads.", true);
|
||||||
|
accumulatedDestruction = CommonConfig.createConfigBool(config, CATEGORY_NUKE, "6.XX_enableAccumulatedDestruction", "Enables the accumulated destruction model for explosions. Blocks accumulate damage and are only destroyed once their resistance is exceeded.\nMore physically accurate, slightly slower. Requires enableParallelization = true.", true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
package com.hbm.explosion;
|
package com.hbm.explosion;
|
||||||
|
|
||||||
|
import com.hbm.config.BombConfig;
|
||||||
import com.hbm.interfaces.IExplosionRay;
|
import com.hbm.interfaces.IExplosionRay;
|
||||||
import com.hbm.main.MainRegistry;
|
import com.hbm.main.MainRegistry;
|
||||||
import net.minecraft.block.Block;
|
import net.minecraft.block.Block;
|
||||||
@ -15,6 +16,7 @@ import org.apache.logging.log4j.Level;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
import java.util.concurrent.atomic.AtomicLongArray;
|
import java.util.concurrent.atomic.AtomicLongArray;
|
||||||
|
import java.util.concurrent.atomic.DoubleAdder;
|
||||||
|
|
||||||
public class ExplosionNukeRayParallelized implements IExplosionRay {
|
public class ExplosionNukeRayParallelized implements IExplosionRay {
|
||||||
|
|
||||||
@ -31,6 +33,8 @@ public class ExplosionNukeRayParallelized implements IExplosionRay {
|
|||||||
private volatile List<Vec3> directions;
|
private volatile List<Vec3> directions;
|
||||||
private final CompletableFuture<List<Vec3>> directionsFuture;
|
private final CompletableFuture<List<Vec3>> directionsFuture;
|
||||||
private final ConcurrentMap<ChunkCoordIntPair, ConcurrentBitSet> destructionMap;
|
private final ConcurrentMap<ChunkCoordIntPair, ConcurrentBitSet> destructionMap;
|
||||||
|
private final ConcurrentMap<ChunkCoordIntPair, ChunkDamageAccumulator> accumulatedDamageMap;
|
||||||
|
|
||||||
private final ConcurrentMap<ChunkKey, SubChunkSnapshot> snapshots;
|
private final ConcurrentMap<ChunkKey, SubChunkSnapshot> snapshots;
|
||||||
|
|
||||||
private final BlockingQueue<RayTask> rayQueue;
|
private final BlockingQueue<RayTask> rayQueue;
|
||||||
@ -40,9 +44,9 @@ public class ExplosionNukeRayParallelized implements IExplosionRay {
|
|||||||
private final Thread latchWatcherThread;
|
private final Thread latchWatcherThread;
|
||||||
private final List<ChunkCoordIntPair> orderedChunks;
|
private final List<ChunkCoordIntPair> orderedChunks;
|
||||||
private volatile boolean collectFinished = false;
|
private volatile boolean collectFinished = false;
|
||||||
|
private volatile boolean consolidationFinished = false;
|
||||||
private volatile boolean destroyFinished = false;
|
private volatile boolean destroyFinished = false;
|
||||||
|
|
||||||
|
|
||||||
public ExplosionNukeRayParallelized(World world, double x, double y, double z, int strength, int speed, int radius) {
|
public ExplosionNukeRayParallelized(World world, double x, double y, double z, int strength, int speed, int radius) {
|
||||||
this.world = world;
|
this.world = world;
|
||||||
this.explosionX = x;
|
this.explosionX = x;
|
||||||
@ -60,6 +64,7 @@ public class ExplosionNukeRayParallelized implements IExplosionRay {
|
|||||||
|
|
||||||
this.latch = new CountDownLatch(rayCount);
|
this.latch = new CountDownLatch(rayCount);
|
||||||
this.destructionMap = new ConcurrentHashMap<>();
|
this.destructionMap = new ConcurrentHashMap<>();
|
||||||
|
this.accumulatedDamageMap = new ConcurrentHashMap<>();
|
||||||
this.snapshots = new ConcurrentHashMap<>();
|
this.snapshots = new ConcurrentHashMap<>();
|
||||||
this.orderedChunks = new ArrayList<>();
|
this.orderedChunks = new ArrayList<>();
|
||||||
|
|
||||||
@ -80,6 +85,11 @@ public class ExplosionNukeRayParallelized implements IExplosionRay {
|
|||||||
Thread.currentThread().interrupt();
|
Thread.currentThread().interrupt();
|
||||||
} finally {
|
} finally {
|
||||||
collectFinished = true;
|
collectFinished = true;
|
||||||
|
if (BombConfig.accumulatedDestruction) {
|
||||||
|
pool.submit(this::runConsolidation);
|
||||||
|
} else {
|
||||||
|
consolidationFinished = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, "ExplosionNuke-LatchWatcher-" + System.nanoTime());
|
}, "ExplosionNuke-LatchWatcher-" + System.nanoTime());
|
||||||
this.latchWatcherThread.setDaemon(true);
|
this.latchWatcherThread.setDaemon(true);
|
||||||
@ -110,7 +120,8 @@ public class ExplosionNukeRayParallelized implements IExplosionRay {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void destructionTick(int timeBudgetMs) {
|
public void destructionTick(int timeBudgetMs) {
|
||||||
if (!collectFinished || destroyFinished) return;
|
if (!collectFinished || !consolidationFinished || destroyFinished) return; // Added consolidationFinished check
|
||||||
|
|
||||||
final long deadline = System.nanoTime() + timeBudgetMs * 1_000_000L;
|
final long deadline = System.nanoTime() + timeBudgetMs * 1_000_000L;
|
||||||
|
|
||||||
if (orderedChunks.isEmpty() && !destructionMap.isEmpty()) {
|
if (orderedChunks.isEmpty() && !destructionMap.isEmpty()) {
|
||||||
@ -193,12 +204,13 @@ public class ExplosionNukeRayParallelized implements IExplosionRay {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isComplete() {
|
public boolean isComplete() {
|
||||||
return collectFinished && destroyFinished;
|
return collectFinished && consolidationFinished && destroyFinished;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void cancel() {
|
public void cancel() {
|
||||||
this.collectFinished = true;
|
this.collectFinished = true;
|
||||||
|
this.consolidationFinished = true;
|
||||||
this.destroyFinished = true;
|
this.destroyFinished = true;
|
||||||
|
|
||||||
if (this.rayQueue != null) this.rayQueue.clear();
|
if (this.rayQueue != null) this.rayQueue.clear();
|
||||||
@ -227,6 +239,7 @@ public class ExplosionNukeRayParallelized implements IExplosionRay {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.destructionMap != null) this.destructionMap.clear();
|
if (this.destructionMap != null) this.destructionMap.clear();
|
||||||
|
if (this.accumulatedDamageMap != null) this.accumulatedDamageMap.clear();
|
||||||
if (this.snapshots != null) this.snapshots.clear();
|
if (this.snapshots != null) this.snapshots.clear();
|
||||||
if (this.orderedChunks != null) this.orderedChunks.clear();
|
if (this.orderedChunks != null) this.orderedChunks.clear();
|
||||||
}
|
}
|
||||||
@ -291,6 +304,72 @@ public class ExplosionNukeRayParallelized implements IExplosionRay {
|
|||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void runConsolidation() {
|
||||||
|
Iterator<Map.Entry<ChunkCoordIntPair, ChunkDamageAccumulator>> chunkEntryIterator = accumulatedDamageMap.entrySet().iterator();
|
||||||
|
while (chunkEntryIterator.hasNext()) {
|
||||||
|
Map.Entry<ChunkCoordIntPair, ChunkDamageAccumulator> entry = chunkEntryIterator.next();
|
||||||
|
ChunkCoordIntPair cp = entry.getKey();
|
||||||
|
ChunkDamageAccumulator accumulator = entry.getValue();
|
||||||
|
|
||||||
|
if (accumulator.isEmpty()) {
|
||||||
|
chunkEntryIterator.remove();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConcurrentBitSet chunkDestructionBitSet = destructionMap.computeIfAbsent(cp, k -> new ConcurrentBitSet());
|
||||||
|
|
||||||
|
Iterator<Map.Entry<Integer, DoubleAdder>> damageEntryIterator = accumulator.entrySet().iterator();
|
||||||
|
while (damageEntryIterator.hasNext()) {
|
||||||
|
Map.Entry<Integer, DoubleAdder> damageEntry = damageEntryIterator.next();
|
||||||
|
int bitIndex = damageEntry.getKey();
|
||||||
|
|
||||||
|
float accumulatedDamage = (float) damageEntry.getValue().sum();
|
||||||
|
|
||||||
|
if (accumulatedDamage <= 0.0f) {
|
||||||
|
damageEntryIterator.remove();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int yGlobal = 255 - (bitIndex >>> 8);
|
||||||
|
int subY = yGlobal >> 4;
|
||||||
|
|
||||||
|
if (subY < 0) {
|
||||||
|
damageEntryIterator.remove();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ChunkKey snapshotKey = new ChunkKey(cp.chunkXPos, cp.chunkZPos, subY);
|
||||||
|
SubChunkSnapshot snap = snapshots.get(snapshotKey);
|
||||||
|
Block originalBlock;
|
||||||
|
|
||||||
|
if (snap == null || snap == SubChunkSnapshot.EMPTY) {
|
||||||
|
damageEntryIterator.remove();
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
int xLocal = (bitIndex >>> 4) & 0xF;
|
||||||
|
int zLocal = bitIndex & 0xF;
|
||||||
|
originalBlock = snap.getBlock(xLocal, yGlobal & 0xF, zLocal);
|
||||||
|
if (originalBlock == Blocks.air) {
|
||||||
|
damageEntryIterator.remove();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float resistance = getNukeResistance(originalBlock);
|
||||||
|
if (accumulatedDamage >= resistance) {
|
||||||
|
chunkDestructionBitSet.set(bitIndex);
|
||||||
|
damageEntryIterator.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (accumulator.isEmpty()) {
|
||||||
|
chunkEntryIterator.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
accumulatedDamageMap.clear();
|
||||||
|
consolidationFinished = true;
|
||||||
|
}
|
||||||
|
|
||||||
private static class ChunkKey {
|
private static class ChunkKey {
|
||||||
final ChunkCoordIntPair pos;
|
final ChunkCoordIntPair pos;
|
||||||
final int subY;
|
final int subY;
|
||||||
@ -349,7 +428,13 @@ public class ExplosionNukeRayParallelized implements IExplosionRay {
|
|||||||
if (bit < 0 || bit >= BITSET_SIZE) return;
|
if (bit < 0 || bit >= BITSET_SIZE) return;
|
||||||
int wd = bit >>> 6;
|
int wd = bit >>> 6;
|
||||||
long m = ~(1L << (bit & 63));
|
long m = ~(1L << (bit & 63));
|
||||||
words.set(wd, words.get(wd) & m);
|
while (true) {
|
||||||
|
long oldWord = words.get(wd);
|
||||||
|
long newWord = oldWord & m;
|
||||||
|
if (oldWord == newWord || words.compareAndSet(wd, oldWord, newWord)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int nextSetBit(int from) {
|
int nextSetBit(int from) {
|
||||||
@ -370,6 +455,35 @@ public class ExplosionNukeRayParallelized implements IExplosionRay {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class ChunkDamageAccumulator {
|
||||||
|
// key = bitIndex, value = total accumulated damage
|
||||||
|
private final ConcurrentHashMap<Integer, DoubleAdder> damageMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
public void addDamage(int bitIndex, float damageAmount) {
|
||||||
|
if (damageAmount <= 0) return;
|
||||||
|
DoubleAdder adder = damageMap.computeIfAbsent(bitIndex, k -> new DoubleAdder());
|
||||||
|
adder.add(damageAmount);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getDamage(int bitIndex) {
|
||||||
|
DoubleAdder adder = damageMap.get(bitIndex);
|
||||||
|
return adder == null ? 0f : (float) adder.sum();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearDamage(int bitIndex) {
|
||||||
|
damageMap.remove(bitIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Map.Entry<Integer, DoubleAdder>> entrySet() {
|
||||||
|
return damageMap.entrySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return damageMap.isEmpty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private class Worker implements Runnable {
|
private class Worker implements Runnable {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
@ -409,15 +523,15 @@ public class ExplosionNukeRayParallelized implements IExplosionRay {
|
|||||||
void init() {
|
void init() {
|
||||||
if (directions == null) directions = directionsFuture.join();
|
if (directions == null) directions = directionsFuture.join();
|
||||||
Vec3 dir = directions.get(this.dirIndex);
|
Vec3 dir = directions.get(this.dirIndex);
|
||||||
|
// This scales the crater. Higher = bigger.
|
||||||
|
// Currently the crater is a little bit bigger than the original implementation
|
||||||
|
this.energy = strength * 0.3F;
|
||||||
this.px = explosionX;
|
this.px = explosionX;
|
||||||
this.py = explosionY;
|
this.py = explosionY;
|
||||||
this.pz = explosionZ;
|
this.pz = explosionZ;
|
||||||
this.x = originX;
|
this.x = originX;
|
||||||
this.y = originY;
|
this.y = originY;
|
||||||
this.z = originZ;
|
this.z = originZ;
|
||||||
// This scales the crater. higher = bigger.
|
|
||||||
// Currently the crater is a little bit bigger than the original implementation
|
|
||||||
this.energy = strength * 0.3F;
|
|
||||||
this.currentRayPosition = 0.0;
|
this.currentRayPosition = 0.0;
|
||||||
|
|
||||||
double dirX = dir.xCoord;
|
double dirX = dir.xCoord;
|
||||||
@ -484,14 +598,24 @@ public class ExplosionNukeRayParallelized implements IExplosionRay {
|
|||||||
energy = 0;
|
energy = 0;
|
||||||
} else {
|
} else {
|
||||||
double energyLossFactor = getEnergyLossFactor(resistance);
|
double energyLossFactor = getEnergyLossFactor(resistance);
|
||||||
energy -= (float) (energyLossFactor * segmentLenForProcessing);
|
float damageDealt = (float) (energyLossFactor * segmentLenForProcessing);
|
||||||
if (energy > 0) {
|
energy -= damageDealt;
|
||||||
ConcurrentBitSet bs = destructionMap.computeIfAbsent(
|
if (damageDealt > 0) {
|
||||||
ck.pos,
|
|
||||||
posKey -> new ConcurrentBitSet()
|
|
||||||
);
|
|
||||||
int bitIndex = ((WORLD_HEIGHT - 1 - y) << 8) | ((x & 0xF) << 4) | (z & 0xF);
|
int bitIndex = ((WORLD_HEIGHT - 1 - y) << 8) | ((x & 0xF) << 4) | (z & 0xF);
|
||||||
bs.set(bitIndex);
|
if (BombConfig.accumulatedDestruction) {
|
||||||
|
ChunkCoordIntPair chunkPos = ck.pos;
|
||||||
|
ChunkDamageAccumulator chunkAccumulator =
|
||||||
|
accumulatedDamageMap.computeIfAbsent(chunkPos, k -> new ChunkDamageAccumulator());
|
||||||
|
chunkAccumulator.addDamage(bitIndex, damageDealt);
|
||||||
|
} else {
|
||||||
|
if (energy > 0) {
|
||||||
|
ConcurrentBitSet bs = destructionMap.computeIfAbsent(
|
||||||
|
ck.pos,
|
||||||
|
posKey -> new ConcurrentBitSet()
|
||||||
|
);
|
||||||
|
bs.set(bitIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user