Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix explosions being different from vanilla #98

Merged
merged 5 commits into from
Aug 18, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import net.minecraft.entity.damage.DamageSource;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.fluid.FluidState;
import net.minecraft.fluid.Fluids;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Box;
import net.minecraft.util.math.MathHelper;
Expand All @@ -19,6 +20,7 @@
import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.chunk.ChunkSection;
import net.minecraft.world.explosion.Explosion;
import net.minecraft.world.explosion.ExplosionBehavior;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
Expand All @@ -28,6 +30,7 @@

import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Random;

@Mixin(Explosion.class)
Expand Down Expand Up @@ -72,6 +75,9 @@ public static float getExposure(Vec3d self, Entity entity) {
@Final
private Map<PlayerEntity, Vec3d> affectedPlayers;

@Shadow
@Final
private ExplosionBehavior behavior;
// The cached mutable block position used during block traversal.
private final BlockPos.Mutable cachedPos = new BlockPos.Mutable();

Expand Down Expand Up @@ -155,7 +161,7 @@ private void performRayCast(Random random, double vecX, double vecY, double vecZ
int prevY = Integer.MIN_VALUE;
int prevZ = Integer.MIN_VALUE;

float prevResistance = 0.0f;
float prevResistance = 0.0F;

// Step through the ray until it is finally stopped
while (strength > 0.0F) {
Expand All @@ -181,8 +187,9 @@ private void performRayCast(Random random, double vecX, double vecY, double vecZ
resistance = prevResistance;
}

strength -= resistance;
// Apply a constant fall-off
strength -= resistance + 0.225F;
strength -= 0.22500001F;

stepX += normX * 0.3D;
stepY += normY * 0.3D;
Expand All @@ -199,12 +206,17 @@ private void performRayCast(Random random, double vecX, double vecY, double vecZ
* @return The resistance of the current block space to the ray
*/
private float traverseBlock(float strength, int blockX, int blockY, int blockZ, LongOpenHashSet touched) {
BlockPos pos = this.cachedPos.set(blockX, blockY, blockZ);

// Early-exit if the y-coordinate is out of bounds.
if (World.isHeightInvalid(blockY)) {
return 0.0f;
Optional<Float> blastResistance = this.behavior.getBlastResistance((Explosion) (Object) this, this.world, pos, Blocks.AIR.getDefaultState(), Fluids.EMPTY.getDefaultState());
if (blastResistance.isPresent()) {
return (blastResistance.get() + 0.3F) * 0.3F;
}
return 0.0F;
}

BlockPos pos = this.cachedPos.set(blockX, blockY, blockZ);

int chunkX = blockX >> 4;
int chunkZ = blockZ >> 4;
Expand All @@ -220,44 +232,47 @@ private float traverseBlock(float strength, int blockX, int blockY, int blockZ,
final Chunk chunk = this.prevChunk;

BlockState blockState = Blocks.AIR.getDefaultState();
float totalResistance = 0.0f;

// If the chunk is missing or out of bounds, assume that it is air
if (chunk != null) {
// We operate directly on chunk sections to avoid interacting with BlockPos and to squeeze out as much
// performance as possible here
ChunkSection section = chunk.getSectionArray()[blockY >> 4];

// If the section doesn't exist or it's empty, assume that the block is air
if (section != null && !section.isEmpty()) {
// Retrieve the block state from the chunk section directly to avoid associated overhead
blockState = section.getBlockState(blockX & 15, blockY & 15, blockZ & 15);

// If the block state is air, it cannot have fluid or any kind of resistance, so just leave
if (blockState.getBlock() != Blocks.AIR) {
// Rather than query the fluid state from the container as we just did with the block state, we can
// simply ask the block state we retrieved what fluid it has. This is exactly what the call would
// do anyways, except that it would have to retrieve the block state a second time, adding overhead.
FluidState fluidState = blockState.getFluidState();

// Pick the highest resistance value between the block and fluid
float resistance = Math.max(blockState.getBlock().getBlastResistance(), fluidState.getBlastResistance());

// If this explosion was caused by an entity, allow for it to modify the resistance of this position
if (this.entity != null) {
resistance = this.entity.getEffectiveExplosionResistance((Explosion) (Object) this, this.world, pos, blockState, fluidState, resistance);
float totalResistance = 0.0F;
Optional<Float> blastResistance;

labelGetBlastResistance:
{
// If the chunk is missing or out of bounds, assume that it is air
if (chunk != null) {
// We operate directly on chunk sections to avoid interacting with BlockPos and to squeeze out as much
// performance as possible here
ChunkSection section = chunk.getSectionArray()[blockY >> 4];

// If the section doesn't exist or it's empty, assume that the block is air
if (section != null && !section.isEmpty()) {
// Retrieve the block state from the chunk section directly to avoid associated overhead
blockState = section.getBlockState(blockX & 15, blockY & 15, blockZ & 15);

// If the block state is air, it cannot have fluid or any kind of resistance, so just leave
if (blockState.getBlock() != Blocks.AIR) {
// Rather than query the fluid state from the container as we just did with the block state, we can
// simply ask the block state we retrieved what fluid it has. This is exactly what the call would
// do anyways, except that it would have to retrieve the block state a second time, adding overhead.
FluidState fluidState = blockState.getFluidState();

// Get the explosion resistance like vanilla
blastResistance = this.behavior.getBlastResistance((Explosion) (Object) this, this.world, pos, blockState, fluidState);
break labelGetBlastResistance;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possibly cleaner to have this be a separate function to avoid the label and break

}

// Calculate how much this block space will resist an explosion's ray
totalResistance = (resistance + 0.3F) * 0.3F;
}
}
blastResistance = this.behavior.getBlastResistance((Explosion) (Object) this, this.world, pos, Blocks.AIR.getDefaultState(), Fluids.EMPTY.getDefaultState());
}
// Calculate how much this block will resist an explosion's ray
if (blastResistance.isPresent()) {
totalResistance = (blastResistance.get() + 0.3F) * 0.3F;
}

// Check if this ray is still strong enough to break blocks, and if so, add this position to the set
// of positions to destroy
if ((strength - totalResistance) > 0.0F) {
if ((this.entity == null) || this.entity.canExplosionDestroyBlock((Explosion) (Object) this, this.world, pos, blockState, strength)) {
float reducedStrength = strength - totalResistance;
if (reducedStrength > 0.0F) {
if (this.behavior.canDestroyBlock((Explosion) (Object) this, this.world, pos, blockState, reducedStrength)) {
touched.add(pos.asLong());
}
}
Expand Down