1
0
Fork 0

fix explosions for real this time

This commit is contained in:
Sen 2025-08-27 19:43:14 +02:00
parent 11db0e57cb
commit aaad753263
Signed by: sen
GPG key ID: 3AC50A6F47D1B722
36 changed files with 338 additions and 495 deletions

View file

@ -24,9 +24,9 @@ public class CommandExplode extends Command {
public void exec(CommandEnvironment env, Executor exec, Vec3 pos, WorldServer world, int radius, boolean ticked, boolean fire, boolean noblocks, boolean altsound) {
if(ticked)
world.newExplosion(pos.xCoord, pos.yCoord, pos.zCoord, radius);
world.explodeTicked(null, pos.xCoord, pos.yCoord, pos.zCoord, radius);
else
world.newExplosion(null, pos.xCoord, pos.yCoord, pos.zCoord, radius, fire, !noblocks, altsound);
world.explode(null, null, pos.xCoord, pos.yCoord, pos.zCoord, radius, fire, !noblocks, altsound);
exec.log("Explosion bei %d, %d, %d in %s erzeugt", ExtMath.floord(pos.xCoord), ExtMath.floord(pos.yCoord), ExtMath.floord(pos.zCoord), world.dimension.getDisplay());
}
}

View file

@ -27,10 +27,13 @@ import common.collect.Maps;
import common.collect.Sets;
import common.dimension.DimType;
import common.dimension.Dimension;
import common.effect.Effect;
import common.enchantment.Enchantment;
import common.entity.DamageSource;
import common.entity.Entity;
import common.entity.EntityTrackerEntry;
import common.entity.effect.EntityLightning;
import common.entity.item.EntityExplosion;
import common.entity.npc.EntityNPC;
import common.entity.types.EntityLiving;
import common.init.Blocks;
@ -40,6 +43,7 @@ import common.log.Log;
import common.network.IPlayer;
import common.network.Packet;
import common.packet.SPacketEntityStatus;
import common.packet.SPacketEntityVelocity;
import common.packet.SPacketExplosion;
import common.packet.SPacketEffect;
import common.packet.SPacketSoundEffect;
@ -67,7 +71,6 @@ import common.vars.Vars;
import common.village.Village;
import common.world.BlockArray;
import common.world.Chunk;
import common.world.Explosion;
import common.world.AWorldServer;
import common.world.LightType;
import common.world.State;
@ -111,6 +114,31 @@ import server.worldgen.tree.WorldGenTaiga2;
public final class WorldServer extends AWorldServer {
private static final int[][] XZ_DIRS = new int[][] {{1, 0}, {0, 1}, {-1, 0}, {0, -1}};
private static final int[][] TABLES = new int[1024][];
private static int[] shuffle(int size) {
int[] table = new int[size];
Random rand = new Random(8236737136521L);
for(int z = 0; z < size; z++) {
table[z] = z;
}
for(int z = 0; z < size; z++) {
int r = rand.zrange(size);
int n = table[z];
table[z] = table[r];
table[r] = n;
}
return table;
}
private static int[] getTable(int size) {
if(size < 0 || size >= TABLES.length)
return null;
int[] table = TABLES[size];
if(table == null)
TABLES[size] = table = shuffle(size);
return table;
}
private final Server server;
private final File chunkDir;
@ -906,24 +934,152 @@ public final class WorldServer extends AWorldServer {
public void setEntityState(Entity entityIn, byte state) {
this.sendToAllTrackingAndSelf(entityIn, new SPacketEntityStatus(entityIn, state));
}
private boolean destroyBlock(BlockPos pos, float power, EntityLiving source) {
State state = this.getState(pos);
Block block = state.getBlock();
if(block != Blocks.air && (block.getMaterial().isLiquid() || power - (block.getResistance() / 5.0F + 0.3F) * 0.3F > 0.0F)) {
if(block.canExplosionDrop() && this.rand.floatv() <= 1.0F / (power * power))
block.drop(this, pos, state, 0);
this.setState(pos, Blocks.air.getState(), power < 40.0F ? 2 : 10);
block.onDestroyedExplosion(this, pos, source, state);
return true;
}
return false;
}
private Set<EntityNPC> damageEntities(Entity exploder, EntityLiving source, double posX, double posY, double posZ, float radius) {
float dmgRadius = radius * 2.0F;
int x1 = ExtMath.floord(posX - (double)dmgRadius - 1.0D);
int x2 = ExtMath.floord(posX + (double)dmgRadius + 1.0D);
int y1 = ExtMath.floord(posY - (double)dmgRadius - 1.0D);
int y2 = ExtMath.floord(posY + (double)dmgRadius + 1.0D);
int z1 = ExtMath.floord(posZ - (double)dmgRadius - 1.0D);
int z2 = ExtMath.floord(posZ + (double)dmgRadius + 1.0D);
List<Entity> list = this.getEntitiesWithinAABBExcludingEntity(exploder, new BoundingBox((double)x1, (double)y1, (double)z1, (double)x2, (double)y2, (double)z2));
Vec3 vec3 = new Vec3(posX, posY, posZ);
Set<EntityNPC> velocity = Sets.newHashSet();
for (int pos = 0; pos < list.size(); pos++)
{
Entity entity = (Entity)list.get(pos);
if (!entity.isImmuneToExplosions())
{
double dist = entity.getDistance(posX, posY, posZ) / (double)dmgRadius;
if (dist <= 1.0D)
{
double dx = entity.posX - posX;
double dy = entity.posY + (double)entity.getEyeHeight() - posY;
double dz = entity.posZ - posZ;
double dt = (double)ExtMath.sqrtd(dx * dx + dy * dy + dz * dz);
if (dt != 0.0D)
{
dx = dx / dt;
dy = dy / dt;
dz = dz / dt;
double density = (double)this.getBlockDensity(vec3, entity.getEntityBoundingBox());
double damage = (1.0D - dist) * density;
if(Vars.damageExplosion)
entity.attackEntityFrom(DamageSource.causeExplosionDamage(source), ((int)((damage * damage + damage) / 2.0D * 8.0D * (double)dmgRadius + 1.0D)));
if(!(entity instanceof EntityLiving living) || !living.hasEffect(Effect.STABILITY)) {
double velo = Enchantment.getKnockbackFactor(entity, damage);
entity.motionX += dx * velo;
entity.motionY += dy * velo;
entity.motionZ += dz * velo;
if(entity.isPlayer())
velocity.add((EntityNPC)entity);
}
}
}
}
}
return velocity;
}
public Explosion newExplosion(Entity entityIn, double x, double y, double z, float strength, boolean isFlaming, boolean isSmoking, boolean altSound) {
Explosion explosion = new Explosion(this, entityIn, x, y, z, strength, isFlaming, isSmoking);
explosion.doExplosionA();
explosion.doExplosionB(false, altSound);
public void explode(EntityLiving source, Entity exploder, double posX, double posY, double posZ, float radius, boolean fire, boolean destroy, boolean altSound) {
Set<BlockPos> set = Sets.<BlockPos>newHashSet();
int d = ((int)radius) + 1;
double falloff = radius * 0.125d;
falloff = falloff > 4.0d ? 4.0d : falloff;
for (int x = -d; x <= d; ++x)
{
for (int y = -d; y <= d; ++y)
{
for (int z = -d; z <= d; ++z)
{
double dist = (double)ExtMath.sqrtd(((double)x) * ((double)x) + ((double)y) * ((double)y) + ((double)z) * ((double)z));
if(dist < radius - falloff ||
(dist < radius && this.rand.doublev() + ((dist - (radius - falloff)) / falloff) < 1.0d)) {
BlockPos pos = new BlockPos(posX + x, posY + y, posZ + z);
if(this.destroyBlock(pos, radius, source))
set.add(pos);
}
}
}
}
List<BlockPos> blocks = Lists.<BlockPos>newArrayList(set);
set.clear();
Set<EntityNPC> velocity = this.damageEntities(exploder, source, posX, posY, posZ, radius);
if(!isSmoking) {
explosion.clearAffectedBlockPositions();
this.sendSound(altSound ? SoundEvent.EXPLODE_ALT : SoundEvent.EXPLODE, posX, posY, posZ, 4.0F);
if (fire)
{
for (BlockPos pos : blocks)
{
if(this.getState(pos).getBlock() == Blocks.air && this.getState(pos.down()).getBlock().isFullBlock() && this.rand.zrange(3) == 0)
this.setState(pos, Blocks.fire.getState());
}
}
for(EntityNPC player : this.players) {
if(player.getDistanceSq(posX, posY, posZ) < 4096.0D)
player.connection.sendPacket(new SPacketExplosion(posX, posY, posZ, destroy ? blocks : null, radius >= 2.0f && destroy));
if(velocity.contains(player))
player.connection.sendPacket(new SPacketEntityVelocity(player));
}
for(EntityNPC entityplayer : this.players) {
if(entityplayer.getDistanceSq(x, y, z) < 4096.0D) {
entityplayer.connection.sendPacket(new SPacketExplosion(x, y, z, strength, explosion.getAffectedBlockPositions(),
(Vec3)explosion.getPlayerKnockbackMap().get(entityplayer), altSound));
}
}
public int explodePartial(EntityLiving source, Entity exploder, double posX, double posY, double posZ, double radius, double minDist, int iter, int max)
{
int d = ((int)radius) + 1;
int div = d * 2 + 1;
int[] table = getTable(div);
if(table == null)
return 0;
double falloff = radius * 0.125d;
falloff = falloff > 4.0d ? 4.0d : falloff;
int lmt = div * div * div;
int cnt = 0;
for(; iter < lmt && cnt < max; iter++)
{
int x = table[(iter / div) % div] - d;
int y = table[iter % div] - d;
int z = table[(iter / div) / div] - d;
double dist = (double)ExtMath.sqrtd(((double)x) * ((double)x) + ((double)y) * ((double)y) + ((double)z) * ((double)z));
if(dist > minDist && (dist < radius - falloff ||
(dist < radius && this.rand.doublev() + ((dist - (radius - falloff)) / falloff) < 1.0d))) {
BlockPos pos = new BlockPos(posX + x, posY + y, posZ + z);
if(this.destroyBlock(pos, (float)radius, null) && this.rand.chance(1000)) {
this.sendSound(SoundEvent.EXPLODE, posX + x, posY + y, posZ + z, 4.0F);
this.spawnParticles(ParticleType.EXPLOSION_HUGE, posX + x, posY + y, posZ + z);
}
cnt++;
}
}
Set<EntityNPC> velocity = this.damageEntities(exploder, source, posX, posY, posZ, (float)radius);
for(EntityNPC player : this.players) {
if(velocity.contains(player))
player.connection.sendPacket(new SPacketEntityVelocity(player));
}
return iter >= lmt ? 0 : iter;
}
return explosion;
public void explodeTicked(EntityLiving source, double x, double y, double z, int radius) {
this.spawnEntityInWorld(new EntityExplosion(this, x, y, z, radius, source));
}
public void resetWeather() {