Corrected concurrency issues in the explosion reversions.
This commit is contained in:
@@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user