From c94c29874f408db7dc919b9d8363c1f1c59e2e9c Mon Sep 17 00:00:00 2001 From: Boblet Date: Fri, 3 Mar 2023 15:03:02 +0100 Subject: [PATCH] some more tests --- .../com/hbm/explosion/ExplosionNukeSmall.java | 61 ---- .../java/com/hbm/items/tool/ItemWandD.java | 13 +- src/main/java/com/hbm/main/MainRegistry.java | 3 + .../java/com/hbm/test/ExplosionTests.java | 71 +++++ .../java/com/hbm/test/ExplosionWorld.java | 28 ++ src/main/java/com/hbm/test/MK5Frame.java | 295 ++++++++++++++++++ src/main/java/com/hbm/util/TimeAnalyzer.java | 8 +- 7 files changed, 415 insertions(+), 64 deletions(-) create mode 100644 src/main/java/com/hbm/test/ExplosionTests.java create mode 100644 src/main/java/com/hbm/test/ExplosionWorld.java create mode 100644 src/main/java/com/hbm/test/MK5Frame.java diff --git a/src/main/java/com/hbm/explosion/ExplosionNukeSmall.java b/src/main/java/com/hbm/explosion/ExplosionNukeSmall.java index 32d3b0f79..88dbdacaf 100644 --- a/src/main/java/com/hbm/explosion/ExplosionNukeSmall.java +++ b/src/main/java/com/hbm/explosion/ExplosionNukeSmall.java @@ -13,67 +13,6 @@ import net.minecraft.world.World; public class ExplosionNukeSmall { - /*@Deprecated public static final int safe = 0; - @Deprecated public static final int tots = 1; - @Deprecated public static final int low = 2; - @Deprecated public static final int medium = 3; - @Deprecated public static final int high = 4; - - @Deprecated - public static void explode(World world, double posX, double posY, double posZ, int size) { - - - //all sizes have the same animation except tiny tots - NBTTagCompound data = new NBTTagCompound(); - if(size == tots) - data.setString("type", "tinytot"); - else - data.setString("type", "muke"); - if(MainRegistry.polaroidID == 11 || world.rand.nextInt(100) == 0) - data.setBoolean("balefire", true); - PacketDispatcher.wrapper.sendToAllAround(new AuxParticlePacketNT(data, posX, posY + 0.5, posZ), new TargetPoint(world.provider.dimensionId, posX, posY, posZ, 250)); - world.playSoundEffect(posX, posY, posZ, "hbm:weapon.mukeExplosion", 15.0F, 1.0F); - - //no shrapnels for large mukes and tinty tots - if(size != high && size != tots) - ExplosionLarge.spawnShrapnels(world, posX, posY, posZ, 25); - - if(size == safe) { - ExplosionNukeGeneric.dealDamage(world, posX, posY, posZ, 45); - - } else if(size > safe && size < high) { - - switch(size) { - case 1: new ExplosionNT(world, null, posX, posY, posZ, 10F).addAllAttrib(ExplosionNT.nukeAttribs).overrideResolution(32).explode(); - ExplosionNukeGeneric.dealDamage(world, posX, posY, posZ, 30); break; - - case 2: new ExplosionNT(world, null, posX, posY, posZ, 15F).addAllAttrib(ExplosionNT.nukeAttribs).overrideResolution(64).explode(); - ExplosionNukeGeneric.dealDamage(world, posX, posY, posZ, 45); break; - - case 3: new ExplosionNT(world, null, posX, posY, posZ, 20F).addAllAttrib(ExplosionNT.nukeAttribs).overrideResolution(64).explode(); - ExplosionNukeGeneric.dealDamage(world, posX, posY, posZ, 55); break; - } - - } else if(size == high) { - world.spawnEntityInWorld(EntityNukeExplosionMK5.statFac(world, BombConfig.fatmanRadius, posX, posY, posZ).mute()); - } - - //radiation is 50 RAD/s in the epicenter, times the radMod - float radMod = size * 0.33F; - - //radMod for safe nukes is the same as for low yield - if(size == safe) - radMod = 0.66F; - - for(int i = -2; i <= 2; i++) { - for(int j = -2; j <= 2; j++) { - if(i + j < 4) { - ChunkRadiationManager.proxy.incrementRad(world, (int) Math.floor(posX + i * 16), (int) Math.floor(posY), (int) Math.floor(posZ + j * 16), 50 / (Math.abs(i) + Math.abs(j) + 1) * radMod); - } - } - } - }*/ - public static void explode(World world, double posX, double posY, double posZ, MukeParams params) { // spawn particles, if present diff --git a/src/main/java/com/hbm/items/tool/ItemWandD.java b/src/main/java/com/hbm/items/tool/ItemWandD.java index 867c68fc1..a2ab0efec 100644 --- a/src/main/java/com/hbm/items/tool/ItemWandD.java +++ b/src/main/java/com/hbm/items/tool/ItemWandD.java @@ -4,8 +4,10 @@ import java.util.List; import com.hbm.lib.Library; import com.hbm.saveddata.TomSaveData; +import com.hbm.util.TimeAnalyzer; import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.Blocks; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.util.MovingObjectPosition; @@ -23,11 +25,18 @@ public class ItemWandD extends Item { if(pos != null) { - TomSaveData data = TomSaveData.forWorld(world); + TimeAnalyzer.startCount("setBlock"); + world.setBlock(pos.blockX, pos.blockY, pos.blockZ, Blocks.dirt); + TimeAnalyzer.startEndCount("getBlock"); + world.getBlock(pos.blockX, pos.blockY, pos.blockZ); + TimeAnalyzer.endCount(); + TimeAnalyzer.dump(); + + /*TomSaveData data = TomSaveData.forWorld(world); data.impact = false; data.fire = 0F; data.dust = 0F; - data.markDirty(); + data.markDirty();*/ /*EntityTomBlast tom = new EntityTomBlast(world); tom.posX = pos.blockX; diff --git a/src/main/java/com/hbm/main/MainRegistry.java b/src/main/java/com/hbm/main/MainRegistry.java index fd4447e08..fa94ce65a 100644 --- a/src/main/java/com/hbm/main/MainRegistry.java +++ b/src/main/java/com/hbm/main/MainRegistry.java @@ -67,6 +67,7 @@ import com.hbm.lib.RefStrings; import com.hbm.packet.PacketDispatcher; import com.hbm.potion.HbmPotion; import com.hbm.saveddata.satellites.Satellite; +import com.hbm.test.ExplosionTests; import com.hbm.tileentity.TileMappings; import com.hbm.tileentity.bomb.TileEntityNukeCustom; import com.hbm.tileentity.machine.*; @@ -819,6 +820,8 @@ public class MainRegistry { Compat.handleRailcraftNonsense(); SuicideThreadDump.register(); + + //ExplosionTests.runTest(); } @EventHandler diff --git a/src/main/java/com/hbm/test/ExplosionTests.java b/src/main/java/com/hbm/test/ExplosionTests.java new file mode 100644 index 000000000..544a61157 --- /dev/null +++ b/src/main/java/com/hbm/test/ExplosionTests.java @@ -0,0 +1,71 @@ +package com.hbm.test; + +import com.hbm.test.MK5Frame.*; +import com.hbm.util.TimeAnalyzer; + +import cpw.mods.fml.common.FMLCommonHandler; + +public class ExplosionTests { + + private static ExplosionWorld world = new ExplosionWorld(); + public static double BUFFER_THRESHOLD = 0.25D; + + public static void runTest() { + + int standardSpeed = (int)Math.ceil(100000 / 300); + + double[] thresholds = new double[] {0.25, 0.5}; + int[] radii = new int[] {100, 250}; + + int x = 200; + int y = 70; + int z = 200; + long mem = 0; + + for(int radius : radii) { + + int strength = radius * 2; + int length = radius; + + System.gc(); + mem = getMem(); + System.out.println("#### STARTING TEST WITH NO PROXIMITY BUFFER " + radius + " ####"); + MK5Frame noBuf = new MK5Frame(world, x, y, z, strength, length).setBuffer(new BufferNone()); + while(!noBuf.isCollectionComplete) noBuf.collectTip(standardSpeed * 10); + while(noBuf.perChunk.size() > 0) noBuf.processChunk(); + TimeAnalyzer.endCount(); + TimeAnalyzer.dump(); + System.out.println("Mem diff: " + ((getMem() - mem) / 1_048_576) + "MB"); + + for(double threshold : thresholds) { + BUFFER_THRESHOLD = threshold; + + System.gc(); + mem = getMem(); + System.out.println("#### STARTING TEST WITH MAP-BASED PROXIMITY BUFFER " + radius + " / " + threshold + " ####"); + MK5Frame mapBuf = new MK5Frame(world, x, y, z, strength, length).setBuffer(new BufferMap()); + while(!mapBuf.isCollectionComplete) mapBuf.collectTip(standardSpeed * 10); + while(mapBuf.perChunk.size() > 0) mapBuf.processChunk(); + TimeAnalyzer.endCount(); + TimeAnalyzer.dump(); + System.out.println("Mem diff: " + ((getMem() - mem) / 1_048_576) + "MB"); + + System.gc(); + mem = getMem(); + System.out.println("#### STARTING TEST WITH ARRAY PROXIMITY BUFFER " + radius + " / " + threshold + " ####"); + MK5Frame arrayBuf = new MK5Frame(world, x, y, z, strength, length).setBuffer(new BufferArray(x, y, z, (int) (length))); + while(!arrayBuf.isCollectionComplete) arrayBuf.collectTip(standardSpeed * 10); + while(arrayBuf.perChunk.size() > 0) arrayBuf.processChunk(); + TimeAnalyzer.endCount(); + TimeAnalyzer.dump(); + System.out.println("Mem diff: " + ((getMem() - mem) / 1_048_576) + "MB"); + } + } + + FMLCommonHandler.instance().exitJava(0, true); + } + + public static long getMem() { + return Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory(); + } +} diff --git a/src/main/java/com/hbm/test/ExplosionWorld.java b/src/main/java/com/hbm/test/ExplosionWorld.java new file mode 100644 index 000000000..469090d9b --- /dev/null +++ b/src/main/java/com/hbm/test/ExplosionWorld.java @@ -0,0 +1,28 @@ +package com.hbm.test; + +import net.minecraft.block.Block; +import net.minecraft.init.Blocks; + +public class ExplosionWorld { + + //public Block[][][] blocks = new Block[500][256][500]; + + public void setBlock(int x, int y, int z, Block block) { + long nanos = System.nanoTime(); + while(System.nanoTime() < nanos + 30_000); + } //NOP + + public Block getBlock(int x, int y, int z) { + long nanos = System.nanoTime(); + while(System.nanoTime() < nanos + 1_000); + if(y == 0) return Blocks.bedrock; + if(y < 50) return Blocks.stone; + if(y < 64) return Blocks.dirt; + + return Blocks.air; + } + + public boolean isAirBlock(int x, int y, int z) { + return getBlock(x, y, z) == Blocks.air; + } +} diff --git a/src/main/java/com/hbm/test/MK5Frame.java b/src/main/java/com/hbm/test/MK5Frame.java new file mode 100644 index 000000000..a20fd01aa --- /dev/null +++ b/src/main/java/com/hbm/test/MK5Frame.java @@ -0,0 +1,295 @@ +package com.hbm.test; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; + +import com.hbm.util.TimeAnalyzer; +import com.hbm.util.fauxpointtwelve.BlockPos; + +import net.minecraft.block.Block; +import net.minecraft.init.Blocks; +import net.minecraft.util.Vec3; +import net.minecraft.world.ChunkCoordIntPair; + +public class MK5Frame { + + public HashMap> perChunk = new HashMap(); //for future: optimize blockmap further by using sub-chunks instead of chunks + public List orderedChunks = new ArrayList(); + private CoordComparator comparator = new CoordComparator(); + int posX; + int posY; + int posZ; + ExplosionWorld world; + + int strength; + int length; + + int gspNumMax; + int gspNum; + double gspX; + double gspY; + + public boolean isCollectionComplete = false; + + public MK5Frame(ExplosionWorld world, int x, int y, int z, int strength, int length) { + this.world = world; + this.posX = x; + this.posY = y; + this.posZ = z; + this.strength = strength; + this.length = length; + + // Total number of points + this.gspNumMax = (int)(2.5 * Math.PI * Math.pow(this.strength,2)); + this.gspNum = 1; + + // The beginning of the generalized spiral points + this.gspX = Math.PI; + this.gspY = 0.0; + } + + private void generateGspUp(){ + + if (this.gspNum < this.gspNumMax) { + int k = this.gspNum + 1; + double hk = -1.0 + 2.0 * (k - 1.0) / (this.gspNumMax - 1.0); + this.gspX = Math.acos(hk); + + double prev_lon = this.gspY; + double lon = prev_lon + 3.6 / Math.sqrt(this.gspNumMax) / Math.sqrt(1.0 - hk * hk); + this.gspY = lon % (Math.PI * 2); + } else { + this.gspX = 0.0; + this.gspY = 0.0; + } + this.gspNum++; + } + + // Get Cartesian coordinates for spherical coordinates + private Vec3 getSpherical2cartesian(){ + double dx = Math.sin(this.gspX) * Math.cos(this.gspY); + double dz = Math.sin(this.gspX) * Math.sin(this.gspY); + double dy = Math.cos(this.gspX); + return Vec3.createVectorHelper(dx, dy, dz); + } + + public void collectTip(int count) { + + TimeAnalyzer.startCount("collect"); + + int amountProcessed = 0; + + while (this.gspNumMax >= this.gspNum){ + // Get Cartesian coordinates for spherical coordinates + Vec3 vec = this.getSpherical2cartesian(); + + int length = (int)Math.ceil(strength); + float res = strength; + + FloatTriplet lastPos = null; + HashSet chunkCoords = new HashSet(); + + for(int i = 0; i < length; i ++) { + + if(i > this.length) + break; + + float x0 = (float) (posX + (vec.xCoord * i)); + float y0 = (float) (posY + (vec.yCoord * i)); + float z0 = (float) (posZ + (vec.zCoord * i)); + + int iX = (int) Math.floor(x0); + int iY = (int) Math.floor(y0); + int iZ = (int) Math.floor(z0); + + double fac = 100 - ((double) i) / ((double) length) * 100; + fac *= 0.07D; + + Block block = null; + boolean withinThreshold = (double) i / (double) length <= ExplosionTests.BUFFER_THRESHOLD; + + Float buffered = withinThreshold ? buffer.getBufferedResult(iX, iY, iZ) : null; + + float f = 0; + + if(buffered == null) { + + block = world.getBlock(iX, iY, iZ); + + if(!block.getMaterial().isLiquid()) { + f = (float) Math.pow(block.getExplosionResistance(null), 7.5D - fac); + } + + if(withinThreshold) buffer.setBufferedResult(iX, iY, iZ, f); + + } else { + f = buffered; + } + + res -= f; + + if(res > 0 && block != Blocks.air && buffered == null) { // if we already have a buffered result we don't need to move the tip forward since that block is already affected + lastPos = new FloatTriplet(x0, y0, z0); + //all-air chunks don't need to be buffered at all + ChunkCoordIntPair chunkPos = new ChunkCoordIntPair(iX >> 4, iZ >> 4); + chunkCoords.add(chunkPos); + } + + if(res <= 0 || i + 1 >= this.length) { + break; + } + } + + for(ChunkCoordIntPair pos : chunkCoords) { + List triplets = perChunk.get(pos); + + if(triplets == null) { + triplets = new ArrayList(); + perChunk.put(pos, triplets); //we re-use the same pos instead of using individualized per-chunk ones to save on RAM + } + + triplets.add(lastPos); + } + + // Raise one generalized spiral points + this.generateGspUp(); + + amountProcessed++; + if(amountProcessed >= count) { + TimeAnalyzer.endCount(); + return; + } + } + + orderedChunks.addAll(perChunk.keySet()); + orderedChunks.sort(comparator); + + isCollectionComplete = true; + TimeAnalyzer.endCount(); + } + + /* TEST INSERT START */ + private ResultBuffer buffer; + public MK5Frame setBuffer(ResultBuffer buffer) { + this.buffer = buffer; + return this; + } + public static interface ResultBuffer { + Float getBufferedResult(int x, int y, int z); + void setBufferedResult(int x, int y, int z, float f); + } + public static class BufferNone implements ResultBuffer { + @Override public Float getBufferedResult(int x, int y, int z) { return null; } + @Override public void setBufferedResult(int x, int y, int z, float f) { } + } + public static class BufferMap implements ResultBuffer { + HashMap map = new HashMap(); + @Override public Float getBufferedResult(int x, int y, int z) { if(y < 0 || y > 255) return null; return map.get(new BlockPos(x, y, z)); } + @Override public void setBufferedResult(int x, int y, int z, float f) { if(y < 0 || y > 255) return; map.put(new BlockPos(x, y, z), f); } + } + public static class BufferArray implements ResultBuffer { + BlockPos center; Float[][][] buffer; int size; public BufferArray(int x, int y, int z, int size) { this.size = (int) (size * 2.1); center = new BlockPos(x, y, z); buffer = new Float[this.size][256][this.size];} + HashMap map = new HashMap(); + @Override public Float getBufferedResult(int x, int y, int z) { + if(y < 0 || y > 255) return null; + int iX = x - center.getX() + size * 100; int iZ = z - center.getZ() + size * 100; + return buffer[iX % size][y][iZ % size]; + } + @Override public void setBufferedResult(int x, int y, int z, float f) { + if(y < 0 || y > 255) return; + int iX = x - center.getX() + size * 100; int iZ = z - center.getZ() + size * 100; + buffer[iX % size][y][iZ % size] = f; + } + } + /* TEST INSERT END */ + + /** little comparator for roughly sorting chunks by distance to the center */ + public class CoordComparator implements Comparator { + + @Override + public int compare(ChunkCoordIntPair o1, ChunkCoordIntPair o2) { + + int chunkX = MK5Frame.this.posX >> 4; + int chunkZ = MK5Frame.this.posZ >> 4; + + int diff1 = Math.abs((chunkX - o1.chunkXPos)) + Math.abs((chunkZ - o1.chunkZPos)); + int diff2 = Math.abs((chunkX - o2.chunkXPos)) + Math.abs((chunkZ - o2.chunkZPos)); + + return diff1 > diff2 ? 1 : diff1 < diff2 ? -1 : 0; + } + } + + public void processChunk() { + + TimeAnalyzer.startCount("processChunk"); + if(this.perChunk.isEmpty()) { + TimeAnalyzer.endCount(); + return; + } + + ChunkCoordIntPair coord = orderedChunks.get(0); + List list = perChunk.get(coord); + HashSet toRem = new HashSet(); + int chunkX = coord.chunkXPos; + int chunkZ = coord.chunkZPos; + + int enter = (int) (Math.min( + Math.abs(posX - (chunkX << 4)), + Math.abs(posZ - (chunkZ << 4)))) - 16; //jump ahead to cut back on NOPs + + for(FloatTriplet triplet : list) { + float x = triplet.xCoord; + float y = triplet.yCoord; + float z = triplet.zCoord; + Vec3 vec = Vec3.createVectorHelper(x - this.posX, y - this.posY, z - this.posZ); + double pX = vec.xCoord / vec.lengthVector(); + double pY = vec.yCoord / vec.lengthVector(); + double pZ = vec.zCoord / vec.lengthVector(); + + boolean inChunk = false; + for(int i = enter; i < vec.lengthVector(); i++) { + int x0 = (int) Math.floor(posX + pX * i); + int y0 = (int) Math.floor(posY + pY * i); + int z0 = (int) Math.floor(posZ + pZ * i); + + if(x0 >> 4 != chunkX || z0 >> 4 != chunkZ) { + if(inChunk) { + break; + } else { + continue; + } + } + + inChunk = true; + + if(!world.isAirBlock(x0, y0, z0)) { + toRem.add(new BlockPos(x0, y0, z0)); + } + } + } + + for(BlockPos pos : toRem) { + world.setBlock(pos.getX(), pos.getY(), pos.getZ(), Blocks.air); + } + + perChunk.remove(coord); + orderedChunks.remove(0); + + TimeAnalyzer.endCount(); + } + + public class FloatTriplet { + public float xCoord; + public float yCoord; + public float zCoord; + + public FloatTriplet(float x, float y, float z) { + xCoord = x; + yCoord = y; + zCoord = z; + } + } +} diff --git a/src/main/java/com/hbm/util/TimeAnalyzer.java b/src/main/java/com/hbm/util/TimeAnalyzer.java index 856f80c8c..6cb4f53ec 100644 --- a/src/main/java/com/hbm/util/TimeAnalyzer.java +++ b/src/main/java/com/hbm/util/TimeAnalyzer.java @@ -40,9 +40,15 @@ public class TimeAnalyzer { milliTime.put(delta.getKey(), total); } + long total = 0; + for(Entry entry : milliTime.entrySet()) { - System.out.println(entry.getKey() + ": " + entry.getValue() + "ns"); + total += entry.getValue(); + String time = String.format("%,d", entry.getValue()); + System.out.println(entry.getKey() + ": " + time + "ns"); } + + System.out.println("Total time passed: " + String.format("%,d", total) + "ns (" + (total / 1_000_000_000) + "s)"); currentSection = ""; sectionStartTime = 0;