Corrected concurrency issues in the explosion reversions.

This commit is contained in:
thetrouper
2025-06-19 10:42:58 -05:00
parent 2ebcfe1635
commit dbe8febac9
3 changed files with 59 additions and 77 deletions

View File

@@ -35,4 +35,8 @@ public class TaskManager implements Main {
tasks.keySet().forEach(Bukkit.getScheduler()::cancelTask); tasks.keySet().forEach(Bukkit.getScheduler()::cancelTask);
tasks.clear(); tasks.clear();
} }
public boolean isClosed() {
return closed;
}
} }

View File

@@ -1,6 +1,7 @@
package me.trouper.alias.server.systems.burning; package me.trouper.alias.server.systems.burning;
import me.trouper.alias.server.Main; import me.trouper.alias.server.Main;
import me.trouper.alias.server.systems.TaskManager;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.block.Block; import org.bukkit.block.Block;
@@ -16,22 +17,27 @@ import java.util.concurrent.ThreadLocalRandom;
public class BlockBurner implements Closeable, Main { public class BlockBurner implements Closeable, Main {
private final BurnOptions options; private final BurnOptions options;
private final BurnPalette palette; private final BurnPalette palette;
private boolean isClosed = false; private final TaskManager taskManager;
private final Set<Block> visited = new HashSet<>(); private final Set<Block> visited = new HashSet<>();
private final Map<Block, Material> burning = new HashMap<>(); private final Map<Block, Material> burning = new HashMap<>();
private final Set<Integer> tasks = new HashSet<>();
public BlockBurner(BurnOptions options) { public BlockBurner(BurnOptions options) {
this.options = options; this.options = options;
this.palette = new BurnPalette(); this.palette = new BurnPalette();
this.taskManager = new TaskManager();
}
public BlockBurner(BurnOptions options, TaskManager sharedTaskManager) {
this.options = options;
this.palette = new BurnPalette();
this.taskManager = sharedTaskManager;
} }
@Override @Override
public void close() { public void close() {
tasks.forEach(task -> Bukkit.getScheduler().cancelTask(task)); taskManager.close();
visited.clear(); visited.clear();
burning.clear(); burning.clear();
isClosed = true;
} }
public void burn(Block block, float heat) { public void burn(Block block, float heat) {
@@ -47,22 +53,13 @@ public class BlockBurner implements Closeable, Main {
if (block.getType().isAir() && canPlaceFireOn(blockBelow)) { if (block.getType().isAir() && canPlaceFireOn(blockBelow)) {
if (ThreadLocalRandom.current().nextFloat() > options.getSetFireChance()) return; if (ThreadLocalRandom.current().nextFloat() > options.getSetFireChance()) return;
if (isClosed()) return;
setBlock(block, palette.getFirePalette().getFirst()); setBlock(block, palette.getFirePalette().getFirst());
if (isClosed()) return; taskManager.scheduleTask(() -> {
int taskId = Bukkit.getScheduler().runTaskLater(main.getPlugin(), ()->{
if (!canPlaceFireOn(blockBelow)) { if (!canPlaceFireOn(blockBelow)) {
setBlock(block, Material.AIR); setBlock(block, Material.AIR);
} }
},20 * 10).getTaskId(); }, 20 * 10);
if (!isClosed()) {
tasks.add(taskId);
} else {
tasks.add(taskId);
Bukkit.getScheduler().cancelTask(taskId);
}
return; return;
} }
@@ -81,19 +78,11 @@ public class BlockBurner implements Closeable, Main {
for (BurnStage stage : stages) { for (BurnStage stage : stages) {
totalDelay += stage.getDelay(); totalDelay += stage.getDelay();
if (isClosed()) return;
int taskId = Bukkit.getScheduler().runTaskLater(main.getPlugin(), ()->{ taskManager.scheduleTask(() -> {
if (block.getType().isAir()) return; if (block.getType().isAir()) return;
setBlock(block, stage.getBlockData()); setBlock(block, stage.getBlockData());
},totalDelay).getTaskId(); }, totalDelay);
if (!isClosed()) {
tasks.add(taskId);
} else {
tasks.add(taskId);
Bukkit.getScheduler().cancelTask(taskId);
}
} }
} }
@@ -130,16 +119,20 @@ public class BlockBurner implements Closeable, Main {
} }
private void setBlock(Block block, Material material) { private void setBlock(Block block, Material material) {
if (isClosed()) return; if (taskManager.isClosed()) return;
block.setType(material); block.setType(material);
} }
private void setBlock(Block block, BlockData data) { private void setBlock(Block block, BlockData data) {
if (isClosed()) return; if (taskManager.isClosed()) return;
block.setBlockData(data); block.setBlockData(data);
} }
public boolean isClosed() { public boolean isClosed() {
return isClosed; return taskManager.isClosed();
}
public TaskManager getTaskManager() {
return taskManager;
} }
} }

View File

@@ -2,6 +2,7 @@ package me.trouper.alias.server.systems.world;
import me.trouper.alias.server.Main; import me.trouper.alias.server.Main;
import me.trouper.alias.server.systems.Verbose; import me.trouper.alias.server.systems.Verbose;
import me.trouper.alias.server.systems.TaskManager;
import me.trouper.alias.server.systems.burning.BlockBurner; import me.trouper.alias.server.systems.burning.BlockBurner;
import me.trouper.alias.server.systems.burning.BurnOptions; import me.trouper.alias.server.systems.burning.BurnOptions;
import org.bukkit.*; import org.bukkit.*;
@@ -64,19 +65,21 @@ public class ExplosionUtils implements Main {
public static class ExplosionResult { public static class ExplosionResult {
private final Map<Block, BlockState> originalStates = new HashMap<>(); private final Map<Block, BlockState> originalStates = new HashMap<>();
private final Map<Block, ItemStack[]> originalInventories = new HashMap<>(); private final Map<Block, ItemStack[]> originalInventories = new HashMap<>();
private final List<Integer> scheduledTaskIds = new ArrayList<>(); private final TaskManager taskManager;
private final BlockBurner burner; private final BlockBurner burner;
public ExplosionResult(TaskManager taskManager) {
this.taskManager = taskManager;
this.burner = new BlockBurner(new BurnOptions(), taskManager);
}
public ExplosionResult(BlockBurner burner) { public ExplosionResult(BlockBurner burner) {
this.burner = burner; this.burner = burner;
this.taskManager = burner.getTaskManager();
} }
public void cleanup() { public void cleanup() {
if (burner != null) { taskManager.close();
burner.close();
}
scheduledTaskIds.forEach(task -> Bukkit.getScheduler().cancelTask(task));
} }
void recordSnapshot(Block block) { void recordSnapshot(Block block) {
@@ -88,17 +91,11 @@ public class ExplosionUtils implements Main {
} }
} }
void addScheduledTask(int taskId) {
scheduledTaskIds.add(taskId);
}
public void restore() { public void restore() {
for (int taskId : scheduledTaskIds) { // First cleanup all tasks
Bukkit.getScheduler().cancelTask(taskId);
}
cleanup(); cleanup();
// Then restore blocks
for (Map.Entry<Block, BlockState> entry : originalStates.entrySet()) { for (Map.Entry<Block, BlockState> entry : originalStates.entrySet()) {
Block block = entry.getKey(); Block block = entry.getKey();
BlockState snapshot = entry.getValue(); BlockState snapshot = entry.getValue();
@@ -115,6 +112,7 @@ public class ExplosionUtils implements Main {
} }
public BlockBurner getBurner() { return burner; } public BlockBurner getBurner() { return burner; }
public TaskManager getTaskManager() { return taskManager; }
public Map<Block, BlockState> getOriginalStates() { public Map<Block, BlockState> getOriginalStates() {
return originalStates; return originalStates;
@@ -123,10 +121,6 @@ public class ExplosionUtils implements Main {
public Map<Block, ItemStack[]> getOriginalInventories() { public Map<Block, ItemStack[]> getOriginalInventories() {
return originalInventories; return originalInventories;
} }
public List<Integer> getScheduledTaskIds() {
return scheduledTaskIds;
}
} }
public static ExplosionResult createExplosion(Location center, ExplosionOptions options) { public static ExplosionResult createExplosion(Location center, ExplosionOptions options) {
@@ -135,11 +129,13 @@ public class ExplosionUtils implements Main {
Map<Block, Double> affectedBlocks = getBlocksInRadius(center, options.getMaxBurnRadius()); Map<Block, Double> affectedBlocks = getBlocksInRadius(center, options.getMaxBurnRadius());
ExplosionResult result = new ExplosionResult(new BlockBurner(options.getBurnOptions())); // Create shared task manager for this explosion
TaskManager sharedTaskManager = new TaskManager();
BlockBurner burner = new BlockBurner(options.getBurnOptions(), sharedTaskManager);
ExplosionResult result = new ExplosionResult(burner);
for (Block block : affectedBlocks.keySet()) { for (Block block : affectedBlocks.keySet()) {
if (block.getType().isAir()) continue; if (block.getType().isAir()) continue;
result.recordSnapshot(block); result.recordSnapshot(block);
} }
@@ -149,10 +145,8 @@ public class ExplosionUtils implements Main {
categorizeBlocks(affectedBlocks, options, blocksToDestroy, blocksToBurn, blocksHeatMap); categorizeBlocks(affectedBlocks, options, blocksToDestroy, blocksToBurn, blocksHeatMap);
BlockBurner burner = new BlockBurner(options.getBurnOptions()); scheduleDestruction(blocksToDestroy, options, sharedTaskManager);
scheduleBurning(blocksToBurn, blocksHeatMap, burner, center, options, sharedTaskManager);
scheduleDestruction(blocksToDestroy, options,result);
scheduleBurning(blocksToBurn, blocksHeatMap, burner, center, options,result);
if (options.isCreateParticles() || options.isPlaySound()) createExplosionEffects(center, options); if (options.isCreateParticles() || options.isPlaySound()) createExplosionEffects(center, options);
@@ -229,12 +223,12 @@ public class ExplosionUtils implements Main {
return (float) (options.getMinHeat() + heatRange * heatFactor); return (float) (options.getMinHeat() + heatRange * heatFactor);
} }
private static void scheduleDestruction(Set<Block> blocksToDestroy, ExplosionOptions options, ExplosionResult result) { private static void scheduleDestruction(Set<Block> blocksToDestroy, ExplosionOptions options, TaskManager taskManager) {
if (blocksToDestroy.isEmpty()) return; if (blocksToDestroy.isEmpty()) return;
long destructionDelayTicks = (long) (options.getDestructionDelay() * 20); long destructionDelayTicks = (long) (options.getDestructionDelay() * 20);
int outerTask = Bukkit.getScheduler().runTaskLater(main.getPlugin(),()->{ taskManager.scheduleTask(() -> {
List<Block> blockList = new ArrayList<>(blocksToDestroy); List<Block> blockList = new ArrayList<>(blocksToDestroy);
Collections.shuffle(blockList); Collections.shuffle(blockList);
@@ -246,29 +240,26 @@ public class ExplosionUtils implements Main {
if (startIndex >= blockList.size()) break; if (startIndex >= blockList.size()) break;
int innerTask = Bukkit.getScheduler().runTaskLater(main.getPlugin(),()->{ taskManager.scheduleTask(() -> {
for (int i = startIndex; i < endIndex; i++) { for (int i = startIndex; i < endIndex; i++) {
Block block = blockList.get(i); Block block = blockList.get(i);
if (!block.getType().isAir()) { if (!block.getType().isAir()) {
block.setType(Material.AIR); block.setType(Material.AIR);
} }
} }
},wave * 2).getTaskId(); }, wave * 2);
result.addScheduledTask(innerTask);
} }
},destructionDelayTicks).getTaskId(); }, destructionDelayTicks);
result.addScheduledTask(outerTask);
} }
private static void scheduleBurning(Set<Block> blocksToMaybeBurn, Map<Block, Float> blocksHeatMap, private static void scheduleBurning(Set<Block> blocksToMaybeBurn, Map<Block, Float> blocksHeatMap,
BlockBurner burner, Location center, ExplosionOptions options, BlockBurner burner, Location center, ExplosionOptions options,
ExplosionResult result) { TaskManager taskManager) {
if (blocksToMaybeBurn.isEmpty()) return; if (blocksToMaybeBurn.isEmpty()) return;
long burnDelayTicks = (long) (options.getBurnDelay() * 20); long burnDelayTicks = (long) (options.getBurnDelay() * 20);
int outerTask = Bukkit.getScheduler().runTaskLater(main.getPlugin(),()->{
taskManager.scheduleTask(() -> {
List<Block> blockList = new ArrayList<>(blocksToMaybeBurn); List<Block> blockList = new ArrayList<>(blocksToMaybeBurn);
Collections.shuffle(blockList); Collections.shuffle(blockList);
@@ -285,7 +276,7 @@ public class ExplosionUtils implements Main {
int waveDelay = waveEntry.getKey() * 3; int waveDelay = waveEntry.getKey() * 3;
List<Block> waveBlocks = waveEntry.getValue(); List<Block> waveBlocks = waveEntry.getValue();
int middleTask = Bukkit.getScheduler().runTaskLater(main.getPlugin(),()->{ taskManager.scheduleTask(() -> {
for (Block block : waveBlocks) { for (Block block : waveBlocks) {
if (burner.isClosed()) continue; if (burner.isClosed()) continue;
@@ -294,21 +285,15 @@ public class ExplosionUtils implements Main {
ThreadLocalRandom random = ThreadLocalRandom.current(); ThreadLocalRandom random = ThreadLocalRandom.current();
int randomDelay = random.nextInt(0, 10); int randomDelay = random.nextInt(0, 10);
int innerTask = Bukkit.getScheduler().runTaskLater(main.getPlugin(),()->{ taskManager.scheduleTask(() -> {
if (!burner.isClosed() && !block.getType().isAir()) { if (!burner.isClosed() && !block.getType().isAir()) {
burner.burn(block, heat); burner.burn(block, heat);
} }
},randomDelay).getTaskId(); }, randomDelay);
result.addScheduledTask(innerTask);
} }
},waveDelay).getTaskId(); }, waveDelay);
result.addScheduledTask(middleTask);
} }
},burnDelayTicks).getTaskId(); }, burnDelayTicks);
result.addScheduledTask(outerTask);
} }
private static void createExplosionEffects(Location center, ExplosionOptions options) { private static void createExplosionEffects(Location center, ExplosionOptions options) {
@@ -326,9 +311,9 @@ public class ExplosionUtils implements Main {
world.spawnParticle(Particle.LARGE_SMOKE, center, 15, 1, 1, 1, 0.05); world.spawnParticle(Particle.LARGE_SMOKE, center, 15, 1, 1, 1, 0.05);
world.spawnParticle(Particle.FLAME, center, 30, 3, 3, 3, 0.1); world.spawnParticle(Particle.FLAME, center, 30, 3, 3, 3, 0.1);
Bukkit.getScheduler().runTaskLater(main.getPlugin(),()->{ Bukkit.getScheduler().runTaskLater(main.getPlugin(), () -> {
world.spawnParticle(Particle.SMOKE, center, 50, 4, 4, 4, 0.02); world.spawnParticle(Particle.SMOKE, center, 50, 4, 4, 4, 0.02);
},20); }, 20);
} }
} }