Code/net/minecraft/world/level/ServerExplosion.java

342 lines
13 KiB
Java

package net.minecraft.world.level;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import javax.annotation.Nullable;
import net.minecraft.Util;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.Mth;
import net.minecraft.util.profiling.Profiler;
import net.minecraft.util.profiling.ProfilerFiller;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.entity.item.PrimedTnt;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.BaseFireBlock;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.gameevent.GameEvent;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
public class ServerExplosion implements Explosion {
private static final ExplosionDamageCalculator EXPLOSION_DAMAGE_CALCULATOR = new ExplosionDamageCalculator();
private static final int MAX_DROPS_PER_COMBINED_STACK = 16;
private static final float LARGE_EXPLOSION_RADIUS = 2.0F;
private final boolean fire;
private final Explosion.BlockInteraction blockInteraction;
private final ServerLevel level;
private final Vec3 center;
@Nullable
private final Entity source;
private final float radius;
private final DamageSource damageSource;
private final ExplosionDamageCalculator damageCalculator;
private final Map<Player, Vec3> hitPlayers = new HashMap<>();
public ServerExplosion(
ServerLevel p_363225_,
@Nullable Entity p_367780_,
@Nullable DamageSource p_367845_,
@Nullable ExplosionDamageCalculator p_361628_,
Vec3 p_364875_,
float p_361128_,
boolean p_362786_,
Explosion.BlockInteraction p_367128_
) {
this.level = p_363225_;
this.source = p_367780_;
this.radius = p_361128_;
this.center = p_364875_;
this.fire = p_362786_;
this.blockInteraction = p_367128_;
this.damageSource = p_367845_ == null ? p_363225_.damageSources().explosion(this) : p_367845_;
this.damageCalculator = p_361628_ == null ? this.makeDamageCalculator(p_367780_) : p_361628_;
}
private ExplosionDamageCalculator makeDamageCalculator(@Nullable Entity p_362997_) {
return (ExplosionDamageCalculator)(p_362997_ == null ? EXPLOSION_DAMAGE_CALCULATOR : new EntityBasedExplosionDamageCalculator(p_362997_));
}
public static float getSeenPercent(Vec3 p_367358_, Entity p_369280_) {
AABB aabb = p_369280_.getBoundingBox();
double d0 = 1.0 / ((aabb.maxX - aabb.minX) * 2.0 + 1.0);
double d1 = 1.0 / ((aabb.maxY - aabb.minY) * 2.0 + 1.0);
double d2 = 1.0 / ((aabb.maxZ - aabb.minZ) * 2.0 + 1.0);
double d3 = (1.0 - Math.floor(1.0 / d0) * d0) / 2.0;
double d4 = (1.0 - Math.floor(1.0 / d2) * d2) / 2.0;
if (!(d0 < 0.0) && !(d1 < 0.0) && !(d2 < 0.0)) {
int i = 0;
int j = 0;
for (double d5 = 0.0; d5 <= 1.0; d5 += d0) {
for (double d6 = 0.0; d6 <= 1.0; d6 += d1) {
for (double d7 = 0.0; d7 <= 1.0; d7 += d2) {
double d8 = Mth.lerp(d5, aabb.minX, aabb.maxX);
double d9 = Mth.lerp(d6, aabb.minY, aabb.maxY);
double d10 = Mth.lerp(d7, aabb.minZ, aabb.maxZ);
Vec3 vec3 = new Vec3(d8 + d3, d9, d10 + d4);
if (p_369280_.level()
.clip(new ClipContext(vec3, p_367358_, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, p_369280_))
.getType()
== HitResult.Type.MISS) {
i++;
}
j++;
}
}
}
return (float)i / j;
} else {
return 0.0F;
}
}
@Override
public float radius() {
return this.radius;
}
@Override
public Vec3 center() {
return this.center;
}
private List<BlockPos> calculateExplodedPositions() {
Set<BlockPos> set = new HashSet<>();
int i = 16;
for (int j = 0; j < 16; j++) {
for (int k = 0; k < 16; k++) {
for (int l = 0; l < 16; l++) {
if (j == 0 || j == 15 || k == 0 || k == 15 || l == 0 || l == 15) {
double d0 = j / 15.0F * 2.0F - 1.0F;
double d1 = k / 15.0F * 2.0F - 1.0F;
double d2 = l / 15.0F * 2.0F - 1.0F;
double d3 = Math.sqrt(d0 * d0 + d1 * d1 + d2 * d2);
d0 /= d3;
d1 /= d3;
d2 /= d3;
float f = this.radius * (0.7F + this.level.random.nextFloat() * 0.6F);
double d4 = this.center.x;
double d5 = this.center.y;
double d6 = this.center.z;
for (float f1 = 0.3F; f > 0.0F; f -= 0.22500001F) {
BlockPos blockpos = BlockPos.containing(d4, d5, d6);
BlockState blockstate = this.level.getBlockState(blockpos);
FluidState fluidstate = this.level.getFluidState(blockpos);
if (!this.level.isInWorldBounds(blockpos)) {
break;
}
Optional<Float> optional = this.damageCalculator.getBlockExplosionResistance(this, this.level, blockpos, blockstate, fluidstate);
if (optional.isPresent()) {
f -= (optional.get() + 0.3F) * 0.3F;
}
if (f > 0.0F && this.damageCalculator.shouldBlockExplode(this, this.level, blockpos, blockstate, f)) {
set.add(blockpos);
}
d4 += d0 * 0.3F;
d5 += d1 * 0.3F;
d6 += d2 * 0.3F;
}
}
}
}
}
return new ObjectArrayList<>(set);
}
private void hurtEntities() {
float f = this.radius * 2.0F;
int i = Mth.floor(this.center.x - f - 1.0);
int j = Mth.floor(this.center.x + f + 1.0);
int k = Mth.floor(this.center.y - f - 1.0);
int l = Mth.floor(this.center.y + f + 1.0);
int i1 = Mth.floor(this.center.z - f - 1.0);
int j1 = Mth.floor(this.center.z + f + 1.0);
for (Entity entity : this.level.getEntities(this.source, new AABB(i, k, i1, j, l, j1))) {
if (!entity.ignoreExplosion(this)) {
double d0 = Math.sqrt(entity.distanceToSqr(this.center)) / f;
if (d0 <= 1.0) {
double d1 = entity.getX() - this.center.x;
double d2 = (entity instanceof PrimedTnt ? entity.getY() : entity.getEyeY()) - this.center.y;
double d3 = entity.getZ() - this.center.z;
double d4 = Math.sqrt(d1 * d1 + d2 * d2 + d3 * d3);
if (d4 != 0.0) {
d1 /= d4;
d2 /= d4;
d3 /= d4;
boolean flag = this.damageCalculator.shouldDamageEntity(this, entity);
float f1 = this.damageCalculator.getKnockbackMultiplier(entity);
float f2 = !flag && f1 == 0.0F ? 0.0F : getSeenPercent(this.center, entity);
if (flag) {
entity.hurtServer(this.level, this.damageSource, this.damageCalculator.getEntityDamageAmount(this, entity, f2));
}
double d5 = (1.0 - d0) * f2 * f1;
double d6;
if (entity instanceof LivingEntity livingentity) {
d6 = d5 * (1.0 - livingentity.getAttributeValue(Attributes.EXPLOSION_KNOCKBACK_RESISTANCE));
} else {
d6 = d5;
}
d1 *= d6;
d2 *= d6;
d3 *= d6;
Vec3 vec3 = new Vec3(d1, d2, d3);
entity.push(vec3);
if (entity instanceof Player player && !player.isSpectator() && (!player.isCreative() || !player.getAbilities().flying)) {
this.hitPlayers.put(player, vec3);
}
entity.onExplosionHit(this.source);
}
}
}
}
}
private void interactWithBlocks(List<BlockPos> p_361066_) {
List<ServerExplosion.StackCollector> list = new ArrayList<>();
Util.shuffle(p_361066_, this.level.random);
for (BlockPos blockpos : p_361066_) {
this.level.getBlockState(blockpos).onExplosionHit(this.level, blockpos, this, (p_369158_, p_366512_) -> addOrAppendStack(list, p_369158_, p_366512_));
}
for (ServerExplosion.StackCollector serverexplosion$stackcollector : list) {
Block.popResource(this.level, serverexplosion$stackcollector.pos, serverexplosion$stackcollector.stack);
}
}
private void createFire(List<BlockPos> p_365156_) {
for (BlockPos blockpos : p_365156_) {
if (this.level.random.nextInt(3) == 0
&& this.level.getBlockState(blockpos).isAir()
&& this.level.getBlockState(blockpos.below()).isSolidRender()) {
this.level.setBlockAndUpdate(blockpos, BaseFireBlock.getState(this.level, blockpos));
}
}
}
public void explode() {
this.level.gameEvent(this.source, GameEvent.EXPLODE, this.center);
List<BlockPos> list = this.calculateExplodedPositions();
this.hurtEntities();
if (this.interactsWithBlocks()) {
ProfilerFiller profilerfiller = Profiler.get();
profilerfiller.push("explosion_blocks");
this.interactWithBlocks(list);
profilerfiller.pop();
}
if (this.fire) {
this.createFire(list);
}
}
private static void addOrAppendStack(List<ServerExplosion.StackCollector> p_364783_, ItemStack p_365928_, BlockPos p_366332_) {
for (ServerExplosion.StackCollector serverexplosion$stackcollector : p_364783_) {
serverexplosion$stackcollector.tryMerge(p_365928_);
if (p_365928_.isEmpty()) {
return;
}
}
p_364783_.add(new ServerExplosion.StackCollector(p_366332_, p_365928_));
}
private boolean interactsWithBlocks() {
return this.blockInteraction != Explosion.BlockInteraction.KEEP;
}
public Map<Player, Vec3> getHitPlayers() {
return this.hitPlayers;
}
@Override
public ServerLevel level() {
return this.level;
}
@Nullable
@Override
public LivingEntity getIndirectSourceEntity() {
return Explosion.getIndirectSourceEntity(this.source);
}
@Nullable
@Override
public Entity getDirectSourceEntity() {
return this.source;
}
public DamageSource getDamageSource() {
return this.damageSource;
}
@Override
public Explosion.BlockInteraction getBlockInteraction() {
return this.blockInteraction;
}
@Override
public boolean canTriggerBlocks() {
if (this.blockInteraction != Explosion.BlockInteraction.TRIGGER_BLOCK) {
return false;
} else {
return this.source != null && this.source.getType() == EntityType.BREEZE_WIND_CHARGE ? this.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) : true;
}
}
@Override
public boolean shouldAffectBlocklikeEntities() {
boolean flag = this.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING);
boolean flag1 = this.source == null || !this.source.isInWater();
boolean flag2 = this.source == null || this.source.getType() != EntityType.BREEZE_WIND_CHARGE && this.source.getType() != EntityType.WIND_CHARGE;
return flag ? flag1 && flag2 : this.blockInteraction.shouldAffectBlocklikeEntities() && flag1 && flag2;
}
public boolean isSmall() {
return this.radius < 2.0F || !this.interactsWithBlocks();
}
static class StackCollector {
final BlockPos pos;
ItemStack stack;
StackCollector(BlockPos p_361613_, ItemStack p_361574_) {
this.pos = p_361613_;
this.stack = p_361574_;
}
public void tryMerge(ItemStack p_362306_) {
if (ItemEntity.areMergable(this.stack, p_362306_)) {
this.stack = ItemEntity.merge(this.stack, p_362306_, 16);
}
}
}
}