mirror of
https://github.com/HbmMods/Hbm-s-Nuclear-Tech-GIT.git
synced 2026-01-25 10:32:49 +00:00
feat: parallelized explosion calculation
and is configurable
This commit is contained in:
parent
d19894fa3d
commit
bf2a4b776f
@ -26,7 +26,8 @@ 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 void loadFromConfig(Configuration config) {
|
public static void loadFromConfig(Configuration config) {
|
||||||
|
|
||||||
final String CATEGORY_NUKES = CommonConfig.CATEGORY_NUKES;
|
final String CATEGORY_NUKES = CommonConfig.CATEGORY_NUKES;
|
||||||
@ -92,7 +93,8 @@ public class BombConfig {
|
|||||||
Property falloutDelayProp = config.get(CATEGORY_NUKE, "6.04_falloutDelay", 4);
|
Property falloutDelayProp = config.get(CATEGORY_NUKE, "6.04_falloutDelay", 4);
|
||||||
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.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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package com.hbm.entity.logic;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.hbm.interfaces.IExplosionRay;
|
||||||
import org.apache.logging.log4j.Level;
|
import org.apache.logging.log4j.Level;
|
||||||
|
|
||||||
import com.hbm.config.BombConfig;
|
import com.hbm.config.BombConfig;
|
||||||
@ -9,6 +10,7 @@ import com.hbm.config.GeneralConfig;
|
|||||||
import com.hbm.entity.effect.EntityFalloutRain;
|
import com.hbm.entity.effect.EntityFalloutRain;
|
||||||
import com.hbm.explosion.ExplosionNukeGeneric;
|
import com.hbm.explosion.ExplosionNukeGeneric;
|
||||||
import com.hbm.explosion.ExplosionNukeRayBatched;
|
import com.hbm.explosion.ExplosionNukeRayBatched;
|
||||||
|
import com.hbm.explosion.ExplosionNukeRayParallelized;
|
||||||
import com.hbm.main.MainRegistry;
|
import com.hbm.main.MainRegistry;
|
||||||
import com.hbm.util.ContaminationUtil;
|
import com.hbm.util.ContaminationUtil;
|
||||||
import com.hbm.util.ContaminationUtil.ContaminationType;
|
import com.hbm.util.ContaminationUtil.ContaminationType;
|
||||||
@ -22,32 +24,32 @@ import net.minecraft.util.Vec3;
|
|||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
public class EntityNukeExplosionMK5 extends EntityExplosionChunkloading {
|
public class EntityNukeExplosionMK5 extends EntityExplosionChunkloading {
|
||||||
|
|
||||||
//Strength of the blast
|
//Strength of the blast
|
||||||
public int strength;
|
public int strength;
|
||||||
//How many rays are calculated per tick
|
//How many rays are calculated per tick
|
||||||
public int speed;
|
public int speed;
|
||||||
public int length;
|
public int length;
|
||||||
|
private long explosionStart;
|
||||||
public boolean fallout = true;
|
public boolean fallout = true;
|
||||||
private int falloutAdd = 0;
|
private int falloutAdd = 0;
|
||||||
|
|
||||||
ExplosionNukeRayBatched explosion;
|
private IExplosionRay explosion;
|
||||||
|
|
||||||
public EntityNukeExplosionMK5(World p_i1582_1_) {
|
public EntityNukeExplosionMK5(World p_i1582_1_) {
|
||||||
super(p_i1582_1_);
|
super(p_i1582_1_);
|
||||||
}
|
}
|
||||||
|
|
||||||
public EntityNukeExplosionMK5(World world, int strength, int speed, int length) {
|
public EntityNukeExplosionMK5(World world, int strength, int speed, int length) {
|
||||||
super(world);
|
super(world);
|
||||||
this.strength = strength;
|
this.strength = strength;
|
||||||
this.speed = speed;
|
this.speed = speed;
|
||||||
this.length = length;
|
this.length = length;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onUpdate() {
|
public void onUpdate() {
|
||||||
|
|
||||||
if(strength == 0) {
|
if(strength == 0) {
|
||||||
this.clearChunkLoader();
|
this.clearChunkLoader();
|
||||||
this.setDead();
|
this.setDead();
|
||||||
@ -55,30 +57,34 @@ public class EntityNukeExplosionMK5 extends EntityExplosionChunkloading {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(!worldObj.isRemote) loadChunk((int) Math.floor(posX / 16D), (int) Math.floor(posZ / 16D));
|
if(!worldObj.isRemote) loadChunk((int) Math.floor(posX / 16D), (int) Math.floor(posZ / 16D));
|
||||||
|
|
||||||
for(Object player : this.worldObj.playerEntities) {
|
for(Object player : this.worldObj.playerEntities) {
|
||||||
((EntityPlayer)player).triggerAchievement(MainRegistry.achManhattan);
|
((EntityPlayer)player).triggerAchievement(MainRegistry.achManhattan);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!worldObj.isRemote && fallout && explosion != null && this.ticksExisted < 10 && strength >= 75) {
|
if(!worldObj.isRemote && fallout && explosion != null && this.ticksExisted < 10 && strength >= 75) {
|
||||||
radiate(2_500_000F / (this.ticksExisted * 5 + 1), this.length * 2);
|
radiate(2_500_000F / (this.ticksExisted * 5 + 1), this.length * 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
ExplosionNukeGeneric.dealDamage(this.worldObj, this.posX, this.posY, this.posZ, this.length * 2);
|
|
||||||
|
|
||||||
if(explosion == null) {
|
|
||||||
explosion = new ExplosionNukeRayBatched(worldObj, (int)this.posX, (int)this.posY, (int)this.posZ, this.strength, this.speed, this.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!explosion.isAusf3Complete) {
|
|
||||||
explosion.collectTip(speed * 10);
|
|
||||||
} else if(explosion.perChunk.size() > 0) {
|
|
||||||
long start = System.currentTimeMillis();
|
|
||||||
|
|
||||||
while(explosion.perChunk.size() > 0 && System.currentTimeMillis() < start + BombConfig.mk5) explosion.processChunk();
|
|
||||||
|
|
||||||
} else if(fallout) {
|
|
||||||
|
|
||||||
|
ExplosionNukeGeneric.dealDamage(this.worldObj, this.posX, this.posY, this.posZ, this.length * 2);
|
||||||
|
|
||||||
|
if(explosion == null) {
|
||||||
|
explosionStart = System.currentTimeMillis();
|
||||||
|
if (BombConfig.parallelization) {
|
||||||
|
explosion = new ExplosionNukeRayParallelized(worldObj, posX, posY, posZ,
|
||||||
|
strength, speed, length);
|
||||||
|
} else {
|
||||||
|
explosion = new ExplosionNukeRayBatched(worldObj, (int) posX, (int) posY, (int) posZ,
|
||||||
|
strength, speed, length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!explosion.isComplete()) {
|
||||||
|
explosion.cacheChunksTick(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);
|
EntityFalloutRain fallout = new EntityFalloutRain(this.worldObj);
|
||||||
fallout.posX = this.posX;
|
fallout.posX = this.posX;
|
||||||
fallout.posY = this.posY;
|
fallout.posY = this.posY;
|
||||||
@ -94,35 +100,35 @@ public class EntityNukeExplosionMK5 extends EntityExplosionChunkloading {
|
|||||||
this.setDead();
|
this.setDead();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void radiate(float rads, double range) {
|
private void radiate(float rads, double range) {
|
||||||
|
|
||||||
List<EntityLivingBase> entities = worldObj.getEntitiesWithinAABB(EntityLivingBase.class, AxisAlignedBB.getBoundingBox(posX, posY, posZ, posX, posY, posZ).expand(range, range, range));
|
List<EntityLivingBase> entities = worldObj.getEntitiesWithinAABB(EntityLivingBase.class, AxisAlignedBB.getBoundingBox(posX, posY, posZ, posX, posY, posZ).expand(range, range, range));
|
||||||
|
|
||||||
for(EntityLivingBase e : entities) {
|
for(EntityLivingBase e : entities) {
|
||||||
|
|
||||||
Vec3 vec = Vec3.createVectorHelper(e.posX - posX, (e.posY + e.getEyeHeight()) - posY, e.posZ - posZ);
|
Vec3 vec = Vec3.createVectorHelper(e.posX - posX, (e.posY + e.getEyeHeight()) - posY, e.posZ - posZ);
|
||||||
double len = vec.lengthVector();
|
double len = vec.lengthVector();
|
||||||
vec = vec.normalize();
|
vec = vec.normalize();
|
||||||
|
|
||||||
float res = 0;
|
float res = 0;
|
||||||
|
|
||||||
for(int i = 1; i < len; i++) {
|
for(int i = 1; i < len; i++) {
|
||||||
|
|
||||||
int ix = (int)Math.floor(posX + vec.xCoord * i);
|
int ix = (int)Math.floor(posX + vec.xCoord * i);
|
||||||
int iy = (int)Math.floor(posY + vec.yCoord * i);
|
int iy = (int)Math.floor(posY + vec.yCoord * i);
|
||||||
int iz = (int)Math.floor(posZ + vec.zCoord * i);
|
int iz = (int)Math.floor(posZ + vec.zCoord * i);
|
||||||
|
|
||||||
res += worldObj.getBlock(ix, iy, iz).getExplosionResistance(null);
|
res += worldObj.getBlock(ix, iy, iz).getExplosionResistance(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(res < 1)
|
if(res < 1)
|
||||||
res = 1;
|
res = 1;
|
||||||
|
|
||||||
float eRads = rads;
|
float eRads = rads;
|
||||||
eRads /= (float)res;
|
eRads /= (float)res;
|
||||||
eRads /= (float)(len * len);
|
eRads /= (float)(len * len);
|
||||||
|
|
||||||
ContaminationUtil.contaminate(e, HazardType.RADIATION, ContaminationType.RAD_BYPASS, eRads);
|
ContaminationUtil.contaminate(e, HazardType.RADIATION, ContaminationType.RAD_BYPASS, eRads);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -136,17 +142,17 @@ public class EntityNukeExplosionMK5 extends EntityExplosionChunkloading {
|
|||||||
protected void writeEntityToNBT(NBTTagCompound nbt) {
|
protected void writeEntityToNBT(NBTTagCompound nbt) {
|
||||||
nbt.setInteger("ticksExisted", this.ticksExisted);
|
nbt.setInteger("ticksExisted", this.ticksExisted);
|
||||||
}
|
}
|
||||||
|
|
||||||
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 " + x + " / " + y + " / " + z + " with strength " + r + "!");
|
||||||
|
|
||||||
if(r == 0)
|
if(r == 0)
|
||||||
r = 25;
|
r = 25;
|
||||||
|
|
||||||
r *= 2;
|
r *= 2;
|
||||||
|
|
||||||
EntityNukeExplosionMK5 mk5 = new EntityNukeExplosionMK5(world);
|
EntityNukeExplosionMK5 mk5 = new EntityNukeExplosionMK5(world);
|
||||||
mk5.strength = (int)(r);
|
mk5.strength = (int)(r);
|
||||||
mk5.speed = (int)Math.ceil(100000 / mk5.strength);
|
mk5.speed = (int)Math.ceil(100000 / mk5.strength);
|
||||||
@ -154,14 +160,14 @@ public class EntityNukeExplosionMK5 extends EntityExplosionChunkloading {
|
|||||||
mk5.length = mk5.strength / 2;
|
mk5.length = mk5.strength / 2;
|
||||||
return mk5;
|
return mk5;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static EntityNukeExplosionMK5 statFacNoRad(World world, int r, double x, double y, double z) {
|
public static EntityNukeExplosionMK5 statFacNoRad(World world, int r, double x, double y, double z) {
|
||||||
|
|
||||||
EntityNukeExplosionMK5 mk5 = statFac(world, r, x, y ,z);
|
EntityNukeExplosionMK5 mk5 = statFac(world, r, x, y ,z);
|
||||||
mk5.fallout = false;
|
mk5.fallout = false;
|
||||||
return mk5;
|
return mk5;
|
||||||
}
|
}
|
||||||
|
|
||||||
public EntityNukeExplosionMK5 moreFallout(int fallout) {
|
public EntityNukeExplosionMK5 moreFallout(int fallout) {
|
||||||
falloutAdd = fallout;
|
falloutAdd = fallout;
|
||||||
return this;
|
return this;
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import java.util.HashMap;
|
|||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.hbm.interfaces.IExplosionRay;
|
||||||
import com.hbm.util.fauxpointtwelve.BlockPos;
|
import com.hbm.util.fauxpointtwelve.BlockPos;
|
||||||
|
|
||||||
import net.minecraft.block.Block;
|
import net.minecraft.block.Block;
|
||||||
@ -14,7 +15,7 @@ import net.minecraft.util.Vec3;
|
|||||||
import net.minecraft.world.ChunkCoordIntPair;
|
import net.minecraft.world.ChunkCoordIntPair;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
public class ExplosionNukeRayBatched {
|
public class ExplosionNukeRayBatched implements IExplosionRay {
|
||||||
|
|
||||||
public HashMap<ChunkCoordIntPair, List<FloatTriplet>> perChunk = new HashMap(); //for future: optimize blockmap further by using sub-chunks instead of chunks
|
public HashMap<ChunkCoordIntPair, List<FloatTriplet>> perChunk = new HashMap(); //for future: optimize blockmap further by using sub-chunks instead of chunks
|
||||||
public List<ChunkCoordIntPair> orderedChunks = new ArrayList();
|
public List<ChunkCoordIntPair> orderedChunks = new ArrayList();
|
||||||
@ -26,7 +27,7 @@ public class ExplosionNukeRayBatched {
|
|||||||
|
|
||||||
int strength;
|
int strength;
|
||||||
int length;
|
int length;
|
||||||
|
int speed;
|
||||||
int gspNumMax;
|
int gspNumMax;
|
||||||
int gspNum;
|
int gspNum;
|
||||||
double gspX;
|
double gspX;
|
||||||
@ -40,8 +41,8 @@ public class ExplosionNukeRayBatched {
|
|||||||
this.posY = y;
|
this.posY = y;
|
||||||
this.posZ = z;
|
this.posZ = z;
|
||||||
this.strength = strength;
|
this.strength = strength;
|
||||||
|
this.speed = speed;
|
||||||
this.length = length;
|
this.length = length;
|
||||||
|
|
||||||
// Total number of points
|
// Total number of points
|
||||||
this.gspNumMax = (int)(2.5 * Math.PI * Math.pow(this.strength,2));
|
this.gspNumMax = (int)(2.5 * Math.PI * Math.pow(this.strength,2));
|
||||||
this.gspNum = 1;
|
this.gspNum = 1;
|
||||||
@ -76,7 +77,7 @@ public class ExplosionNukeRayBatched {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void collectTip(int count) {
|
public void collectTip(int count) {
|
||||||
|
|
||||||
//count = Math.min(count, 10);
|
//count = Math.min(count, 10);
|
||||||
|
|
||||||
int amountProcessed = 0;
|
int amountProcessed = 0;
|
||||||
@ -106,7 +107,7 @@ public class ExplosionNukeRayBatched {
|
|||||||
|
|
||||||
double fac = 100 - ((double) i) / ((double) length) * 100;
|
double fac = 100 - ((double) i) / ((double) length) * 100;
|
||||||
fac *= 0.07D;
|
fac *= 0.07D;
|
||||||
|
|
||||||
Block block = world.getBlock(iX, iY, iZ);
|
Block block = world.getBlock(iX, iY, iZ);
|
||||||
|
|
||||||
if(!block.getMaterial().isLiquid())
|
if(!block.getMaterial().isLiquid())
|
||||||
@ -125,18 +126,18 @@ public class ExplosionNukeRayBatched {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for(ChunkCoordIntPair pos : chunkCoords) {
|
for(ChunkCoordIntPair pos : chunkCoords) {
|
||||||
List<FloatTriplet> triplets = perChunk.get(pos);
|
List<FloatTriplet> triplets = perChunk.get(pos);
|
||||||
|
|
||||||
if(triplets == null) {
|
if(triplets == null) {
|
||||||
triplets = new ArrayList();
|
triplets = new ArrayList();
|
||||||
perChunk.put(pos, triplets); //we re-use the same pos instead of using individualized per-chunk ones to save on RAM
|
perChunk.put(pos, triplets); //we re-use the same pos instead of using individualized per-chunk ones to save on RAM
|
||||||
}
|
}
|
||||||
|
|
||||||
triplets.add(lastPos);
|
triplets.add(lastPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Raise one generalized spiral points
|
// Raise one generalized spiral points
|
||||||
this.generateGspUp();
|
this.generateGspUp();
|
||||||
|
|
||||||
@ -145,20 +146,20 @@ public class ExplosionNukeRayBatched {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
orderedChunks.addAll(perChunk.keySet());
|
orderedChunks.addAll(perChunk.keySet());
|
||||||
orderedChunks.sort(comparator);
|
orderedChunks.sort(comparator);
|
||||||
|
|
||||||
isAusf3Complete = true;
|
isAusf3Complete = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static float masqueradeResistance(Block block) {
|
public static float masqueradeResistance(Block block) {
|
||||||
|
|
||||||
if(block == Blocks.sandstone) return Blocks.stone.getExplosionResistance(null);
|
if(block == Blocks.sandstone) return Blocks.stone.getExplosionResistance(null);
|
||||||
if(block == Blocks.obsidian) return Blocks.stone.getExplosionResistance(null) * 3;
|
if(block == Blocks.obsidian) return Blocks.stone.getExplosionResistance(null) * 3;
|
||||||
return block.getExplosionResistance(null);
|
return block.getExplosionResistance(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** little comparator for roughly sorting chunks by distance to the center */
|
/** little comparator for roughly sorting chunks by distance to the center */
|
||||||
public class CoordComparator implements Comparator<ChunkCoordIntPair> {
|
public class CoordComparator implements Comparator<ChunkCoordIntPair> {
|
||||||
|
|
||||||
@ -170,15 +171,15 @@ public class ExplosionNukeRayBatched {
|
|||||||
|
|
||||||
int diff1 = Math.abs((chunkX - o1.chunkXPos)) + Math.abs((chunkZ - o1.chunkZPos));
|
int diff1 = Math.abs((chunkX - o1.chunkXPos)) + Math.abs((chunkZ - o1.chunkZPos));
|
||||||
int diff2 = Math.abs((chunkX - o2.chunkXPos)) + Math.abs((chunkZ - o2.chunkZPos));
|
int diff2 = Math.abs((chunkX - o2.chunkXPos)) + Math.abs((chunkZ - o2.chunkZPos));
|
||||||
|
|
||||||
return diff1 - diff2;
|
return diff1 - diff2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void processChunk() {
|
public void processChunk() {
|
||||||
|
|
||||||
if(this.perChunk.isEmpty()) return;
|
if(this.perChunk.isEmpty()) return;
|
||||||
|
|
||||||
ChunkCoordIntPair coord = orderedChunks.get(0);
|
ChunkCoordIntPair coord = orderedChunks.get(0);
|
||||||
List<FloatTriplet> list = perChunk.get(coord);
|
List<FloatTriplet> list = perChunk.get(coord);
|
||||||
HashSet<BlockPos> toRem = new HashSet();
|
HashSet<BlockPos> toRem = new HashSet();
|
||||||
@ -186,13 +187,13 @@ public class ExplosionNukeRayBatched {
|
|||||||
//List<BlockPos> toRem = new ArrayList();
|
//List<BlockPos> toRem = new ArrayList();
|
||||||
int chunkX = coord.chunkXPos;
|
int chunkX = coord.chunkXPos;
|
||||||
int chunkZ = coord.chunkZPos;
|
int chunkZ = coord.chunkZPos;
|
||||||
|
|
||||||
int enter = (int) (Math.min(
|
int enter = (int) (Math.min(
|
||||||
Math.abs(posX - (chunkX << 4)),
|
Math.abs(posX - (chunkX << 4)),
|
||||||
Math.abs(posZ - (chunkZ << 4)))) - 16; //jump ahead to cut back on NOPs
|
Math.abs(posZ - (chunkZ << 4)))) - 16; //jump ahead to cut back on NOPs
|
||||||
|
|
||||||
enter = Math.max(enter, 0);
|
enter = Math.max(enter, 0);
|
||||||
|
|
||||||
for(FloatTriplet triplet : list) {
|
for(FloatTriplet triplet : list) {
|
||||||
float x = triplet.xCoord;
|
float x = triplet.xCoord;
|
||||||
float y = triplet.yCoord;
|
float y = triplet.yCoord;
|
||||||
@ -205,13 +206,13 @@ public class ExplosionNukeRayBatched {
|
|||||||
int tipX = (int) Math.floor(x);
|
int tipX = (int) Math.floor(x);
|
||||||
int tipY = (int) Math.floor(y);
|
int tipY = (int) Math.floor(y);
|
||||||
int tipZ = (int) Math.floor(z);
|
int tipZ = (int) Math.floor(z);
|
||||||
|
|
||||||
boolean inChunk = false;
|
boolean inChunk = false;
|
||||||
for(int i = enter; i < vec.lengthVector(); i++) {
|
for(int i = enter; i < vec.lengthVector(); i++) {
|
||||||
int x0 = (int) Math.floor(posX + pX * i);
|
int x0 = (int) Math.floor(posX + pX * i);
|
||||||
int y0 = (int) Math.floor(posY + pY * i);
|
int y0 = (int) Math.floor(posY + pY * i);
|
||||||
int z0 = (int) Math.floor(posZ + pZ * i);
|
int z0 = (int) Math.floor(posZ + pZ * i);
|
||||||
|
|
||||||
if(x0 >> 4 != chunkX || z0 >> 4 != chunkZ) {
|
if(x0 >> 4 != chunkX || z0 >> 4 != chunkZ) {
|
||||||
if(inChunk) {
|
if(inChunk) {
|
||||||
break;
|
break;
|
||||||
@ -219,13 +220,13 @@ public class ExplosionNukeRayBatched {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inChunk = true;
|
inChunk = true;
|
||||||
|
|
||||||
if(!world.isAirBlock(x0, y0, z0)) {
|
if(!world.isAirBlock(x0, y0, z0)) {
|
||||||
|
|
||||||
BlockPos pos = new BlockPos(x0, y0, z0);
|
BlockPos pos = new BlockPos(x0, y0, z0);
|
||||||
|
|
||||||
if(x0 == tipX && y0 == tipY && z0 == tipZ) {
|
if(x0 == tipX && y0 == tipY && z0 == tipZ) {
|
||||||
toRemTips.add(pos);
|
toRemTips.add(pos);
|
||||||
}
|
}
|
||||||
@ -241,20 +242,41 @@ public class ExplosionNukeRayBatched {
|
|||||||
world.setBlock(pos.getX(), pos.getY(), pos.getZ(), Blocks.air, 0, 2);
|
world.setBlock(pos.getX(), pos.getY(), pos.getZ(), Blocks.air, 0, 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
perChunk.remove(coord);
|
perChunk.remove(coord);
|
||||||
orderedChunks.remove(0);
|
orderedChunks.remove(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void handleTip(int x, int y, int z) {
|
protected void handleTip(int x, int y, int z) {
|
||||||
world.setBlock(x, y, z, Blocks.air, 0, 3);
|
world.setBlock(x, y, z, Blocks.air, 0, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isComplete() {
|
||||||
|
return isAusf3Complete && perChunk.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cacheChunksTick(int time) {
|
||||||
|
if (!isAusf3Complete) {
|
||||||
|
// time ignored here since collectTip() did not implement a time limit
|
||||||
|
collectTip(speed*10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void destructionTick(int time) {
|
||||||
|
if (!isAusf3Complete) return;
|
||||||
|
long start = System.currentTimeMillis();
|
||||||
|
while(!perChunk.isEmpty() && System.currentTimeMillis() < start + time)
|
||||||
|
processChunk();
|
||||||
|
}
|
||||||
|
|
||||||
public class FloatTriplet {
|
public class FloatTriplet {
|
||||||
public float xCoord;
|
public float xCoord;
|
||||||
public float yCoord;
|
public float yCoord;
|
||||||
public float zCoord;
|
public float zCoord;
|
||||||
|
|
||||||
public FloatTriplet(float x, float y, float z) {
|
public FloatTriplet(float x, float y, float z) {
|
||||||
xCoord = x;
|
xCoord = x;
|
||||||
yCoord = y;
|
yCoord = y;
|
||||||
|
|||||||
@ -0,0 +1,497 @@
|
|||||||
|
package com.hbm.explosion;
|
||||||
|
|
||||||
|
import com.hbm.interfaces.IExplosionRay;
|
||||||
|
import com.hbm.main.MainRegistry;
|
||||||
|
import net.minecraft.block.Block;
|
||||||
|
import net.minecraft.init.Blocks;
|
||||||
|
import net.minecraft.util.Vec3;
|
||||||
|
import net.minecraft.world.ChunkCoordIntPair;
|
||||||
|
import net.minecraft.world.EnumSkyBlock;
|
||||||
|
import net.minecraft.world.World;
|
||||||
|
import net.minecraft.world.chunk.Chunk;
|
||||||
|
import net.minecraft.world.chunk.storage.ExtendedBlockStorage;
|
||||||
|
import org.apache.logging.log4j.Level;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.*;
|
||||||
|
import java.util.concurrent.atomic.AtomicLongArray;
|
||||||
|
|
||||||
|
public class ExplosionNukeRayParallelized implements IExplosionRay {
|
||||||
|
|
||||||
|
private static final int WORLD_HEIGHT = 256;
|
||||||
|
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;
|
||||||
|
private final double explosionX, explosionY, explosionZ;
|
||||||
|
private final int originX, originY, originZ;
|
||||||
|
private final int strength;
|
||||||
|
private final int radius;
|
||||||
|
|
||||||
|
private final List<Vec3> directions;
|
||||||
|
private final ConcurrentMap<ChunkCoordIntPair, ConcurrentBitSet> destructionMap;
|
||||||
|
private final ConcurrentMap<ChunkKey, SubChunkSnapshot> snapshots;
|
||||||
|
|
||||||
|
private final BlockingQueue<RayTask> rayQueue;
|
||||||
|
private final BlockingQueue<ChunkKey> cacheQueue;
|
||||||
|
private final ExecutorService pool;
|
||||||
|
private final CountDownLatch latch;
|
||||||
|
private final Thread latchWatcherThread;
|
||||||
|
private final List<ChunkCoordIntPair> orderedChunks;
|
||||||
|
private volatile boolean collectFinished = false;
|
||||||
|
private volatile boolean destroyFinished = false;
|
||||||
|
|
||||||
|
|
||||||
|
public ExplosionNukeRayParallelized(World world, double x, double y, double z, int strength, int speed, int radius) {
|
||||||
|
this.world = world;
|
||||||
|
this.explosionX = x;
|
||||||
|
this.explosionY = y;
|
||||||
|
this.explosionZ = z;
|
||||||
|
|
||||||
|
this.originX = (int) Math.floor(x);
|
||||||
|
this.originY = (int) Math.floor(y);
|
||||||
|
this.originZ = (int) Math.floor(z);
|
||||||
|
|
||||||
|
this.strength = strength;
|
||||||
|
this.radius = radius;
|
||||||
|
|
||||||
|
int rayCount = Math.max(0, (int) (2.5 * Math.PI * strength * strength));
|
||||||
|
|
||||||
|
this.latch = new CountDownLatch(rayCount);
|
||||||
|
this.destructionMap = new ConcurrentHashMap<>();
|
||||||
|
this.snapshots = new ConcurrentHashMap<>();
|
||||||
|
this.orderedChunks = new ArrayList<>();
|
||||||
|
|
||||||
|
this.rayQueue = new LinkedBlockingQueue<>();
|
||||||
|
this.cacheQueue = new LinkedBlockingQueue<>();
|
||||||
|
|
||||||
|
int workers = Math.max(1, Runtime.getRuntime().availableProcessors() - 1);
|
||||||
|
this.pool = Executors.newWorkStealingPool(workers);
|
||||||
|
this.directions = generateSphereRays(rayCount);
|
||||||
|
|
||||||
|
for (int i = 0; i < rayCount; i++) rayQueue.add(new RayTask(i));
|
||||||
|
for (int i = 0; i < workers; i++) pool.submit(new Worker());
|
||||||
|
|
||||||
|
this.latchWatcherThread = new Thread(() -> {
|
||||||
|
try {
|
||||||
|
latch.await();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
} finally {
|
||||||
|
collectFinished = true;
|
||||||
|
}
|
||||||
|
}, "ExplosionNuke-LatchWatcher-" + System.nanoTime());
|
||||||
|
this.latchWatcherThread.setDaemon(true);
|
||||||
|
this.latchWatcherThread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static float getNukeResistance(Block b) {
|
||||||
|
// if (b.getMaterial().isLiquid()) return 0.1F;
|
||||||
|
if (b == Blocks.sandstone) return Blocks.stone.getExplosionResistance(null);
|
||||||
|
if (b == Blocks.obsidian) return Blocks.stone.getExplosionResistance(null) * 3;
|
||||||
|
return b.getExplosionResistance(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cacheChunksTick(int timeBudgetMs) {
|
||||||
|
if (collectFinished || this.cacheQueue == null) return;
|
||||||
|
|
||||||
|
final long deadline = System.nanoTime() + (timeBudgetMs * 1_000_000L);
|
||||||
|
while (System.nanoTime() < deadline) {
|
||||||
|
ChunkKey ck = cacheQueue.poll();
|
||||||
|
if (ck == null) break;
|
||||||
|
snapshots.computeIfAbsent(ck, key -> {
|
||||||
|
SubChunkSnapshot snap = createSubChunk(key.pos, key.subY);
|
||||||
|
return snap == null ? SubChunkSnapshot.EMPTY : snap;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void destructionTick(int timeBudgetMs) {
|
||||||
|
if (!collectFinished || destroyFinished) return;
|
||||||
|
final long deadline = System.nanoTime() + timeBudgetMs * 1_000_000L;
|
||||||
|
|
||||||
|
if (orderedChunks.isEmpty() && !destructionMap.isEmpty()) {
|
||||||
|
orderedChunks.addAll(destructionMap.keySet());
|
||||||
|
orderedChunks.sort(Comparator.comparingInt(c -> Math.abs((originX >> 4) - c.chunkXPos) + Math.abs((originZ >> 4) - c.chunkZPos)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterator<ChunkCoordIntPair> it = orderedChunks.iterator();
|
||||||
|
while (it.hasNext() && System.nanoTime() < deadline) {
|
||||||
|
ChunkCoordIntPair cp = it.next();
|
||||||
|
ConcurrentBitSet bs = destructionMap.get(cp);
|
||||||
|
if (bs == null) {
|
||||||
|
it.remove();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Chunk chunk = world.getChunkFromChunkCoords(cp.chunkXPos, cp.chunkZPos);
|
||||||
|
ExtendedBlockStorage[] storages = chunk.getBlockStorageArray();
|
||||||
|
boolean ChunkModified = false;
|
||||||
|
|
||||||
|
for (int subY = 0; subY < storages.length; subY++) {
|
||||||
|
ExtendedBlockStorage storage = storages[subY];
|
||||||
|
if (storage == null) continue;
|
||||||
|
|
||||||
|
int yPrimeMin = 255 - ((subY << 4) + 15);
|
||||||
|
int startBit = yPrimeMin << 8;
|
||||||
|
int yPrimeMax = 255 - (subY << 4);
|
||||||
|
int endBit = (yPrimeMax << 8) | 0xFF;
|
||||||
|
|
||||||
|
int bit = bs.nextSetBit(startBit);
|
||||||
|
if (bit < 0 || bit > endBit) continue;
|
||||||
|
|
||||||
|
while (bit >= 0 && bit <= endBit && System.nanoTime() < deadline) {
|
||||||
|
int yGlobal = 255 - (bit >>> 8);
|
||||||
|
int xGlobal = (cp.chunkXPos << 4) | ((bit >>> 4) & 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 yLocal = yGlobal & 0xF;
|
||||||
|
int zLocal = zGlobal & 0xF;
|
||||||
|
storage.func_150818_a(xLocal, yLocal, zLocal, Blocks.air);
|
||||||
|
storage.setExtBlockMetadata(xLocal, yLocal, zLocal, 0);
|
||||||
|
ChunkModified = true;
|
||||||
|
|
||||||
|
world.notifyBlocksOfNeighborChange(xGlobal, yGlobal, zGlobal, Blocks.air);
|
||||||
|
world.markBlockForUpdate(xGlobal, yGlobal, zGlobal);
|
||||||
|
|
||||||
|
world.updateLightByType(EnumSkyBlock.Sky, xGlobal, yGlobal, zGlobal);
|
||||||
|
world.updateLightByType(EnumSkyBlock.Block, xGlobal, yGlobal, zGlobal);
|
||||||
|
|
||||||
|
bs.clear(bit);
|
||||||
|
bit = bs.nextSetBit(bit + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ChunkModified) {
|
||||||
|
chunk.setChunkModified();
|
||||||
|
world.markBlockRangeForRenderUpdate(cp.chunkXPos << 4, 0, cp.chunkZPos << 4, (cp.chunkXPos << 4) | 15, WORLD_HEIGHT - 1, (cp.chunkZPos << 4) | 15);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bs.isEmpty()) {
|
||||||
|
destructionMap.remove(cp);
|
||||||
|
for (int sy = 0; sy < (WORLD_HEIGHT >> 4); sy++) {
|
||||||
|
snapshots.remove(new ChunkKey(cp.chunkXPos, cp.chunkZPos, sy));
|
||||||
|
}
|
||||||
|
it.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (orderedChunks.isEmpty() && destructionMap.isEmpty()) {
|
||||||
|
destroyFinished = true;
|
||||||
|
if (pool != null) pool.shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isComplete() {
|
||||||
|
return collectFinished && destroyFinished;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cancel() {
|
||||||
|
this.collectFinished = true;
|
||||||
|
this.destroyFinished = true;
|
||||||
|
|
||||||
|
if (this.rayQueue != null) this.rayQueue.clear();
|
||||||
|
if (this.cacheQueue != null) this.cacheQueue.clear();
|
||||||
|
|
||||||
|
if (this.latch != null) {
|
||||||
|
while (this.latch.getCount() > 0) {
|
||||||
|
this.latch.countDown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.latchWatcherThread != null && this.latchWatcherThread.isAlive()) {
|
||||||
|
this.latchWatcherThread.interrupt();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.pool != null && !this.pool.isShutdown()) {
|
||||||
|
this.pool.shutdownNow();
|
||||||
|
try {
|
||||||
|
if (!this.pool.awaitTermination(100, TimeUnit.MILLISECONDS)) {
|
||||||
|
MainRegistry.logger.log(Level.ERROR, "ExplosionNukeRayParallelized thread pool did not terminate promptly on cancel.");
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
if (!this.pool.isShutdown()) {
|
||||||
|
this.pool.shutdownNow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.destructionMap != null) this.destructionMap.clear();
|
||||||
|
if (this.snapshots != null) this.snapshots.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) {
|
||||||
|
List<Vec3> list = new ArrayList<>(count);
|
||||||
|
if (count <= 0) return list;
|
||||||
|
if (count == 1) {
|
||||||
|
list.add(Vec3.createVectorHelper(1, 0, 0).normalize());
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
double phi = Math.PI * (3.0 - Math.sqrt(5.0));
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
double y = 1.0 - (i / (double) (count - 1)) * 2.0;
|
||||||
|
double r = Math.sqrt(1.0 - y * y);
|
||||||
|
double t = phi * i;
|
||||||
|
list.add(Vec3.createVectorHelper(Math.cos(t) * r, y, Math.sin(t) * r));
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
words.set(wd, words.get(wd) & m);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 class Worker implements Runnable {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
while (true) {
|
||||||
|
if (collectFinished && rayQueue.isEmpty()) break;
|
||||||
|
RayTask task = rayQueue.poll(100, TimeUnit.MILLISECONDS);
|
||||||
|
if (task == null) {
|
||||||
|
if (collectFinished && rayQueue.isEmpty()) break;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
task.trace();
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class RayTask {
|
||||||
|
final int dirIndex;
|
||||||
|
double px, py, pz;
|
||||||
|
int x, y, z;
|
||||||
|
float energy;
|
||||||
|
double tMaxX, tMaxY, tMaxZ, tDeltaX, tDeltaY, tDeltaZ;
|
||||||
|
int stepX, stepY, stepZ;
|
||||||
|
boolean initialised = false;
|
||||||
|
|
||||||
|
RayTask(int dirIdx) {
|
||||||
|
this.dirIndex = dirIdx;
|
||||||
|
}
|
||||||
|
|
||||||
|
void init() {
|
||||||
|
Vec3 dir = directions.get(dirIndex);
|
||||||
|
px = explosionX;
|
||||||
|
py = explosionY;
|
||||||
|
pz = explosionZ;
|
||||||
|
x = originX;
|
||||||
|
y = originY;
|
||||||
|
z = originZ;
|
||||||
|
energy = strength;
|
||||||
|
|
||||||
|
|
||||||
|
final double EPS = 1e-6;
|
||||||
|
double ax = Math.abs(dir.xCoord);
|
||||||
|
stepX = ax < EPS ? 0 : (dir.xCoord > 0 ? 1 : -1);
|
||||||
|
double invDx = stepX == 0 ? Double.POSITIVE_INFINITY : 1.0 / ax;
|
||||||
|
double ay = Math.abs(dir.yCoord);
|
||||||
|
stepY = ay < EPS ? 0 : (dir.yCoord > 0 ? 1 : -1);
|
||||||
|
double invDy = stepY == 0 ? Double.POSITIVE_INFINITY : 1.0 / ay;
|
||||||
|
double az = Math.abs(dir.zCoord);
|
||||||
|
stepZ = az < EPS ? 0 : (dir.zCoord > 0 ? 1 : -1);
|
||||||
|
double invDz = stepZ == 0 ? Double.POSITIVE_INFINITY : 1.0 / az;
|
||||||
|
|
||||||
|
tDeltaX = invDx;
|
||||||
|
tDeltaY = invDy;
|
||||||
|
tDeltaZ = invDz;
|
||||||
|
tMaxX = stepX == 0 ? Double.POSITIVE_INFINITY : ((stepX > 0 ? (x + 1 - px) : (px - x)) * invDx);
|
||||||
|
tMaxY = stepY == 0 ? Double.POSITIVE_INFINITY : ((stepY > 0 ? (y + 1 - py) : (py - y)) * invDy);
|
||||||
|
tMaxZ = stepZ == 0 ? Double.POSITIVE_INFINITY : ((stepZ > 0 ? (z + 1 - pz) : (pz - z)) * invDz);
|
||||||
|
initialised = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void trace() {
|
||||||
|
if (!initialised) init();
|
||||||
|
if (energy <= 0) {
|
||||||
|
latch.countDown();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (energy > 0) {
|
||||||
|
if (y < 0 || y >= WORLD_HEIGHT) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
double dxBlock = x + 0.5 - explosionX;
|
||||||
|
double dyBlock = y + 0.5 - explosionY;
|
||||||
|
double dzBlock = z + 0.5 - explosionZ;
|
||||||
|
if (dxBlock * dxBlock + dyBlock * dyBlock + dzBlock * dzBlock > radius * radius) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ChunkKey ck = new ChunkKey(x >> 4, z >> 4, y >> 4);
|
||||||
|
SubChunkSnapshot snap = snapshots.get(ck);
|
||||||
|
|
||||||
|
if (snap == null) {
|
||||||
|
cacheQueue.offer(ck);
|
||||||
|
rayQueue.offer(this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (snap != SubChunkSnapshot.EMPTY) {
|
||||||
|
Block block = snap.getBlock(x & 15, y & 15, z & 15);
|
||||||
|
if (block != Blocks.air) {
|
||||||
|
float res = getNukeResistance(block);
|
||||||
|
float resistanceCutoff = 2_000_000F;
|
||||||
|
if (res >= resistanceCutoff) break;
|
||||||
|
double distToBlock = Math.sqrt(dxBlock * dxBlock + dyBlock * dyBlock + dzBlock * dzBlock);
|
||||||
|
double effectiveDist = Math.max(distToBlock, 0.01);
|
||||||
|
energy -= (float) (Math.pow(res + 1.0, 3.0 * (effectiveDist / radius)) - 1.0);
|
||||||
|
if (energy > 0) {
|
||||||
|
ConcurrentBitSet bs = destructionMap.computeIfAbsent(ck.pos, posKey -> new ConcurrentBitSet());
|
||||||
|
bs.set(((255 - y) << 8) | ((x & 15) << 4) | (z & 15));
|
||||||
|
} else break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tMaxX < tMaxY) {
|
||||||
|
if (tMaxX < tMaxZ) {
|
||||||
|
x += stepX;
|
||||||
|
tMaxX += tDeltaX;
|
||||||
|
} else {
|
||||||
|
z += stepZ;
|
||||||
|
tMaxZ += tDeltaZ;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (tMaxY < tMaxZ) {
|
||||||
|
y += stepY;
|
||||||
|
tMaxY += tDeltaY;
|
||||||
|
} else {
|
||||||
|
z += stepZ;
|
||||||
|
tMaxZ += tDeltaZ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
9
src/main/java/com/hbm/interfaces/IExplosionRay.java
Normal file
9
src/main/java/com/hbm/interfaces/IExplosionRay.java
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package com.hbm.interfaces;
|
||||||
|
|
||||||
|
public interface IExplosionRay {
|
||||||
|
boolean isComplete();
|
||||||
|
|
||||||
|
void cacheChunksTick(int processTime);
|
||||||
|
|
||||||
|
void destructionTick(int processTime);
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user