Make MASpawnThread logic cancellable.
This commit changes how MASpawnThread is started. Instead of simply scheduling a task and letting it run whenever, we now explicitly tell it to stop when the arena ends. This matters, because if the scheduled task does not get cancelled, it will run at the scheduled time. MASpawnThread's run() method has an early return to stop whenever the arena isn't running, but with unlucky timing, it's possible to start a new arena session before the task runs again, meaning the same MASpawnThread keeps scheduling itself on the previous scheduling loop. Instead of this whacky approach, we now specifically have MASpawnThread track its own BukkitTask. Upon arena start, we instantiate a new MASpawnThread and call its start() method, which in turn schedules itself and stores a reference to the associated BukkitTask. Upon arena end, we call the MASpawnThread's stop() method, which explicitly calls the BukkitTask's cancel() method, effectively stopping/unscheduling the task. This fixes the (very) long-standing "double wave spawner" bug that we haven't been able to reproduce for so long. Hooray!
This commit is contained in:
@@ -996,26 +996,15 @@ public class ArenaImpl implements Arena
|
||||
}
|
||||
|
||||
private void startSpawner() {
|
||||
// Set the spawn flags to enable monster spawning.
|
||||
world.setSpawnFlags(true, true);
|
||||
//world.setDifficulty(Difficulty.NORMAL);
|
||||
|
||||
// Create a spawner if one doesn't exist, otherwise reset it
|
||||
if (spawnThread == null) {
|
||||
spawnThread = new MASpawnThread(plugin, this);
|
||||
} else {
|
||||
spawnThread.reset();
|
||||
if (spawnThread != null) {
|
||||
spawnThread.stop();
|
||||
spawnThread = null;
|
||||
}
|
||||
|
||||
// Schedule it for the initial first wave delay.
|
||||
scheduleTask(spawnThread, settings.getInt("first-wave-delay", 5) * 20);
|
||||
|
||||
// Schedule to enable PvP if pvp-enabled: true
|
||||
scheduleTask(new Runnable() {
|
||||
public void run() {
|
||||
eventListener.pvpActivate();
|
||||
}
|
||||
}, settings.getInt("first-wave-delay", 5) * 20);
|
||||
|
||||
world.setSpawnFlags(true, true);
|
||||
|
||||
spawnThread = new MASpawnThread(plugin, this);
|
||||
spawnThread.start();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1030,9 +1019,15 @@ public class ArenaImpl implements Arena
|
||||
}
|
||||
|
||||
private void stopSpawner() {
|
||||
if (spawnThread == null) {
|
||||
plugin.getLogger().warning("Can't stop non-existent spawner in arena " + configName() + ". This should never happen.");
|
||||
return;
|
||||
}
|
||||
|
||||
spawnThread.stop();
|
||||
spawnThread = null;
|
||||
|
||||
world.setSpawnFlags(allowMonsters, allowAnimals);
|
||||
eventListener.pvpDeactivate();
|
||||
//world.setDifficulty(spawnMonsters);
|
||||
}
|
||||
|
||||
private void startBouncingSheep()
|
||||
|
||||
@@ -16,11 +16,13 @@ import com.garbagemule.MobArena.waves.enums.WaveType;
|
||||
import com.garbagemule.MobArena.waves.types.BossWave;
|
||||
import com.garbagemule.MobArena.waves.types.SupplyWave;
|
||||
import com.garbagemule.MobArena.waves.types.UpgradeWave;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.scheduler.BukkitTask;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -41,6 +43,8 @@ public class MASpawnThread implements Runnable
|
||||
private int waveInterval;
|
||||
private int nextWaveDelay;
|
||||
|
||||
private BukkitTask task;
|
||||
|
||||
/**
|
||||
* Create a new monster spawner for the input arena.
|
||||
* Note that the arena's WaveManager is reset
|
||||
@@ -75,6 +79,32 @@ public class MASpawnThread implements Runnable
|
||||
nextWaveDelay = arena.getSettings().getInt("next-wave-delay", 0);
|
||||
}
|
||||
|
||||
public void start() {
|
||||
if (task != null) {
|
||||
plugin.getLogger().warning("Starting spawner in arena " + arena.configName() + " with existing spawner still running. This should never happen.");
|
||||
task.cancel();
|
||||
task = null;
|
||||
}
|
||||
|
||||
int delay = arena.getSettings().getInt("first-wave-delay", 5) * 20;
|
||||
task = Bukkit.getScheduler().runTaskLater(plugin, () -> {
|
||||
arena.getEventListener().pvpActivate();
|
||||
this.run();
|
||||
}, delay);
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
if (task == null) {
|
||||
plugin.getLogger().warning("Can't stop non-existent spawner in arena " + arena.configName() + ". This should never happen.");
|
||||
return;
|
||||
}
|
||||
|
||||
arena.getEventListener().pvpDeactivate();
|
||||
|
||||
task.cancel();
|
||||
task = null;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
// If the arena isn't running or if there are no players in it.
|
||||
if (!arena.isRunning() || arena.getPlayersInArena().isEmpty()) {
|
||||
@@ -109,7 +139,7 @@ public class MASpawnThread implements Runnable
|
||||
|
||||
// Delay the next wave
|
||||
if (nextWaveDelay > 0) {
|
||||
arena.scheduleTask(this::spawnNextWave, nextWaveDelay * 20);
|
||||
task = Bukkit.getScheduler().runTaskLater(plugin, this::spawnNextWave, nextWaveDelay * 20);
|
||||
} else {
|
||||
spawnNextWave();
|
||||
}
|
||||
@@ -146,7 +176,7 @@ public class MASpawnThread implements Runnable
|
||||
updateStats(nextWave);
|
||||
|
||||
// Reschedule the spawner for the next wave.
|
||||
arena.scheduleTask(this, waveInterval * 20);
|
||||
task = Bukkit.getScheduler().runTaskLater(plugin, this, waveInterval * 20);
|
||||
}
|
||||
|
||||
private void spawnWave(int wave) {
|
||||
|
||||
Reference in New Issue
Block a user