Fork of Poseidon providing Bukkit #1060 to older Beta versions (b1.0-b1.7.3)
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}