mirror of
https://github.com/HbmMods/Hbm-s-Nuclear-Tech-GIT.git
synced 2026-01-25 10:32:49 +00:00
DDA cleanup
This commit is contained in:
parent
acbea88eca
commit
70cd3d4071
@ -26,8 +26,7 @@ public class BombConfig {
|
|||||||
public static int fDelay = 4;
|
public static int fDelay = 4;
|
||||||
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 int explosionAlgorithm = 2;
|
||||||
public static boolean accumulatedDestruction = true;
|
|
||||||
|
|
||||||
public static void loadFromConfig(Configuration config) {
|
public static void loadFromConfig(Configuration config) {
|
||||||
|
|
||||||
@ -95,8 +94,7 @@ public class BombConfig {
|
|||||||
falloutDelayProp.comment = "How many ticks to wait for the next fallout chunk computation";
|
falloutDelayProp.comment = "How many ticks to wait for the next fallout chunk computation";
|
||||||
fDelay = falloutDelayProp.getInt();
|
fDelay = falloutDelayProp.getInt();
|
||||||
|
|
||||||
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.05_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);
|
explosionAlgorithm = CommonConfig.createConfigInt(config, CATEGORY_NUKE, "6.06_explosionAlgorithm", "Configures the algorithm of mk5 explosion. \n0 = Legacy, 1 = Threaded DDA, 2 = Threaded DDA with damage accumulation.", 2);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -70,7 +70,7 @@ public class EntityNukeExplosionMK5 extends EntityExplosionChunkloading {
|
|||||||
|
|
||||||
if(explosion == null) {
|
if(explosion == null) {
|
||||||
explosionStart = System.currentTimeMillis();
|
explosionStart = System.currentTimeMillis();
|
||||||
if (BombConfig.parallelization) {
|
if (BombConfig.explosionAlgorithm == 1 || BombConfig.explosionAlgorithm == 2) {
|
||||||
explosion = new ExplosionNukeRayParallelized(worldObj, posX, posY, posZ,
|
explosion = new ExplosionNukeRayParallelized(worldObj, posX, posY, posZ,
|
||||||
strength, speed, length);
|
strength, speed, length);
|
||||||
} else {
|
} else {
|
||||||
@ -82,20 +82,17 @@ public class EntityNukeExplosionMK5 extends EntityExplosionChunkloading {
|
|||||||
if(!explosion.isComplete()) {
|
if(!explosion.isComplete()) {
|
||||||
explosion.cacheChunksTick(BombConfig.mk5);
|
explosion.cacheChunksTick(BombConfig.mk5);
|
||||||
explosion.destructionTick(BombConfig.mk5);
|
explosion.destructionTick(BombConfig.mk5);
|
||||||
} else if(fallout) {
|
|
||||||
if(GeneralConfig.enableExtendedLogging && explosionStart != 0)
|
|
||||||
MainRegistry.logger.log(Level.INFO, "[NUKE] Explosion complete. Time elapsed: " + (System.currentTimeMillis() - explosionStart) + "ms");
|
|
||||||
EntityFalloutRain fallout = new EntityFalloutRain(this.worldObj);
|
|
||||||
fallout.posX = this.posX;
|
|
||||||
fallout.posY = this.posY;
|
|
||||||
fallout.posZ = this.posZ;
|
|
||||||
fallout.setScale((int)(this.length * 2.5 + falloutAdd) * BombConfig.falloutRange / 100);
|
|
||||||
|
|
||||||
this.worldObj.spawnEntityInWorld(fallout);
|
|
||||||
|
|
||||||
this.clearChunkLoader();
|
|
||||||
this.setDead();
|
|
||||||
} else {
|
} else {
|
||||||
|
if(GeneralConfig.enableExtendedLogging && explosionStart != 0)
|
||||||
|
MainRegistry.logger.log(Level.INFO, "[NUKE] Explosion complete. Time elapsed: {}ms", (System.currentTimeMillis() - explosionStart));
|
||||||
|
if(fallout) {
|
||||||
|
EntityFalloutRain fallout = new EntityFalloutRain(this.worldObj);
|
||||||
|
fallout.posX = this.posX;
|
||||||
|
fallout.posY = this.posY;
|
||||||
|
fallout.posZ = this.posZ;
|
||||||
|
fallout.setScale((int)(this.length * 2.5 + falloutAdd) * BombConfig.falloutRange / 100);
|
||||||
|
this.worldObj.spawnEntityInWorld(fallout);
|
||||||
|
}
|
||||||
this.clearChunkLoader();
|
this.clearChunkLoader();
|
||||||
this.setDead();
|
this.setDead();
|
||||||
}
|
}
|
||||||
@ -153,7 +150,7 @@ public class EntityNukeExplosionMK5 extends EntityExplosionChunkloading {
|
|||||||
public static EntityNukeExplosionMK5 statFac(World world, int r, double x, double y, double z) {
|
public static EntityNukeExplosionMK5 statFac(World world, int r, double x, double y, double z) {
|
||||||
|
|
||||||
if(GeneralConfig.enableExtendedLogging && !world.isRemote)
|
if(GeneralConfig.enableExtendedLogging && !world.isRemote)
|
||||||
MainRegistry.logger.log(Level.INFO, "[NUKE] Initialized explosion at " + x + " / " + y + " / " + z + " with strength " + r + "!");
|
MainRegistry.logger.log(Level.INFO, "[NUKE] Initialized explosion at {} / {} / {} with strength {}!", x, y, z, r);
|
||||||
|
|
||||||
if(r == 0)
|
if(r == 0)
|
||||||
r = 25;
|
r = 25;
|
||||||
|
|||||||
@ -3,6 +3,9 @@ package com.hbm.explosion;
|
|||||||
import com.hbm.config.BombConfig;
|
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 com.hbm.util.ChunkKey;
|
||||||
|
import com.hbm.util.ConcurrentBitSet;
|
||||||
|
import com.hbm.util.SubChunkSnapshot;
|
||||||
import net.minecraft.block.Block;
|
import net.minecraft.block.Block;
|
||||||
import net.minecraft.init.Blocks;
|
import net.minecraft.init.Blocks;
|
||||||
import net.minecraft.util.Vec3;
|
import net.minecraft.util.Vec3;
|
||||||
@ -15,14 +18,14 @@ 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.DoubleAdder;
|
import java.util.concurrent.atomic.DoubleAdder;
|
||||||
|
|
||||||
|
import static com.hbm.util.SubChunkSnapshot.getSnapshot;
|
||||||
|
|
||||||
public class ExplosionNukeRayParallelized implements IExplosionRay {
|
public class ExplosionNukeRayParallelized implements IExplosionRay {
|
||||||
|
|
||||||
private static final int WORLD_HEIGHT = 256;
|
private static final int WORLD_HEIGHT = 256;
|
||||||
private static final int BITSET_SIZE = 16 * WORLD_HEIGHT * 16;
|
private static final int BITSET_SIZE = 16 * WORLD_HEIGHT * 16;
|
||||||
private static final int WORDS_PER_SET = BITSET_SIZE >>> 6; // (16*256*16)/64
|
|
||||||
|
|
||||||
protected final World world;
|
protected final World world;
|
||||||
private final double explosionX, explosionY, explosionZ;
|
private final double explosionX, explosionY, explosionZ;
|
||||||
@ -85,7 +88,7 @@ public class ExplosionNukeRayParallelized implements IExplosionRay {
|
|||||||
Thread.currentThread().interrupt();
|
Thread.currentThread().interrupt();
|
||||||
} finally {
|
} finally {
|
||||||
collectFinished = true;
|
collectFinished = true;
|
||||||
if (BombConfig.accumulatedDestruction) {
|
if (BombConfig.explosionAlgorithm == 2) {
|
||||||
pool.submit(this::runConsolidation);
|
pool.submit(this::runConsolidation);
|
||||||
} else {
|
} else {
|
||||||
consolidationFinished = true;
|
consolidationFinished = true;
|
||||||
@ -112,7 +115,7 @@ public class ExplosionNukeRayParallelized implements IExplosionRay {
|
|||||||
ChunkKey ck = cacheQueue.poll();
|
ChunkKey ck = cacheQueue.poll();
|
||||||
if (ck == null) break;
|
if (ck == null) break;
|
||||||
snapshots.computeIfAbsent(ck, key -> {
|
snapshots.computeIfAbsent(ck, key -> {
|
||||||
SubChunkSnapshot snap = createSubChunk(key.pos, key.subY);
|
SubChunkSnapshot snap = getSnapshot(this.world, key.pos, key.subY);
|
||||||
return snap == null ? SubChunkSnapshot.EMPTY : snap;
|
return snap == null ? SubChunkSnapshot.EMPTY : snap;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -146,37 +149,33 @@ public class ExplosionNukeRayParallelized implements IExplosionRay {
|
|||||||
ExtendedBlockStorage storage = storages[subY];
|
ExtendedBlockStorage storage = storages[subY];
|
||||||
if (storage == null) continue;
|
if (storage == null) continue;
|
||||||
|
|
||||||
int yPrimeMin = 255 - ((subY << 4) + 15);
|
int startBit = (WORLD_HEIGHT - 1 - ((subY << 4) + 15)) << 8;
|
||||||
int startBit = yPrimeMin << 8;
|
int endBit = ((WORLD_HEIGHT - 1 - (subY << 4)) << 8) | 0xFF;
|
||||||
int yPrimeMax = 255 - (subY << 4);
|
|
||||||
int endBit = (yPrimeMax << 8) | 0xFF;
|
|
||||||
|
|
||||||
int bit = bs.nextSetBit(startBit);
|
int bit = bs.nextSetBit(startBit);
|
||||||
if (bit < 0 || bit > endBit) continue;
|
|
||||||
|
|
||||||
while (bit >= 0 && bit <= endBit && System.nanoTime() < deadline) {
|
while (bit >= 0 && bit <= endBit && System.nanoTime() < deadline) {
|
||||||
int yGlobal = 255 - (bit >>> 8);
|
int yGlobal = WORLD_HEIGHT - 1 - (bit >>> 8);
|
||||||
int xGlobal = (cp.chunkXPos << 4) | ((bit >>> 4) & 0xF);
|
int xGlobal = (cp.chunkXPos << 4) | ((bit >>> 4) & 0xF);
|
||||||
int zGlobal = (cp.chunkZPos << 4) | (bit & 0xF);
|
int zGlobal = (cp.chunkZPos << 4) | (bit & 0xF);
|
||||||
|
|
||||||
if (world.getTileEntity(xGlobal, yGlobal, zGlobal) != null) {
|
|
||||||
chunk.removeTileEntity(xGlobal & 0xF, yGlobal, zGlobal & 0xF); // world Y
|
|
||||||
world.removeTileEntity(xGlobal, yGlobal, zGlobal);
|
|
||||||
}
|
|
||||||
|
|
||||||
int xLocal = xGlobal & 0xF;
|
int xLocal = xGlobal & 0xF;
|
||||||
int yLocal = yGlobal & 0xF;
|
int yLocal = yGlobal & 0xF;
|
||||||
int zLocal = zGlobal & 0xF;
|
int zLocal = zGlobal & 0xF;
|
||||||
storage.func_150818_a(xLocal, yLocal, zLocal, Blocks.air);
|
if (storage.getBlockByExtId(xLocal, yLocal, zLocal) != Blocks.air) {
|
||||||
storage.setExtBlockMetadata(xLocal, yLocal, zLocal, 0);
|
if (world.getTileEntity(xGlobal, yGlobal, zGlobal) != null) {
|
||||||
chunkModified = true;
|
world.removeTileEntity(xGlobal, yGlobal, zGlobal);
|
||||||
|
}
|
||||||
|
|
||||||
world.notifyBlocksOfNeighborChange(xGlobal, yGlobal, zGlobal, Blocks.air);
|
storage.func_150818_a(xLocal, yLocal, zLocal, Blocks.air);
|
||||||
world.markBlockForUpdate(xGlobal, yGlobal, zGlobal);
|
storage.setExtBlockMetadata(xLocal, yLocal, zLocal, 0);
|
||||||
|
chunkModified = true;
|
||||||
|
|
||||||
world.updateLightByType(EnumSkyBlock.Sky, xGlobal, yGlobal, zGlobal);
|
world.notifyBlocksOfNeighborChange(xGlobal, yGlobal, zGlobal, Blocks.air);
|
||||||
world.updateLightByType(EnumSkyBlock.Block, xGlobal, yGlobal, zGlobal);
|
world.markBlockForUpdate(xGlobal, yGlobal, zGlobal);
|
||||||
|
|
||||||
|
world.updateLightByType(EnumSkyBlock.Sky, xGlobal, yGlobal, zGlobal);
|
||||||
|
world.updateLightByType(EnumSkyBlock.Block, xGlobal, yGlobal, zGlobal);
|
||||||
|
}
|
||||||
bs.clear(bit);
|
bs.clear(bit);
|
||||||
bit = bs.nextSetBit(bit + 1);
|
bit = bs.nextSetBit(bit + 1);
|
||||||
}
|
}
|
||||||
@ -186,7 +185,6 @@ public class ExplosionNukeRayParallelized implements IExplosionRay {
|
|||||||
chunk.setChunkModified();
|
chunk.setChunkModified();
|
||||||
world.markBlockRangeForRenderUpdate(cp.chunkXPos << 4, 0, cp.chunkZPos << 4, (cp.chunkXPos << 4) | 15, WORLD_HEIGHT - 1, (cp.chunkZPos << 4) | 15);
|
world.markBlockRangeForRenderUpdate(cp.chunkXPos << 4, 0, cp.chunkZPos << 4, (cp.chunkXPos << 4) | 15, WORLD_HEIGHT - 1, (cp.chunkZPos << 4) | 15);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bs.isEmpty()) {
|
if (bs.isEmpty()) {
|
||||||
destructionMap.remove(cp);
|
destructionMap.remove(cp);
|
||||||
for (int sy = 0; sy < (WORLD_HEIGHT >> 4); sy++) {
|
for (int sy = 0; sy < (WORLD_HEIGHT >> 4); sy++) {
|
||||||
@ -244,49 +242,6 @@ public class ExplosionNukeRayParallelized implements IExplosionRay {
|
|||||||
if (this.orderedChunks != null) this.orderedChunks.clear();
|
if (this.orderedChunks != null) this.orderedChunks.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
private SubChunkSnapshot createSubChunk(ChunkCoordIntPair cpos, int subY) {
|
|
||||||
if (!world.getChunkProvider().chunkExists(cpos.chunkXPos, cpos.chunkZPos)) {
|
|
||||||
return SubChunkSnapshot.EMPTY;
|
|
||||||
}
|
|
||||||
Chunk chunk = world.getChunkFromChunkCoords(cpos.chunkXPos, cpos.chunkZPos);
|
|
||||||
ExtendedBlockStorage ebs = chunk.getBlockStorageArray()[subY];
|
|
||||||
if (ebs == null || ebs.isEmpty()) {
|
|
||||||
return SubChunkSnapshot.EMPTY;
|
|
||||||
}
|
|
||||||
|
|
||||||
short[] data = new short[16 * 16 * 16];
|
|
||||||
List<Block> palette = new ArrayList<>();
|
|
||||||
palette.add(Blocks.air);
|
|
||||||
Map<Block, Short> idxMap = new HashMap<>();
|
|
||||||
idxMap.put(Blocks.air, (short) 0);
|
|
||||||
boolean allAir = true;
|
|
||||||
|
|
||||||
for (int ly = 0; ly < 16; ly++) {
|
|
||||||
for (int lz = 0; lz < 16; lz++) {
|
|
||||||
for (int lx = 0; lx < 16; lx++) {
|
|
||||||
Block block = ebs.getBlockByExtId(lx, ly, lz);
|
|
||||||
int idx;
|
|
||||||
if (block == Blocks.air) {
|
|
||||||
idx = 0;
|
|
||||||
} else {
|
|
||||||
allAir = false;
|
|
||||||
Short e = idxMap.get(block);
|
|
||||||
if (e == null) {
|
|
||||||
idxMap.put(block, (short) palette.size());
|
|
||||||
palette.add(block);
|
|
||||||
idx = palette.size() - 1;
|
|
||||||
} else {
|
|
||||||
idx = e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
data[(ly << 8) | (lz << 4) | lx] = (short) idx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (allAir) return SubChunkSnapshot.EMPTY;
|
|
||||||
return new SubChunkSnapshot(palette.toArray(new Block[0]), data);
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<Vec3> generateSphereRays(int count) {
|
private List<Vec3> generateSphereRays(int count) {
|
||||||
List<Vec3> list = new ArrayList<>(count);
|
List<Vec3> list = new ArrayList<>(count);
|
||||||
if (count <= 0) return list;
|
if (count <= 0) return list;
|
||||||
@ -316,7 +271,7 @@ public class ExplosionNukeRayParallelized implements IExplosionRay {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ConcurrentBitSet chunkDestructionBitSet = destructionMap.computeIfAbsent(cp, k -> new ConcurrentBitSet());
|
ConcurrentBitSet chunkDestructionBitSet = destructionMap.computeIfAbsent(cp, k -> new ConcurrentBitSet(BITSET_SIZE));
|
||||||
|
|
||||||
Iterator<Map.Entry<Integer, DoubleAdder>> damageEntryIterator = accumulator.entrySet().iterator();
|
Iterator<Map.Entry<Integer, DoubleAdder>> damageEntryIterator = accumulator.entrySet().iterator();
|
||||||
while (damageEntryIterator.hasNext()) {
|
while (damageEntryIterator.hasNext()) {
|
||||||
@ -330,7 +285,7 @@ public class ExplosionNukeRayParallelized implements IExplosionRay {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
int yGlobal = 255 - (bitIndex >>> 8);
|
int yGlobal = WORLD_HEIGHT - 1 - (bitIndex >>> 8);
|
||||||
int subY = yGlobal >> 4;
|
int subY = yGlobal >> 4;
|
||||||
|
|
||||||
if (subY < 0) {
|
if (subY < 0) {
|
||||||
@ -370,91 +325,6 @@ public class ExplosionNukeRayParallelized implements IExplosionRay {
|
|||||||
consolidationFinished = true;
|
consolidationFinished = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class ChunkKey {
|
|
||||||
final ChunkCoordIntPair pos;
|
|
||||||
final int subY;
|
|
||||||
|
|
||||||
ChunkKey(int cx, int cz, int sy) {
|
|
||||||
this.pos = new ChunkCoordIntPair(cx, cz);
|
|
||||||
this.subY = sy;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (this == o) return true;
|
|
||||||
if (!(o instanceof ChunkKey)) return false;
|
|
||||||
ChunkKey k = (ChunkKey) o;
|
|
||||||
return subY == k.subY && pos.equals(k.pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return Objects.hash(pos.chunkXPos, pos.chunkZPos, subY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class SubChunkSnapshot {
|
|
||||||
private static final SubChunkSnapshot EMPTY = new SubChunkSnapshot(new Block[]{Blocks.air}, null);
|
|
||||||
private final Block[] palette;
|
|
||||||
private final short[] data;
|
|
||||||
|
|
||||||
SubChunkSnapshot(Block[] p, short[] d) {
|
|
||||||
this.palette = p;
|
|
||||||
this.data = d;
|
|
||||||
}
|
|
||||||
|
|
||||||
Block getBlock(int x, int y, int z) {
|
|
||||||
if (this == EMPTY || data == null) return Blocks.air;
|
|
||||||
short idx = data[(y << 8) | (z << 4) | x];
|
|
||||||
return (idx >= 0 && idx < palette.length) ? palette[idx] : Blocks.air;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final class ConcurrentBitSet {
|
|
||||||
private final AtomicLongArray words = new AtomicLongArray(WORDS_PER_SET);
|
|
||||||
|
|
||||||
void set(int bit) {
|
|
||||||
if (bit < 0 || bit >= BITSET_SIZE) return;
|
|
||||||
int wd = bit >>> 6;
|
|
||||||
long m = 1L << (bit & 63);
|
|
||||||
while (true) {
|
|
||||||
long o = words.get(wd);
|
|
||||||
long u = o | m;
|
|
||||||
if (o == u || words.compareAndSet(wd, o, u)) return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void clear(int bit) {
|
|
||||||
if (bit < 0 || bit >= BITSET_SIZE) return;
|
|
||||||
int wd = bit >>> 6;
|
|
||||||
long m = ~(1L << (bit & 63));
|
|
||||||
while (true) {
|
|
||||||
long oldWord = words.get(wd);
|
|
||||||
long newWord = oldWord & m;
|
|
||||||
if (oldWord == newWord || words.compareAndSet(wd, oldWord, newWord)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int nextSetBit(int from) {
|
|
||||||
if (from < 0) from = 0;
|
|
||||||
int wd = from >>> 6;
|
|
||||||
if (wd >= WORDS_PER_SET) return -1;
|
|
||||||
long w = words.get(wd) & (~0L << (from & 63));
|
|
||||||
while (true) {
|
|
||||||
if (w != 0) return (wd << 6) + Long.numberOfTrailingZeros(w);
|
|
||||||
if (++wd == WORDS_PER_SET) return -1;
|
|
||||||
w = words.get(wd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean isEmpty() {
|
|
||||||
for (int i = 0; i < WORDS_PER_SET; i++) if (words.get(i) != 0) return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class ChunkDamageAccumulator {
|
private static class ChunkDamageAccumulator {
|
||||||
// key = bitIndex, value = total accumulated damage
|
// key = bitIndex, value = total accumulated damage
|
||||||
private final ConcurrentHashMap<Integer, DoubleAdder> damageMap = new ConcurrentHashMap<>();
|
private final ConcurrentHashMap<Integer, DoubleAdder> damageMap = new ConcurrentHashMap<>();
|
||||||
@ -602,7 +472,7 @@ public class ExplosionNukeRayParallelized implements IExplosionRay {
|
|||||||
energy -= damageDealt;
|
energy -= damageDealt;
|
||||||
if (damageDealt > 0) {
|
if (damageDealt > 0) {
|
||||||
int bitIndex = ((WORLD_HEIGHT - 1 - y) << 8) | ((x & 0xF) << 4) | (z & 0xF);
|
int bitIndex = ((WORLD_HEIGHT - 1 - y) << 8) | ((x & 0xF) << 4) | (z & 0xF);
|
||||||
if (BombConfig.accumulatedDestruction) {
|
if (BombConfig.explosionAlgorithm == 2) {
|
||||||
ChunkCoordIntPair chunkPos = ck.pos;
|
ChunkCoordIntPair chunkPos = ck.pos;
|
||||||
ChunkDamageAccumulator chunkAccumulator =
|
ChunkDamageAccumulator chunkAccumulator =
|
||||||
accumulatedDamageMap.computeIfAbsent(chunkPos, k -> new ChunkDamageAccumulator());
|
accumulatedDamageMap.computeIfAbsent(chunkPos, k -> new ChunkDamageAccumulator());
|
||||||
@ -611,7 +481,7 @@ public class ExplosionNukeRayParallelized implements IExplosionRay {
|
|||||||
if (energy > 0) {
|
if (energy > 0) {
|
||||||
ConcurrentBitSet bs = destructionMap.computeIfAbsent(
|
ConcurrentBitSet bs = destructionMap.computeIfAbsent(
|
||||||
ck.pos,
|
ck.pos,
|
||||||
posKey -> new ConcurrentBitSet()
|
posKey -> new ConcurrentBitSet(BITSET_SIZE)
|
||||||
);
|
);
|
||||||
bs.set(bitIndex);
|
bs.set(bitIndex);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,31 @@
|
|||||||
package com.hbm.interfaces;
|
package com.hbm.interfaces;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for procedural explosions.
|
||||||
|
* @author mlbv
|
||||||
|
*/
|
||||||
public interface IExplosionRay {
|
public interface IExplosionRay {
|
||||||
boolean isComplete();
|
/**
|
||||||
|
* Called every tick. Caches the chunks affected by the explosion.
|
||||||
|
* All heavy calculations are recommended to be done off the main thread.
|
||||||
|
* @param processTimeMs maximum time to process in this tick
|
||||||
|
*/
|
||||||
|
void cacheChunksTick(int processTimeMs);
|
||||||
|
|
||||||
void cacheChunksTick(int processTime);
|
/**
|
||||||
|
* Called every tick to apply block destruction to the affected chunks.
|
||||||
void destructionTick(int processTime);
|
* @param processTimeMs maximum time to process in this tick
|
||||||
|
*/
|
||||||
|
void destructionTick(int processTimeMs);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Immediately cancels the explosion.
|
||||||
|
*/
|
||||||
void cancel();
|
void cancel();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true if the explosion is finished or cancelled.
|
||||||
|
*/
|
||||||
|
boolean isComplete();
|
||||||
}
|
}
|
||||||
|
|||||||
32
src/main/java/com/hbm/util/ChunkKey.java
Normal file
32
src/main/java/com/hbm/util/ChunkKey.java
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package com.hbm.util;
|
||||||
|
|
||||||
|
import net.minecraft.world.ChunkCoordIntPair;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unique identifier for sub-chunks.
|
||||||
|
* @Author mlbv
|
||||||
|
*/
|
||||||
|
public class ChunkKey {
|
||||||
|
public final ChunkCoordIntPair pos;
|
||||||
|
public final int subY;
|
||||||
|
|
||||||
|
public ChunkKey(int cx, int cz, int sy) {
|
||||||
|
this.pos = new ChunkCoordIntPair(cx, cz);
|
||||||
|
this.subY = sy;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (!(o instanceof ChunkKey)) return false;
|
||||||
|
ChunkKey k = (ChunkKey) o;
|
||||||
|
return subY == k.subY && pos.equals(k.pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(pos.chunkXPos, pos.chunkZPos, subY);
|
||||||
|
}
|
||||||
|
}
|
||||||
70
src/main/java/com/hbm/util/ConcurrentBitSet.java
Normal file
70
src/main/java/com/hbm/util/ConcurrentBitSet.java
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
package com.hbm.util;
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicLongArray;
|
||||||
|
import java.util.concurrent.atomic.LongAdder;
|
||||||
|
|
||||||
|
public class ConcurrentBitSet {
|
||||||
|
private final AtomicLongArray words;
|
||||||
|
private final int size;
|
||||||
|
private final LongAdder bitCount = new LongAdder();
|
||||||
|
|
||||||
|
public ConcurrentBitSet(int size) {
|
||||||
|
this.size = size;
|
||||||
|
int wordCount = (size + 63) >>> 6;
|
||||||
|
this.words = new AtomicLongArray(wordCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set(int bit) {
|
||||||
|
if (bit < 0 || bit >= size) return;
|
||||||
|
int wordIndex = bit >>> 6;
|
||||||
|
long mask = 1L << (bit & 63);
|
||||||
|
while (true) {
|
||||||
|
long oldWord = words.get(wordIndex);
|
||||||
|
long newWord = oldWord | mask;
|
||||||
|
if (oldWord == newWord) return;
|
||||||
|
if (words.compareAndSet(wordIndex, oldWord, newWord)) {
|
||||||
|
bitCount.increment();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clear(int bit) {
|
||||||
|
if (bit < 0 || bit >= size) return;
|
||||||
|
int wordIndex = bit >>> 6;
|
||||||
|
long mask = ~(1L << (bit & 63));
|
||||||
|
while (true) {
|
||||||
|
long oldWord = words.get(wordIndex);
|
||||||
|
long newWord = oldWord & mask;
|
||||||
|
if (oldWord == newWord) return;
|
||||||
|
if (words.compareAndSet(wordIndex, oldWord, newWord)) {
|
||||||
|
bitCount.decrement();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int nextSetBit(int from) {
|
||||||
|
if (from < 0) from = 0;
|
||||||
|
int wordIndex = from >>> 6;
|
||||||
|
if (wordIndex >= words.length()) return -1;
|
||||||
|
long word = words.get(wordIndex) & (~0L << (from & 63));
|
||||||
|
while (true) {
|
||||||
|
if (word != 0) {
|
||||||
|
int idx = (wordIndex << 6) + Long.numberOfTrailingZeros(word);
|
||||||
|
return (idx < size) ? idx : -1;
|
||||||
|
}
|
||||||
|
wordIndex++;
|
||||||
|
if (wordIndex >= words.length()) return -1;
|
||||||
|
word = words.get(wordIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return bitCount.sum() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long cardinality() {
|
||||||
|
return bitCount.sum();
|
||||||
|
}
|
||||||
|
}
|
||||||
120
src/main/java/com/hbm/util/SubChunkSnapshot.java
Normal file
120
src/main/java/com/hbm/util/SubChunkSnapshot.java
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
package com.hbm.util;
|
||||||
|
|
||||||
|
import net.minecraft.block.Block;
|
||||||
|
import net.minecraft.init.Blocks;
|
||||||
|
import net.minecraft.world.ChunkCoordIntPair;
|
||||||
|
import net.minecraft.world.World;
|
||||||
|
import net.minecraft.world.chunk.Chunk;
|
||||||
|
import net.minecraft.world.chunk.storage.ExtendedBlockStorage;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A snapshot of a 16×16×16 sub-chunk.
|
||||||
|
* @Author mlbv
|
||||||
|
*/
|
||||||
|
public class SubChunkSnapshot {
|
||||||
|
/**
|
||||||
|
* A sub-chunk that contains only air.
|
||||||
|
*/
|
||||||
|
public static final SubChunkSnapshot EMPTY = new SubChunkSnapshot(new Block[]{Blocks.air}, null);
|
||||||
|
private final Block[] palette;
|
||||||
|
private final short[] data;
|
||||||
|
|
||||||
|
private SubChunkSnapshot(Block[] p, short[] d) {
|
||||||
|
this.palette = p;
|
||||||
|
this.data = d;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a SubChunkSnapshot from a loaded chunk.
|
||||||
|
*
|
||||||
|
* @param world
|
||||||
|
* The World instance from which to retrieve the chunk.
|
||||||
|
* @param cpos
|
||||||
|
* The ChunkCoordIntPair identifying the chunk coordinates (x, z).
|
||||||
|
* @param subY
|
||||||
|
* The vertical sub-chunk index (0–15) within the chunk.
|
||||||
|
* @return
|
||||||
|
* A SubChunkSnapshot containing the palette and block data for the sub-chunk,
|
||||||
|
* or SubChunkSnapshot.EMPTY if the region is unloaded or contains only air.
|
||||||
|
*/
|
||||||
|
public static SubChunkSnapshot getSnapshot(World world, ChunkCoordIntPair cpos, int subY) {
|
||||||
|
if (!world.getChunkProvider().chunkExists(cpos.chunkXPos, cpos.chunkZPos)) {
|
||||||
|
return SubChunkSnapshot.EMPTY;
|
||||||
|
}
|
||||||
|
return getOrLoadSnapshot(world, cpos, subY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a SubChunkSnapshot.
|
||||||
|
*
|
||||||
|
* @param world
|
||||||
|
* The World instance from which to retrieve the chunk.
|
||||||
|
* @param cpos
|
||||||
|
* The ChunkCoordIntPair identifying the chunk coordinates (x, z).
|
||||||
|
* @param subY
|
||||||
|
* The vertical sub-chunk index (0–15) within the chunk.
|
||||||
|
* @return
|
||||||
|
* A SubChunkSnapshot containing the palette and block data for the sub-chunk,
|
||||||
|
* or SubChunkSnapshot.EMPTY if the region contains only air.
|
||||||
|
*/
|
||||||
|
public static SubChunkSnapshot getOrLoadSnapshot(World world, ChunkCoordIntPair cpos, int subY){
|
||||||
|
Chunk chunk = world.getChunkFromChunkCoords(cpos.chunkXPos, cpos.chunkZPos);
|
||||||
|
ExtendedBlockStorage ebs = chunk.getBlockStorageArray()[subY];
|
||||||
|
if (ebs == null || ebs.isEmpty()) return SubChunkSnapshot.EMPTY;
|
||||||
|
|
||||||
|
short[] data = new short[16 * 16 * 16];
|
||||||
|
List<Block> palette = new ArrayList<>();
|
||||||
|
palette.add(Blocks.air);
|
||||||
|
Map<Block, Short> idxMap = new HashMap<>();
|
||||||
|
idxMap.put(Blocks.air, (short) 0);
|
||||||
|
boolean allAir = true;
|
||||||
|
|
||||||
|
for (int ly = 0; ly < 16; ly++) {
|
||||||
|
for (int lz = 0; lz < 16; lz++) {
|
||||||
|
for (int lx = 0; lx < 16; lx++) {
|
||||||
|
Block block = ebs.getBlockByExtId(lx, ly, lz);
|
||||||
|
int idx;
|
||||||
|
if (block == Blocks.air) {
|
||||||
|
idx = 0;
|
||||||
|
} else {
|
||||||
|
allAir = false;
|
||||||
|
Short e = idxMap.get(block);
|
||||||
|
if (e == null) {
|
||||||
|
idxMap.put(block, (short) palette.size());
|
||||||
|
palette.add(block);
|
||||||
|
idx = palette.size() - 1;
|
||||||
|
} else {
|
||||||
|
idx = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data[(ly << 8) | (lz << 4) | lx] = (short) idx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (allAir) return SubChunkSnapshot.EMPTY;
|
||||||
|
return new SubChunkSnapshot(palette.toArray(new Block[0]), data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the Block at the specified local coordinates within this sub-chunk snapshot.
|
||||||
|
*
|
||||||
|
* @param x
|
||||||
|
* The local x-coordinate within the sub-chunk (0–15).
|
||||||
|
* @param y
|
||||||
|
* The local y-coordinate within the sub-chunk (0–15).
|
||||||
|
* @param z
|
||||||
|
* The local z-coordinate within the sub-chunk (0–15).
|
||||||
|
* @return
|
||||||
|
* The Block instance at the given position.
|
||||||
|
*/
|
||||||
|
public Block getBlock(int x, int y, int z) {
|
||||||
|
if (this == EMPTY || data == null) return Blocks.air;
|
||||||
|
short idx = data[(y << 8) | (z << 4) | x];
|
||||||
|
return (idx >= 0 && idx < palette.length) ? palette[idx] : Blocks.air;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user