diff options
Diffstat (limited to 'src/main/java/ihl/explosion/ExplosionVectorBlockV2.java')
| -rw-r--r-- | src/main/java/ihl/explosion/ExplosionVectorBlockV2.java | 363 |
1 files changed, 363 insertions, 0 deletions
diff --git a/src/main/java/ihl/explosion/ExplosionVectorBlockV2.java b/src/main/java/ihl/explosion/ExplosionVectorBlockV2.java new file mode 100644 index 0000000..e00e19a --- /dev/null +++ b/src/main/java/ihl/explosion/ExplosionVectorBlockV2.java @@ -0,0 +1,363 @@ +package ihl.explosion;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Random;
+import java.util.Set;
+
+import ihl.IHLMod;
+import ihl.utils.IHLUtils;
+import net.minecraft.block.Block;
+import net.minecraft.entity.Entity;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+import net.minecraft.network.play.server.S26PacketMapChunkBulk;
+import net.minecraft.util.DamageSource;
+import net.minecraft.world.ChunkCoordIntPair;
+import net.minecraft.world.Explosion;
+import net.minecraft.world.World;
+import net.minecraft.world.chunk.Chunk;
+import net.minecraft.world.chunk.storage.ExtendedBlockStorage;
+import net.minecraftforge.common.MinecraftForge;
+import net.minecraftforge.event.world.ExplosionEvent;
+
+public class ExplosionVectorBlockV2 {
+ final Set<Integer> startVectors = new HashSet<Integer>();
+ public final int[][] directionMasks = new int[8][3];
+ public final int bits = IHLMod.config.explosionVectorSizeBits;
+ private final int maxValue = (1 << bits) - 1;
+ public final int halfValue = (1 << bits - 1) - 1;
+ private final int maxArraySize = 1 << bits * 3;
+// public final int[][] vectors = new int[maxArraySize][2];
+ public final int[][] vectors = new int[maxArraySize][0];
+ private final Set<Chunk> chunksToUpdate = new HashSet<Chunk>(64);
+ private final Map<Integer, ItemStack> cachedDrops = new HashMap<Integer, ItemStack>(128);
+ final Map<Integer, WorldSavedDataBlastWave> blastWaveByDimensionId = new HashMap<Integer, WorldSavedDataBlastWave>();
+ private final Map<ExtendedBlockStorage, Entity[]> cachedEntities = new HashMap<ExtendedBlockStorage, Entity[]>();
+ private final Random random = new Random();
+
+ public ExplosionVectorBlockV2() {
+ this.precalculateExplosion();
+ startVectors.add(0);
+ directionMasks[0] = new int[] { 1, 1, 1 };
+ directionMasks[1] = new int[] { -1, 1, 1 };
+ directionMasks[2] = new int[] { 1, -1, 1 };
+ directionMasks[3] = new int[] { 1, 1, -1 };
+ directionMasks[4] = new int[] { -1, -1, 1 };
+ directionMasks[5] = new int[] { 1, -1, -1 };
+ directionMasks[6] = new int[] { -1, 1, -1 };
+ directionMasks[7] = new int[] { -1, -1, -1 };
+ }
+
+ public int encodeXYZ(int x, int y, int z) {
+ return x << bits * 2 | y << bits | z;
+ }
+
+ public int[] decodeXYZ(int l) {
+ return new int[] { l >> bits * 2, l >> bits & maxValue, l & maxValue };
+ }
+
+ public void precalculateExplosion() {
+ for (int levelRadius = 1; levelRadius <= this.maxValue; levelRadius++)
+ for (int ix = 0; ix <= levelRadius; ix++)
+ for (int iy = 0; iy <= levelRadius; iy++) {
+ for (int iz = (ix == levelRadius || iy == levelRadius) ? 0 : levelRadius; iz <= levelRadius; iz++) {
+ int vxyz = encodeXYZ(ix, iy, iz);
+ int[] prevXYZ = new int[] { ix, iy, iz };
+ reduceCoordinate(prevXYZ, levelRadius);
+ int pvxyz = encodeXYZ(prevXYZ[0], prevXYZ[1], prevXYZ[2]);
+ findFreeSpace(pvxyz, vxyz);
+ }
+ }
+ }
+
+ private void findFreeSpace(int pvxyz, int vxyz) {
+ int[] nV = new int[vectors[pvxyz].length+1];
+ for(int i=0;i<vectors[pvxyz].length;i++){
+ nV[i]=vectors[pvxyz][i];
+ }
+ nV[vectors[pvxyz].length] = vxyz;
+ vectors[pvxyz] = nV;
+ }
+
+ private void reduceCoordinate(int[] pxyz, int levelRadius) {
+ float x = pxyz[0]+0.5f;
+ float y = pxyz[1]+0.5f;
+ float z = pxyz[2]+0.5f;
+ float rx = x/levelRadius;
+ float ry = y/levelRadius;
+ float rz = z/levelRadius;
+ while(x>=pxyz[0] && y>=pxyz[1] && z>=pxyz[2]){
+ x-=rx;
+ y-=ry;
+ z-=rz;
+ }
+ pxyz[0]=x<0?0:(int)x;
+ pxyz[1]=y<0?0:(int)y;
+ pxyz[2]=z<0?0:(int)z;
+ }
+
+ public void breakBlocksAndGetDescendants(World world, int sourceX, int sourceY, int sourceZ, Explosion explosion,
+ int ev, int power, int[] directionMask) {
+ power = this.getNewPowerAndProcessBlocks(world, ev, sourceX, sourceY, sourceZ, explosion, power, directionMask);
+ power = (int) (power * 0.94) - 1;
+ if (power > 1) {
+ if (this.vectors[ev].length==0) {
+ int[] xyz = decodeXYZ(ev);
+ int xb = xyz[0] >> bits - 1;
+ int yb = xyz[1] >> bits - 1;
+ int zb = xyz[2] >> bits - 1;
+ int hashb = xb << 2 | yb << 1 | zb;
+ xyz[0] -= xb * halfValue;
+ xyz[1] -= yb * halfValue;
+ xyz[2] -= zb * halfValue;
+ if (hashb == 0 || xb > 1 || yb > 1 || zb > 1) {
+ throw new ArithmeticException("End vectors shall be higher than half value");
+ }
+ int ev2 = encodeXYZ(xyz[0], xyz[1], xyz[2]);
+ breakBlocksAndGetDescendants(world, sourceX + xb * halfValue * directionMask[0],
+ sourceY + yb * halfValue * directionMask[1], sourceZ + zb * halfValue * directionMask[2],
+ explosion, ev2, power, directionMask);
+ } else {
+ for (int d1 : this.vectors[ev]) {
+ breakBlocksAndGetDescendants(world, sourceX, sourceY, sourceZ, explosion, d1, power,
+ directionMask);
+ }
+ }
+ }
+ }
+
+ private int getNewPowerAndProcessBlocks(World world, int ev, int sourceX, int sourceY, int sourceZ,
+ Explosion explosion, int power2, int[] directionMask) {
+ int power1 = power2;
+ int[] xyz = decodeXYZ(ev);
+ int absX = xyz[0] * directionMask[0] + sourceX;
+ int absY = xyz[1] * directionMask[1] + sourceY;
+ int absZ = xyz[2] * directionMask[2] + sourceZ;
+ if (absY < 0 || absY >= 256) {
+ return 0;
+ }
+ int absEBSX = absX >> 4;
+ int absEBSZ = absZ >> 4;
+ if (world.getChunkProvider().chunkExists(absEBSX, absEBSZ)) {
+ if (world.getChunkProvider().provideChunk(absEBSX, absEBSZ).getTopFilledSegment() + 24 >= absY) {
+ int remainingPower = this.tryDestroyBlock(world, absX, absY, absZ, sourceX, sourceY, sourceZ, power1,
+ explosion);
+ return remainingPower;
+ } else {
+ return 0;
+ }
+ } else {
+ WorldSavedDataBlastWave blastWave = null;
+ int dimensionId = world.provider.dimensionId;
+ if (this.blastWaveByDimensionId.containsKey(dimensionId)) {
+ blastWave = this.blastWaveByDimensionId.get(dimensionId);
+ } else {
+ blastWave = new WorldSavedDataBlastWave("blastWave");
+ this.blastWaveByDimensionId.put(dimensionId, blastWave);
+ }
+ long chunkXZKey = ChunkCoordIntPair.chunkXZ2Int(absEBSX, absEBSZ);
+ blastWave.scheduleExplosionEffectsOnChunkLoad(chunkXZKey, ev, sourceX, sourceY, sourceZ, power1,
+ directionMask);
+ return 0;
+ }
+ }
+
+ public int tryDestroyBlock(World world, int absX, int absY, int absZ, int sourceX, int sourceY, int sourceZ,
+ int power, Explosion explosion) {
+ Chunk chunk = world.getChunkProvider().provideChunk(absX >> 4, absZ >> 4);
+ ExtendedBlockStorage ebs = this.getEBS(chunk, absX, absY, absZ);
+ if (ebs == null) {
+ return power;
+ } else {
+ Block block = ebs.getBlockByExtId(absX & 15, absY & 15, absZ & 15);
+ if (block.getBlockHardness(world, absX, absY, absZ) < 0) {
+ return 0;
+ } else if (absX == sourceX && absY == sourceY && absZ == sourceZ) {
+ int array_index = (absY & 15) << 8 | (absZ & 15) << 4 | (absX & 15);
+ if (ebs.getBlockLSBArray()[array_index] != 0 && ebs.getBlockMSBArray() != null
+ && ebs.getBlockMSBArray().get(absX & 15, absY & 15, absZ & 15) != 0) {
+ ebs.blockRefCount--;
+ }
+ ebs.getBlockLSBArray()[array_index] = 0;
+ if (ebs.getBlockMSBArray() != null) {
+ ebs.getBlockMSBArray().set(absX & 15, absY & 15, absZ & 15, 0);
+ }
+ return power;
+ } else {
+ int remainingPower = power
+ - (int) (block.getExplosionResistance(null, world, absX, absY, absZ, sourceX, sourceY, sourceZ)
+ * 10f + 0.5f);
+ if (remainingPower >= 0) {
+ int array_index = (absY & 15) << 8 | (absZ & 15) << 4 | (absX & 15);
+ if (ebs.getBlockLSBArray()[array_index] != 0 && ebs.getBlockMSBArray() != null
+ && ebs.getBlockMSBArray().get(absX & 15, absY & 15, absZ & 15) != 0) {
+ ebs.blockRefCount--;
+ }
+ ebs.getBlockLSBArray()[array_index] = 0;
+ if (ebs.getBlockMSBArray() != null) {
+ ebs.getBlockMSBArray().set(absX & 15, absY & 15, absZ & 15, 0);
+ }
+ List<ItemStack> dropsList = block.getDrops(world, absX, absY, absZ,
+ ebs.getExtBlockMetadata(absX & 15, absY & 15, absZ & 15), 0);
+ Iterator<ItemStack> drops = dropsList.iterator();
+ while (drops.hasNext()) {
+ ItemStack drop = drops.next();
+ int key = Item.getIdFromItem(drop.getItem()) ^ (drop.getItemDamage() << 16);
+ if (this.cachedDrops.containsKey(key)) {
+ this.cachedDrops.get(key).stackSize += drop.stackSize;
+ } else {
+ this.cachedDrops.put(key, drop);
+ }
+ }
+ Entity[] entities = this.getFromOrCreateCache(world, ebs, absX, absY, absZ);
+ if (entities != null && entities[array_index] != null) {
+ entities[array_index].attackEntityFrom(DamageSource.setExplosionSource(explosion), power / 10f);
+ }
+ } else {
+ block.onNeighborBlockChange(world, absX, absY, absZ, block);
+ if (random.nextInt(8) == 0) {
+ if ((++absY & 15) != 0) {
+ int array_index = (absY & 15) << 8 | (absZ & 15) << 4 | (absX & 15);
+ if (ebs.getBlockLSBArray()[array_index] == 0 && (ebs.getBlockMSBArray() == null
+ || ebs.getBlockMSBArray().get(absX & 15, absY & 15, absZ & 15) == 0)) {
+ this.placeDrops(world, absX, absY, absZ);
+ }
+ }
+ }
+ }
+ return remainingPower;
+ }
+ }
+ }
+
+ public Entity[] getFromOrCreateCache(World world, ExtendedBlockStorage ebs, int absX, int absY, int absZ) {
+ Entity[] entities = this.cachedEntities.get(ebs);
+ if (entities == null) {
+ Chunk chunk = world.getChunkProvider().provideChunk(absX >> 4, absZ >> 4);
+ List<Entity> eList = this.getEntityList(chunk, absX, absY, absZ);
+ if (eList != null && !eList.isEmpty()) {
+ entities = new Entity[4096];
+ Iterator<Entity> eListI = eList.iterator();
+ this.cachedEntities.put(ebs, entities);
+ while (eListI.hasNext()) {
+ Entity entity = eListI.next();
+ int entityX = (int) entity.boundingBox.minX;
+ int entityY = (int) entity.boundingBox.minY;
+ int entityZ = (int) entity.boundingBox.minZ;
+ int rx = entityX & 15;
+ int ry = entityY & 15;
+ int rz = entityZ & 15;
+ int array_index = ry << 8 | rz << 4 | rx;
+ entities[array_index] = entity;
+ }
+ }
+ }
+ return entities;
+ }
+
+ public ExtendedBlockStorage getEBS(Chunk chunk, int absX, int absY, int absZ) {
+ ExtendedBlockStorage[] ebsA = chunk.getBlockStorageArray();
+ ExtendedBlockStorage ebs = ebsA[absY >> 4];
+ if (ebs != null) {
+ this.chunksToUpdate.add(chunk);
+ }
+ return ebs;
+ }
+
+ @SuppressWarnings("unchecked")
+ public List<Entity> getEntityList(Chunk chunk, int absX, int absY, int absZ) {
+ return chunk.entityLists[absY >> 4];
+ }
+
+ private void placeDrops(World world, int x, int y, int z) {
+ Iterator<Entry<Integer, ItemStack>> di = this.cachedDrops.entrySet().iterator();
+ if (di.hasNext()) {
+ Entry<Integer, ItemStack> cde = di.next();
+ while (di.hasNext()) {
+ cde = di.next();
+ }
+ ItemStack stack = cde.getValue();
+ if (stack != null && stack.getItem() != null && stack.stackSize > 0) {
+ if (stack.stackSize <= stack.getMaxStackSize()) {
+ if (stack.stackSize > 0) {
+ PileTileEntity pte = new PileTileEntity();
+ pte.xCoord = x;
+ pte.yCoord = y;
+ pte.zCoord = z;
+ pte.setWorldObj(world);
+ pte.validate();
+ pte.setContent(stack);
+ IHLUtils.setBlockAndTileEntityRaw(world, x, y, z, PileBlock.instance, pte);
+ }
+ di.remove();
+ } else {
+ ItemStack stack1 = stack.copy();
+ stack1.stackSize = stack.getMaxStackSize();
+ PileTileEntity pte = new PileTileEntity();
+ pte.content = stack1;
+ IHLUtils.setBlockAndTileEntityRaw(world, x, y, z, PileBlock.instance, pte);
+ stack.stackSize -= stack.getMaxStackSize();
+ }
+ }
+ }
+ }
+
+ public void sendChunkUpdateToPlayersInExplosionAffectedZone(World world, int sourceX, int sourceY, int sourceZ) {
+ Iterator<Chunk> ci = this.chunksToUpdate.iterator();
+ while (ci.hasNext()) {
+ Chunk chunk = ci.next();
+ chunk.generateSkylightMap();
+ Arrays.fill(chunk.updateSkylightColumns, true);
+ chunk.func_150804_b(false);
+ }
+ List<Chunk> chunks = new ArrayList<Chunk>();
+ chunks.addAll(this.chunksToUpdate);
+ for (Object player : world.playerEntities) {
+ if (player instanceof EntityPlayerMP) {
+ EntityPlayerMP playerMP = (EntityPlayerMP) player;
+ playerMP.playerNetServerHandler.sendPacket(new S26PacketMapChunkBulk(chunks));
+ }
+ }
+ this.chunksToUpdate.clear();
+ }
+
+ public void doExplosion(World world, int sourceX, int sourceY, int sourceZ, final Set<Integer> startVectors1,
+ int startPower, int[] directionMask, Explosion explosion) {
+ for (int sv : startVectors1) {
+ this.breakBlocksAndGetDescendants(world, sourceX - (directionMask[0] < 0 ? 1 : 0),
+ sourceY - (directionMask[1] < 0 ? 1 : 0), sourceZ - (directionMask[2] < 0 ? 1 : 0), explosion, sv,
+ startPower, directionMask);
+ }
+ // Free and clean resources
+ this.cachedEntities.clear();
+ }
+
+ public void doExplosion(World world, int sourceX, int sourceY, int sourceZ, final Set<Integer> startVectors1,
+ int startPower) {
+ IHLMod.log.info("Starting explosion server");
+ Explosion explosion = new Explosion(world, null, sourceX, sourceY, sourceZ, 100f);
+ if (!MinecraftForge.EVENT_BUS.post(new ExplosionEvent.Start(world, explosion))) {
+ for (int[] directionMask : directionMasks) {
+ this.doExplosion(world, sourceX, sourceY, sourceZ, startVectors1, startPower, directionMask, explosion);
+ }
+ sendChunkUpdateToPlayersInExplosionAffectedZone(world, sourceX, sourceY, sourceZ);
+ for (Entry<Integer, ItemStack> entry : this.cachedDrops.entrySet()) {
+ IHLEntityFallingPile fallingPile = new IHLEntityFallingPile(world);
+ fallingPile.setPosition(sourceX + 0.5d, sourceY + 0.5d, sourceZ + 0.5d);
+ fallingPile.setEntityItemStack(entry.getValue());
+ fallingPile.setVelocity(random.nextDouble() - 0.5d, random.nextDouble() * 2,
+ random.nextDouble() - 0.5d);
+ world.spawnEntityInWorld(fallingPile);
+ }
+ this.cachedDrops.clear();
+ }
+ }
+}
\ No newline at end of file |
