From 1f28a7c9f52a619cb211cc353758d9d7b215aa19 Mon Sep 17 00:00:00 2001 From: MartinTheDragon Date: Wed, 9 Mar 2022 16:56:53 +0100 Subject: [PATCH 1/3] Backport the faster fallout algorithm --- src/main/java/com/hbm/config/BombConfig.java | 8 +- .../hbm/entity/effect/EntityFalloutRain.java | 143 ++++++++++-------- 2 files changed, 85 insertions(+), 66 deletions(-) diff --git a/src/main/java/com/hbm/config/BombConfig.java b/src/main/java/com/hbm/config/BombConfig.java index efc6befa0..68443e8e9 100644 --- a/src/main/java/com/hbm/config/BombConfig.java +++ b/src/main/java/com/hbm/config/BombConfig.java @@ -80,17 +80,13 @@ public class BombConfig { Property propBlastSpeed = config.get(CATEGORY_NUKE, "6.01_blastSpeed", 1024); propBlastSpeed.comment = "Base speed of MK3 system (old and schrabidium) detonations (Blocks / tick)"; blastSpeed = propBlastSpeed.getInt(); - // fallout range + // new explosion speed Property propFalloutRange = config.get(CATEGORY_NUKE, "6.02_blastSpeedNew", 1024); propFalloutRange.comment = "Base speed of MK4 system (new) detonations (Blocks / tick)"; mk4 = propFalloutRange.getInt(); - // fallout speed + // fallout range Property falloutRangeProp = config.get(CATEGORY_NUKE, "6.03_falloutRange", 100); falloutRangeProp.comment = "Radius of fallout area (base radius * value in percent)"; falloutRange = falloutRangeProp.getInt(); - // new explosion speed - Property falloutSpeed = config.get(CATEGORY_NUKE, "6.04_falloutSpeed", 256); - falloutSpeed.comment = "Blocks processed per tick by the fallout rain"; - fSpeed = falloutSpeed.getInt(); } } diff --git a/src/main/java/com/hbm/entity/effect/EntityFalloutRain.java b/src/main/java/com/hbm/entity/effect/EntityFalloutRain.java index ef7f89280..9e8330252 100644 --- a/src/main/java/com/hbm/entity/effect/EntityFalloutRain.java +++ b/src/main/java/com/hbm/entity/effect/EntityFalloutRain.java @@ -1,24 +1,23 @@ package com.hbm.entity.effect; import com.hbm.blocks.ModBlocks; -import com.hbm.config.BombConfig; import com.hbm.config.RadiationConfig; import com.hbm.config.VersatileConfig; import com.hbm.saveddata.AuxSavedData; - import net.minecraft.block.Block; import net.minecraft.block.material.Material; import net.minecraft.entity.Entity; import net.minecraft.init.Blocks; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.Vec3; +import net.minecraft.world.ChunkCoordIntPair; import net.minecraft.world.World; import net.minecraftforge.common.util.ForgeDirection; +import java.util.*; + public class EntityFalloutRain extends Entity { - - public int revProgress; - public int radProgress; + private boolean firstTick = true; // Of course Vanilla has it private in Entity... public EntityFalloutRain(World p_i1582_1_) { super(p_i1582_1_); @@ -35,47 +34,28 @@ public class EntityFalloutRain extends Entity { @Override public void onUpdate() { - if(!worldObj.isRemote) { - - for(int i = 0; i < BombConfig.fSpeed; i++) { - - Vec3 vec = Vec3.createVectorHelper(radProgress * 0.5, 0, 0); - double circum = radProgress * 2 * Math.PI * 2; - - /// - if(circum == 0) - circum = 1; - /// - - double part = 360D / circum; - - vec.rotateAroundY((float) (part * revProgress)); - - int x = (int) (posX + vec.xCoord); - int z = (int) (posZ + vec.zCoord); - - //int y = worldObj.getHeightValue(x, z) - 1; - - //if(worldObj.getBlock(x, y, z) == Blocks.grass) - // worldObj.setBlock(x, y, z, ModBlocks.waste_earth); - - double dist = radProgress * 100 / getScale() * 0.5; - - stomp(x, z, dist); - - revProgress++; - - if(revProgress > circum) { - revProgress = 0; - radProgress++; - } - - if(radProgress > getScale() * 2D) { - this.setDead(); - } - } - + if (firstTick) { + if (chunksToProcess.isEmpty() && outerChunksToProcess.isEmpty()) gatherChunks(); + firstTick = false; + } + + if (!chunksToProcess.isEmpty()) { + long chunkPos = chunksToProcess.remove(chunksToProcess.size() - 1); // Just so it doesn't shift the whole list every time + int chunkPosX = (int) (chunkPos & Integer.MAX_VALUE); + int chunkPosZ = (int) (chunkPos >> 32 & Integer.MAX_VALUE); + for (int x = chunkPosX << 4; x <= (chunkPosX << 4) + 16; x++) for (int z = chunkPosZ << 4; z <= (chunkPosZ << 4) + 16; z++) + stomp(x, z, Math.hypot(x - posX, z - posZ) * 100 / getScale()); + } else if (!outerChunksToProcess.isEmpty()) { + long chunkPos = outerChunksToProcess.remove(outerChunksToProcess.size() - 1); + int chunkPosX = (int) (chunkPos & Integer.MAX_VALUE); + int chunkPosZ = (int) (chunkPos >> 32 & Integer.MAX_VALUE); + for (int x = chunkPosX << 4; x <= (chunkPosX << 4) + 16; x++) for (int z = chunkPosZ << 4; z <= (chunkPosZ << 4) + 16; z++) { + double distance = Math.hypot(x - posX, z - posZ); + if (distance <= getScale()) stomp(x, z, distance * 100 / getScale()); + } + } else setDead(); + if(this.isDead) { if(RadiationConfig.rain > 0 && getScale() > 150) { worldObj.getWorldInfo().setRaining(true); @@ -87,6 +67,32 @@ public class EntityFalloutRain extends Entity { } } } + + private final List chunksToProcess = new ArrayList<>(); + private final List outerChunksToProcess = new ArrayList<>(); + + // Is it worth the effort to split this into a method that can be called over multiple ticks? I'd say it's fast enough anyway... + private void gatherChunks() { + Set chunks = new LinkedHashSet<>(); // LinkedHashSet preserves insertion order + Set outerChunks = new LinkedHashSet<>(); + int outerRange = getScale(); + for (int angle = 0; angle <= 720; angle++) { // TODO Make this adjust to the fallout's scale + Vec3 vector = Vec3.createVectorHelper(outerRange, 0, 0); + vector.rotateAroundY((float) (angle / 180.0 * Math.PI)); // Ugh, mutable data classes (also, ugh, radians; it uses degrees in 1.18; took me two hours to debug) + outerChunks.add(ChunkCoordIntPair.chunkXZ2Int((int) (posX + vector.xCoord) >> 4, (int) (posZ + vector.zCoord) >> 4)); + } + for (int distance = 0; distance <= outerRange; distance += 8) for (int angle = 0; angle <= 720; angle++) { + Vec3 vector = Vec3.createVectorHelper(distance, 0, 0); + vector.rotateAroundY((float) (angle / 180.0 * Math.PI)); + long chunkCoord = ChunkCoordIntPair.chunkXZ2Int((int) (posX + vector.xCoord) >> 4, (int) (posZ + vector.zCoord) >> 4); + if (!outerChunks.contains(chunkCoord)) chunks.add(chunkCoord); + } + + chunksToProcess.addAll(chunks); + outerChunksToProcess.addAll(outerChunks); + Collections.reverse(chunksToProcess); // So it starts nicely from the middle + Collections.reverse(outerChunksToProcess); + } private void stomp(int x, int z, double dist) { @@ -103,7 +109,7 @@ public class EntityFalloutRain extends Entity { if(b != ModBlocks.fallout && (ab == Blocks.air || (ab.isReplaceable(worldObj, x, y + 1, z) && !ab.getMaterial().isLiquid()))) { - double d = (double) radProgress / (double) getScale() * 0.5; + double d = dist / 100; double chance = 0.05 - Math.pow((d - 0.6) * 0.5, 2); @@ -219,33 +225,50 @@ public class EntityFalloutRain extends Entity { @Override protected void entityInit() { - this.dataWatcher.addObject(16, Integer.valueOf(0)); + this.dataWatcher.addObject(16, 0); } @Override - protected void readEntityFromNBT(NBTTagCompound p_70037_1_) { - setScale(p_70037_1_.getInteger("scale")); - revProgress = p_70037_1_.getInteger("revProgress"); - radProgress = p_70037_1_.getInteger("radProgress"); + protected void readEntityFromNBT(NBTTagCompound tag) { + setScale(tag.getInteger("scale")); + chunksToProcess.addAll(readChunksFromIntArray(tag.getIntArray("chunks"))); + outerChunksToProcess.addAll(readChunksFromIntArray(tag.getIntArray("outerChunks"))); + } + + private Collection readChunksFromIntArray(int[] data) { + List coords = new ArrayList<>(); + boolean firstPart = true; + int x = 0; + for (int coord : data) { + if (firstPart) x = coord; + else coords.add(ChunkCoordIntPair.chunkXZ2Int(x, coord)); + firstPart = !firstPart; + } + return coords; } @Override - protected void writeEntityToNBT(NBTTagCompound p_70014_1_) { - p_70014_1_.setInteger("scale", getScale()); - p_70014_1_.setInteger("revProgress", revProgress); - p_70014_1_.setInteger("radProgress", radProgress); - + protected void writeEntityToNBT(NBTTagCompound tag) { + tag.setInteger("scale", getScale()); + tag.setIntArray("chunks", writeChunksToIntArray(chunksToProcess)); + tag.setIntArray("outerChunks", writeChunksToIntArray(outerChunksToProcess)); + } + + private int[] writeChunksToIntArray(List coords) { + int[] data = new int[coords.size() * 2]; + for (int i = 0; i < coords.size(); i++) { + data[i * 2] = (int) (coords.get(i) & Integer.MAX_VALUE); + data[i * 2 + 1] = (int) (coords.get(i) >> 32 & Integer.MAX_VALUE); + } + return data; } public void setScale(int i) { - - this.dataWatcher.updateObject(16, Integer.valueOf(i)); + this.dataWatcher.updateObject(16, i); } public int getScale() { - int scale = this.dataWatcher.getWatchableObjectInt(16); - return scale == 0 ? 1 : scale; } } From 4cf9d64d8eb095093d651c06a00599c6333d4806 Mon Sep 17 00:00:00 2001 From: MartinTheDragon Date: Wed, 9 Mar 2022 18:38:47 +0100 Subject: [PATCH 2/3] Make the fallout chunk precision self-adjusting --- .../com/hbm/entity/effect/EntityFalloutRain.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/hbm/entity/effect/EntityFalloutRain.java b/src/main/java/com/hbm/entity/effect/EntityFalloutRain.java index 9e8330252..a9a012c4a 100644 --- a/src/main/java/com/hbm/entity/effect/EntityFalloutRain.java +++ b/src/main/java/com/hbm/entity/effect/EntityFalloutRain.java @@ -76,14 +76,17 @@ public class EntityFalloutRain extends Entity { Set chunks = new LinkedHashSet<>(); // LinkedHashSet preserves insertion order Set outerChunks = new LinkedHashSet<>(); int outerRange = getScale(); - for (int angle = 0; angle <= 720; angle++) { // TODO Make this adjust to the fallout's scale + // Basically defines something like the step size, but as indirect proportion. The actual angle used for rotation will always end up at 360° for angle == adjustedMaxAngle + // So yea, I mathematically worked out that 20 is a good value for this, with the minimum possible being 18 in order to reach all chunks + int adjustedMaxAngle = 20 * outerRange / 32; // step size = 20 * chunks / 2 + for (int angle = 0; angle <= adjustedMaxAngle; angle++) { Vec3 vector = Vec3.createVectorHelper(outerRange, 0, 0); - vector.rotateAroundY((float) (angle / 180.0 * Math.PI)); // Ugh, mutable data classes (also, ugh, radians; it uses degrees in 1.18; took me two hours to debug) + vector.rotateAroundY((float) (angle * Math.PI / 180.0 / (adjustedMaxAngle / 360.0))); // Ugh, mutable data classes (also, ugh, radians; it uses degrees in 1.18; took me two hours to debug) outerChunks.add(ChunkCoordIntPair.chunkXZ2Int((int) (posX + vector.xCoord) >> 4, (int) (posZ + vector.zCoord) >> 4)); } - for (int distance = 0; distance <= outerRange; distance += 8) for (int angle = 0; angle <= 720; angle++) { + for (int distance = 0; distance <= outerRange; distance += 8) for (int angle = 0; angle <= adjustedMaxAngle; angle++) { Vec3 vector = Vec3.createVectorHelper(distance, 0, 0); - vector.rotateAroundY((float) (angle / 180.0 * Math.PI)); + vector.rotateAroundY((float) (angle * Math.PI / 180.0 / (adjustedMaxAngle / 360.0))); long chunkCoord = ChunkCoordIntPair.chunkXZ2Int((int) (posX + vector.xCoord) >> 4, (int) (posZ + vector.zCoord) >> 4); if (!outerChunks.contains(chunkCoord)) chunks.add(chunkCoord); } @@ -93,7 +96,7 @@ public class EntityFalloutRain extends Entity { Collections.reverse(chunksToProcess); // So it starts nicely from the middle Collections.reverse(outerChunksToProcess); } - + private void stomp(int x, int z, double dist) { int depth = 0; From fdfc7a40a4630bf15d1151082442cdea3aec12cd Mon Sep 17 00:00:00 2001 From: MartinTheDragon Date: Fri, 11 Mar 2022 17:17:16 +0100 Subject: [PATCH 3/3] Delay fallout chunks and adjust ore chances --- src/main/java/com/hbm/config/BombConfig.java | 5 +- .../hbm/entity/effect/EntityFalloutRain.java | 47 +++++++++++-------- 2 files changed, 32 insertions(+), 20 deletions(-) diff --git a/src/main/java/com/hbm/config/BombConfig.java b/src/main/java/com/hbm/config/BombConfig.java index 68443e8e9..8e325df4f 100644 --- a/src/main/java/com/hbm/config/BombConfig.java +++ b/src/main/java/com/hbm/config/BombConfig.java @@ -23,7 +23,7 @@ public class BombConfig { public static int mk4 = 1024; public static int blastSpeed = 1024; public static int falloutRange = 100; - public static int fSpeed = 256; + public static int fDelay = 4; public static int limitExplosionLifespan = 0; public static void loadFromConfig(Configuration config) { @@ -88,5 +88,8 @@ public class BombConfig { Property falloutRangeProp = config.get(CATEGORY_NUKE, "6.03_falloutRange", 100); falloutRangeProp.comment = "Radius of fallout area (base radius * value in percent)"; falloutRange = falloutRangeProp.getInt(); + Property falloutDelayProp = config.get(CATEGORY_NUKE, "6.04_falloutDelay", 4); + falloutDelayProp.comment = "How many ticks to wait for the next fallout chunk computation"; + fDelay = falloutDelayProp.getInt(); } } diff --git a/src/main/java/com/hbm/entity/effect/EntityFalloutRain.java b/src/main/java/com/hbm/entity/effect/EntityFalloutRain.java index a9a012c4a..67963d3ef 100644 --- a/src/main/java/com/hbm/entity/effect/EntityFalloutRain.java +++ b/src/main/java/com/hbm/entity/effect/EntityFalloutRain.java @@ -1,6 +1,7 @@ package com.hbm.entity.effect; import com.hbm.blocks.ModBlocks; +import com.hbm.config.BombConfig; import com.hbm.config.RadiationConfig; import com.hbm.config.VersatileConfig; import com.hbm.saveddata.AuxSavedData; @@ -32,6 +33,8 @@ public class EntityFalloutRain extends Entity { this.isImmuneToFire = true; } + private int tickDelay = BombConfig.fDelay; + @Override public void onUpdate() { if(!worldObj.isRemote) { @@ -40,23 +43,28 @@ public class EntityFalloutRain extends Entity { firstTick = false; } - if (!chunksToProcess.isEmpty()) { - long chunkPos = chunksToProcess.remove(chunksToProcess.size() - 1); // Just so it doesn't shift the whole list every time - int chunkPosX = (int) (chunkPos & Integer.MAX_VALUE); - int chunkPosZ = (int) (chunkPos >> 32 & Integer.MAX_VALUE); - for (int x = chunkPosX << 4; x <= (chunkPosX << 4) + 16; x++) for (int z = chunkPosZ << 4; z <= (chunkPosZ << 4) + 16; z++) - stomp(x, z, Math.hypot(x - posX, z - posZ) * 100 / getScale()); - } else if (!outerChunksToProcess.isEmpty()) { - long chunkPos = outerChunksToProcess.remove(outerChunksToProcess.size() - 1); - int chunkPosX = (int) (chunkPos & Integer.MAX_VALUE); - int chunkPosZ = (int) (chunkPos >> 32 & Integer.MAX_VALUE); - for (int x = chunkPosX << 4; x <= (chunkPosX << 4) + 16; x++) for (int z = chunkPosZ << 4; z <= (chunkPosZ << 4) + 16; z++) { - double distance = Math.hypot(x - posX, z - posZ); - if (distance <= getScale()) stomp(x, z, distance * 100 / getScale()); - } - } else setDead(); + if (tickDelay == 0) { + tickDelay = BombConfig.fDelay; + if (!chunksToProcess.isEmpty()) { + long chunkPos = chunksToProcess.remove(chunksToProcess.size() - 1); // Just so it doesn't shift the whole list every time + int chunkPosX = (int) (chunkPos & Integer.MAX_VALUE); + int chunkPosZ = (int) (chunkPos >> 32 & Integer.MAX_VALUE); + for (int x = chunkPosX << 4; x <= (chunkPosX << 4) + 16; x++) for (int z = chunkPosZ << 4; z <= (chunkPosZ << 4) + 16; z++) + stomp(x, z, Math.hypot(x - posX, z - posZ) * 100 / getScale()); + } else if (!outerChunksToProcess.isEmpty()) { + long chunkPos = outerChunksToProcess.remove(outerChunksToProcess.size() - 1); + int chunkPosX = (int) (chunkPos & Integer.MAX_VALUE); + int chunkPosZ = (int) (chunkPos >> 32 & Integer.MAX_VALUE); + for (int x = chunkPosX << 4; x <= (chunkPosX << 4) + 16; x++) for (int z = chunkPosZ << 4; z <= (chunkPosZ << 4) + 16; z++) { + double distance = Math.hypot(x - posX, z - posZ); + if (distance <= getScale()) stomp(x, z, distance * 100 / getScale()); + } + } else setDead(); + } - if(this.isDead) { + tickDelay--; + + if(this.isDead) { if(RadiationConfig.rain > 0 && getScale() > 150) { worldObj.getWorldInfo().setRaining(true); worldObj.getWorldInfo().setThundering(true); @@ -97,6 +105,7 @@ public class EntityFalloutRain extends Entity { Collections.reverse(outerChunksToProcess); } + // TODO cache chunks? private void stomp(int x, int z, double dist) { int depth = 0; @@ -154,7 +163,7 @@ public class EntityFalloutRain extends Entity { return; } else if(b == Blocks.sand) { - if(rand.nextInt(60) == 0) + if(rand.nextInt(20) == 0) worldObj.setBlock(x, y, z, meta == 0 ? ModBlocks.waste_trinitite : ModBlocks.waste_trinitite_red); return; } @@ -171,9 +180,9 @@ public class EntityFalloutRain extends Entity { else if (b == Blocks.coal_ore) { int ra = rand.nextInt(150); - if (ra < 7) { + if (ra < 20) { worldObj.setBlock(x, y, z, Blocks.diamond_ore); - } else if (ra < 10) { + } else if (ra < 30) { worldObj.setBlock(x, y, z, Blocks.emerald_ore); } return;