Fork of Poseidon providing Bukkit #1060 to older Beta versions (b1.0-b1.7.3)
at develop 362 lines 15 kB view raw
1package net.minecraft.server; 2 3import com.legacyminecraft.poseidon.PoseidonConfig; 4import org.bukkit.Location; 5import org.bukkit.event.entity.EntityDamageByBlockEvent; 6import org.bukkit.event.entity.EntityDamageByEntityEvent; 7import org.bukkit.event.entity.EntityDamageEvent; 8import org.bukkit.event.entity.EntityExplodeEvent; 9import org.jetbrains.annotations.NotNull; 10 11import java.util.*; 12 13public class Explosion { 14 public boolean setFire = false; 15 private final Random random = new Random(); 16 private final World world; 17 public double posX; 18 public double posY; 19 public double posZ; 20 public Entity source; 21 public EntityDamageEvent.DamageCause customDamageCause = null; // Poseidon 22 public float size; 23 public Set<ChunkPosition> blocks = new HashSet<>(); // UberBukkit: Set -> Set<ChunkPosition> 24 25 public boolean wasCanceled = false; // CraftBukkit 26 27 public Explosion(World world, Entity entity, double d0, double d1, double d2, float f) { 28 this.world = world; 29 this.source = entity; 30 this.size = f; 31 this.posX = d0; 32 this.posY = d1; 33 this.posZ = d2; 34 } 35 36 public void a() { 37 float f = this.size; 38 byte b0 = 16; 39 40 int i; 41 int j; 42 int k; 43 double d0; 44 double d1; 45 double d2; 46 47 for (i = 0; i < b0; ++i) { 48 for (j = 0; j < b0; ++j) { 49 for (k = 0; k < b0; ++k) { 50 if (i == 0 || i == b0 - 1 || j == 0 || j == b0 - 1 || k == 0 || k == b0 - 1) { 51 double d3 = ((float) i / ((float) b0 - 1.0F) * 2.0F - 1.0F); 52 double d4 = ((float) j / ((float) b0 - 1.0F) * 2.0F - 1.0F); 53 double d5 = ((float) k / ((float) b0 - 1.0F) * 2.0F - 1.0F); 54 double d6 = Math.sqrt(d3 * d3 + d4 * d4 + d5 * d5); 55 56 d3 /= d6; 57 d4 /= d6; 58 d5 /= d6; 59 float f1 = this.size * (0.7F + this.world.random.nextFloat() * 0.6F); 60 61 d0 = this.posX; 62 d1 = this.posY; 63 d2 = this.posZ; 64 65 for (float f2 = 0.3F; f1 > 0.0F; f1 -= f2 * 0.75F) { 66 int l = MathHelper.floor(d0); 67 int i1 = MathHelper.floor(d1); 68 int j1 = MathHelper.floor(d2); 69 int k1 = this.world.getTypeId(l, i1, j1); 70 71 if (k1 > 0) { 72 f1 -= (Block.byId[k1].a(this.source) + 0.3F) * f2; 73 } 74 75 if (f1 > 0.0F) { 76 this.blocks.add(new ChunkPosition(l, i1, j1)); 77 } 78 79 d0 += d3 * (double) f2; 80 d1 += d4 * (double) f2; 81 d2 += d5 * (double) f2; 82 } 83 } 84 } 85 } 86 } 87 88 this.size *= 2.0F; 89 i = MathHelper.floor(this.posX - (double) this.size - 1.0D); 90 j = MathHelper.floor(this.posX + (double) this.size + 1.0D); 91 k = MathHelper.floor(this.posY - (double) this.size - 1.0D); 92 int l1 = MathHelper.floor(this.posY + (double) this.size + 1.0D); 93 int i2 = MathHelper.floor(this.posZ - (double) this.size - 1.0D); 94 int j2 = MathHelper.floor(this.posZ + (double) this.size + 1.0D); 95 List list = this.world.b(this.source, AxisAlignedBB.b(i, k, i2, j, l1, j2)); 96 Vec3D vec3d = Vec3D.create(this.posX, this.posY, this.posZ); 97 98 /* 99 * Whether explosions should be optimized or not 100 * A backport from PaperMC 101 * Config option: 102 * optimizedExplosions: false 103 */ 104 boolean optimizeExplosions = (boolean) PoseidonConfig.getInstance().getProperty("world-settings.optimized-explosions"); 105 boolean sendMotion = (boolean) PoseidonConfig.getInstance().getProperty("world-settings.send-explosion-velocity"); 106 107 for (int k2 = 0; k2 < list.size(); ++k2) { 108 Entity entity = (Entity) list.get(k2); 109 double d7 = entity.f(this.posX, this.posY, this.posZ) / (double) this.size; 110 111 if (d7 <= 1.0D) { 112 d0 = entity.locX - this.posX; 113 d1 = entity.locY - this.posY; 114 d2 = entity.locZ - this.posZ; 115 double d8 = MathHelper.a(d0 * d0 + d1 * d1 + d2 * d2); 116 117 d0 /= d8; 118 d1 /= d8; 119 d2 /= d8; 120 double d9; 121 if (optimizeExplosions) { 122 d9 = this.getBlockDensity(vec3d, entity); // Paper - Optimize explosions 123 } else { 124 d9 = this.world.a(vec3d, entity.boundingBox); 125 } 126 double d10 = (1.0D - d7) * d9; 127 128 // CraftBukkit start - explosion damage hook 129 org.bukkit.Server server = this.world.getServer(); 130 org.bukkit.entity.Entity damagee = (entity == null) ? null : entity.getBukkitEntity(); 131 int damageDone = (int) ((d10 * d10 + d10) / 2.0D * 8.0D * (double) this.size + 1.0D); 132 133 // Block explosion, damagee is not null 134 if (damagee != null && (this.source == null || this.source instanceof EntityTNTPrimed)) { 135 // This event gets fired by tnt, exploding beds, and explosions created by plugins 136 // TODO: get the x/y/z of the tnt block? 137 EntityDamageByBlockEvent event = getEntityDamageByBlockEvent(damagee, damageDone); 138 server.getPluginManager().callEvent(event); 139 140 if (!event.isCancelled()) { 141 entity.damageEntity(this.source, event.getDamage()); 142 entity.motX += d0 * d10; 143 entity.motY += d1 * d10; 144 entity.motZ += d2 * d10; 145 if (sendMotion) { // Poseidon: fix explosion velocity 146 entity.velocityChanged = true; 147 } 148 } 149 } else { 150 EntityDamageByEntityEvent event = new EntityDamageByEntityEvent(this.source.getBukkitEntity(), damagee, EntityDamageEvent.DamageCause.ENTITY_EXPLOSION, damageDone); 151 server.getPluginManager().callEvent(event); 152 153 if (!event.isCancelled()) { 154 entity.damageEntity(this.source, event.getDamage()); 155 entity.motX += d0 * d10; 156 entity.motY += d1 * d10; 157 entity.motZ += d2 * d10; 158 if (sendMotion) { // Poseidon: fix explosion velocity 159 entity.velocityChanged = true; 160 } 161 } 162 } 163 // CraftBukkit end 164 } 165 } 166 167 this.size = f; 168 169 ArrayList<ChunkPosition> arraylist = new ArrayList<>(); 170 arraylist.addAll(this.blocks); 171 172 if (this.setFire) { 173 for (int l2 = arraylist.size() - 1; l2 >= 0; --l2) { 174 ChunkPosition chunkposition = arraylist.get(l2); 175 int i3 = chunkposition.x; 176 int j3 = chunkposition.y; 177 int k3 = chunkposition.z; 178 int l3 = this.world.getTypeId(i3, j3, k3); 179 int i4 = this.world.getTypeId(i3, j3 - 1, k3); 180 181 if (l3 == 0 && Block.o[i4] && this.random.nextInt(3) == 0) { 182 this.world.setTypeId(i3, j3, k3, Block.FIRE.id); 183 } 184 } 185 } 186 } 187 188 @NotNull 189 private EntityDamageByBlockEvent getEntityDamageByBlockEvent(org.bukkit.entity.Entity damagee, int damageDone) { 190 EntityDamageByBlockEvent event; 191 if (this.customDamageCause != null) { 192 event = new EntityDamageByBlockEvent(null, damagee, this.customDamageCause, damageDone); 193 } else if (this.source instanceof EntityTNTPrimed) { 194 event = new EntityDamageByBlockEvent(null, damagee, EntityDamageEvent.DamageCause.TNT_EXPLOSION, damageDone); 195 } else { 196 event = new EntityDamageByBlockEvent(null, damagee, EntityDamageEvent.DamageCause.BLOCK_EXPLOSION, damageDone); 197 } 198 return event; 199 } 200 201 public void a(boolean flag) { 202 this.world.makeSound(this.posX, this.posY, this.posZ, "random.explode", 4.0F, (1.0F + (this.world.random.nextFloat() - this.world.random.nextFloat()) * 0.2F) * 0.7F); 203 204 ArrayList<ChunkPosition> blocksCopy = new ArrayList<>(this.blocks); 205 206 // CraftBukkit start 207 org.bukkit.World bworld = this.world.getWorld(); 208 org.bukkit.entity.Entity explode = this.source == null ? null : this.source.getBukkitEntity(); 209 Location location = new Location(bworld, this.posX, this.posY, this.posZ); 210 211 List<org.bukkit.block.Block> blockList = new ArrayList<>(); 212 for (int j = blocksCopy.size() - 1; j >= 0; j--) { 213 ChunkPosition cpos = blocksCopy.get(j); 214 // UberBukkit - No need to handle blocks that aren't in the world's boundaries 215 if (cpos.y > 127 || cpos.y < 0) { 216 blocksCopy.remove(j); 217 continue; 218 } 219 220 org.bukkit.block.Block block = bworld.getBlockAt(cpos.x, cpos.y, cpos.z); 221 if (block.getType() != org.bukkit.Material.AIR) { 222 blockList.add(block); 223 } 224 } 225 226 EntityExplodeEvent event = new EntityExplodeEvent(explode, location, blockList); 227 this.world.getServer().getPluginManager().callEvent(event); 228 229 if (event.isCancelled()) { 230 this.wasCanceled = true; 231 return; 232 } 233 234 // Project Poseidon Start 235 // Backport from newer CraftBukkit 236 blocksCopy.clear(); 237 this.blocks.clear(); 238 for (final org.bukkit.block.Block block2 : event.blockList()) { 239 final ChunkPosition coords = new ChunkPosition(block2.getX(), block2.getY(), block2.getZ()); 240 blocksCopy.add(coords); 241 this.blocks.add(coords); 242 } 243 // Project Poseidon End 244 // CraftBukkit end 245 246 for (int i = blocksCopy.size() - 1; i >= 0; --i) { 247 ChunkPosition chunkposition = blocksCopy.get(i); 248 int j = chunkposition.x; 249 int k = chunkposition.y; 250 int l = chunkposition.z; 251 int i1 = this.world.getTypeId(j, k, l); 252 253 if (flag) { 254 double d0 = (float) j + this.world.random.nextFloat(); 255 double d1 = (float) k + this.world.random.nextFloat(); 256 double d2 = (float) l + this.world.random.nextFloat(); 257 double d3 = d0 - this.posX; 258 double d4 = d1 - this.posY; 259 double d5 = d2 - this.posZ; 260 double d6 = MathHelper.a(d3 * d3 + d4 * d4 + d5 * d5); 261 262 d3 /= d6; 263 d4 /= d6; 264 d5 /= d6; 265 double d7 = 0.5D / (d6 / (double) this.size + 0.1D); 266 267 d7 *= this.world.random.nextFloat() * this.world.random.nextFloat() + 0.3F; 268 d3 *= d7; 269 d4 *= d7; 270 d5 *= d7; 271 this.world.a("explode", (d0 + this.posX) / 2.0D, (d1 + this.posY) / 2.0D, (d2 + this.posZ) / 2.0D, d3, d4, d5); 272 this.world.a("smoke", d0, d1, d2, d3, d4, d5); 273 } 274 275 // CraftBukkit - stop explosions from putting out fire 276 if (i1 > 0 && i1 != Block.FIRE.id) { 277 // CraftBukkit 278 Block.byId[i1].dropNaturally(this.world, j, k, l, this.world.getData(j, k, l), event.getYield()); 279 this.world.setTypeId(j, k, l, 0); 280 Block.byId[i1].d(this.world, j, k, l); 281 } 282 } 283 } 284 285 // Paper start - Optimize explosions 286 private float getBlockDensity(Vec3D vec3d, Entity entity) { 287 CacheKey key = new CacheKey(this, entity.boundingBox); 288 Float blockDensity = this.world.explosionDensityCache.get(key); 289 if (blockDensity == null) { 290 blockDensity = this.world.a(vec3d, entity.boundingBox); 291 this.world.explosionDensityCache.put(key, blockDensity); 292 } 293 294 return blockDensity; 295 } 296 297 static class CacheKey { 298 private final World world; 299 private final double posX, posY, posZ; 300 private final double minX, minY, minZ; 301 private final double maxX, maxY, maxZ; 302 303 public CacheKey(Explosion explosion, AxisAlignedBB aabb) { 304 this.world = explosion.world; 305 this.posX = explosion.posX; 306 this.posY = explosion.posY; 307 this.posZ = explosion.posZ; 308 this.minX = aabb.a; 309 this.minY = aabb.b; 310 this.minZ = aabb.c; 311 this.maxX = aabb.d; 312 this.maxY = aabb.e; 313 this.maxZ = aabb.f; 314 } 315 316 @Override 317 public boolean equals(Object o) { 318 if (this == o) return true; 319 if (o == null || getClass() != o.getClass()) return false; 320 321 CacheKey cacheKey = (CacheKey) o; 322 323 if (Double.compare(cacheKey.posX, posX) != 0) return false; 324 if (Double.compare(cacheKey.posY, posY) != 0) return false; 325 if (Double.compare(cacheKey.posZ, posZ) != 0) return false; 326 if (Double.compare(cacheKey.minX, minX) != 0) return false; 327 if (Double.compare(cacheKey.minY, minY) != 0) return false; 328 if (Double.compare(cacheKey.minZ, minZ) != 0) return false; 329 if (Double.compare(cacheKey.maxX, maxX) != 0) return false; 330 if (Double.compare(cacheKey.maxY, maxY) != 0) return false; 331 if (Double.compare(cacheKey.maxZ, maxZ) != 0) return false; 332 return world.equals(cacheKey.world); 333 } 334 335 @Override 336 public int hashCode() { 337 int result; 338 long temp; 339 result = world.hashCode(); 340 temp = Double.doubleToLongBits(posX); 341 result = 31 * result + (int) (temp ^ (temp >>> 32)); 342 temp = Double.doubleToLongBits(posY); 343 result = 31 * result + (int) (temp ^ (temp >>> 32)); 344 temp = Double.doubleToLongBits(posZ); 345 result = 31 * result + (int) (temp ^ (temp >>> 32)); 346 temp = Double.doubleToLongBits(minX); 347 result = 31 * result + (int) (temp ^ (temp >>> 32)); 348 temp = Double.doubleToLongBits(minY); 349 result = 31 * result + (int) (temp ^ (temp >>> 32)); 350 temp = Double.doubleToLongBits(minZ); 351 result = 31 * result + (int) (temp ^ (temp >>> 32)); 352 temp = Double.doubleToLongBits(maxX); 353 result = 31 * result + (int) (temp ^ (temp >>> 32)); 354 temp = Double.doubleToLongBits(maxY); 355 result = 31 * result + (int) (temp ^ (temp >>> 32)); 356 temp = Double.doubleToLongBits(maxZ); 357 result = 31 * result + (int) (temp ^ (temp >>> 32)); 358 return result; 359 } 360 } 361 // Paper end 362}