mirror of
https://github.com/taoneill/war.git
synced 2024-11-10 12:39:55 +01:00
Load war zone blocks in batches to prevent server hangs
War zone blocks are now loaded from the database at a configurable speed of blocks per tick. This prevents an entire server from hanging whenever a war zone is reset. The speed can be increased or decreased based on your server's performance.
This commit is contained in:
parent
aacd93b960
commit
60e74eff99
@ -183,6 +183,7 @@ public class War extends JavaPlugin {
|
||||
warConfig.put(WarConfig.MAXZONES, 12);
|
||||
warConfig.put(WarConfig.PVPINZONESONLY, false);
|
||||
warConfig.put(WarConfig.TNTINZONESONLY, false);
|
||||
warConfig.put(WarConfig.RESETSPEED, 5000);
|
||||
|
||||
warzoneDefaultConfig.put(WarzoneConfig.AUTOASSIGN, false);
|
||||
warzoneDefaultConfig.put(WarzoneConfig.BLOCKHEADS, true);
|
||||
|
@ -1097,7 +1097,6 @@ public class Warzone {
|
||||
public void reinitialize() {
|
||||
this.isReinitializing = true;
|
||||
this.getVolume().resetBlocksAsJob();
|
||||
this.initializeZoneAsJob();
|
||||
}
|
||||
|
||||
public void handlePlayerLeave(Player player, Location destination, PlayerMoveEvent event, boolean removeFromTeam) {
|
||||
|
@ -8,7 +8,8 @@ public enum WarConfig {
|
||||
KEEPOLDZONEVERSIONS (Boolean.class),
|
||||
MAXZONES (Integer.class),
|
||||
PVPINZONESONLY (Boolean.class),
|
||||
TNTINZONESONLY (Boolean.class);
|
||||
TNTINZONESONLY (Boolean.class),
|
||||
RESETSPEED (Integer.class);
|
||||
|
||||
private final Class<?> configType;
|
||||
|
||||
|
@ -0,0 +1,85 @@
|
||||
package com.tommytony.war.job;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
|
||||
import com.tommytony.war.War;
|
||||
import com.tommytony.war.Warzone;
|
||||
import com.tommytony.war.structure.ZoneLobby;
|
||||
import com.tommytony.war.volume.ZoneVolume;
|
||||
|
||||
public class PartialZoneResetJob extends BukkitRunnable implements Cloneable {
|
||||
|
||||
private final Warzone zone;
|
||||
private final ZoneVolume volume;
|
||||
private final int speed;
|
||||
private final int total;
|
||||
private int completed = 0;
|
||||
private final long startTime = System.currentTimeMillis();
|
||||
private long messageCounter = System.currentTimeMillis();
|
||||
public static final long MESSAGE_INTERVAL = 3000;
|
||||
// Ticks between job runs
|
||||
public static final int JOB_INTERVAL = 1;
|
||||
|
||||
/**
|
||||
* Reset a warzone's blocks at a certain speed.
|
||||
*
|
||||
* @param volume
|
||||
* Warzone to reset.
|
||||
* @param speed
|
||||
* Blocks to modify per #INTERVAL.
|
||||
*/
|
||||
public PartialZoneResetJob(Warzone zone, int speed) {
|
||||
this.zone = zone;
|
||||
this.volume = zone.getVolume();
|
||||
this.speed = speed;
|
||||
this.total = volume.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
volume.resetSection(completed, speed);
|
||||
completed += speed;
|
||||
if (completed < total) {
|
||||
if (System.currentTimeMillis() - messageCounter > MESSAGE_INTERVAL) {
|
||||
messageCounter = System.currentTimeMillis();
|
||||
int percent = (int) (((double) completed / (double) total) * 100);
|
||||
long seconds = (System.currentTimeMillis() - startTime) / 1000;
|
||||
String message = MessageFormat.format(
|
||||
War.war.getString("zone.battle.resetprogress"),
|
||||
percent, seconds);
|
||||
for (Player player : War.war.getServer().getOnlinePlayers()) {
|
||||
ZoneLobby lobby = ZoneLobby.getLobbyByLocation(player);
|
||||
if (zone.getPlayers().contains(player)
|
||||
|| (lobby != null && lobby.getZone() == zone)) {
|
||||
War.war.msg(player, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
War.war.getServer().getScheduler()
|
||||
.runTaskLater(War.war, this.clone(), JOB_INTERVAL);
|
||||
} else {
|
||||
zone.initializeZone();
|
||||
War.war.getLogger().info(
|
||||
"Finished reset cycle for warzone " + volume.getName());
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
War.war.getLogger().log(Level.WARNING,
|
||||
"Failed to load zone during reset loop", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PartialZoneResetJob clone() {
|
||||
try {
|
||||
return (PartialZoneResetJob) super.clone();
|
||||
} catch (CloneNotSupportedException e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
}
|
@ -56,10 +56,12 @@ public class ZoneVolumeMapper {
|
||||
* @param String zoneName Zone to load the volume from
|
||||
* @param World world The world the zone is located
|
||||
* @param boolean onlyLoadCorners Should only the corners be loaded
|
||||
* @param start Starting position to load blocks at
|
||||
* @param total Amount of blocks to read
|
||||
* @return integer Changed blocks
|
||||
* @throws SQLException Error communicating with SQLite3 database
|
||||
*/
|
||||
public static int load(ZoneVolume volume, String zoneName, World world, boolean onlyLoadCorners) throws SQLException {
|
||||
public static int load(ZoneVolume volume, String zoneName, World world, boolean onlyLoadCorners, int start, int total) throws SQLException {
|
||||
int changed = 0;
|
||||
File databaseFile = new File(War.war.getDataFolder(), String.format("/dat/warzone-%s/volume-%s.sl3", zoneName, volume.getName()));
|
||||
if (!databaseFile.exists()) {
|
||||
@ -99,7 +101,7 @@ public class ZoneVolumeMapper {
|
||||
databaseConnection.close();
|
||||
return 0;
|
||||
}
|
||||
ResultSet query = stmt.executeQuery("SELECT * FROM blocks");
|
||||
ResultSet query = stmt.executeQuery("SELECT * FROM blocks ORDER BY rowid LIMIT " + start + ", " + total);
|
||||
while (query.next()) {
|
||||
int x = query.getInt("x"), y = query.getInt("y"), z = query.getInt("z");
|
||||
BlockState modify = corner1.getRelative(x, y, z).getState();
|
||||
|
@ -10,6 +10,8 @@ import org.bukkit.block.Block;
|
||||
import com.tommytony.war.Team;
|
||||
import com.tommytony.war.War;
|
||||
import com.tommytony.war.Warzone;
|
||||
import com.tommytony.war.config.WarConfig;
|
||||
import com.tommytony.war.job.PartialZoneResetJob;
|
||||
import com.tommytony.war.mapper.ZoneVolumeMapper;
|
||||
import com.tommytony.war.structure.Monument;
|
||||
|
||||
@ -48,7 +50,7 @@ public class ZoneVolume extends Volume {
|
||||
}
|
||||
|
||||
public void loadCorners() throws SQLException {
|
||||
ZoneVolumeMapper.load(this, this.zone.getName(), this.getWorld(), true);
|
||||
ZoneVolumeMapper.load(this, this.zone.getName(), this.getWorld(), true, 0, 0);
|
||||
this.isSaved = true;
|
||||
}
|
||||
|
||||
@ -57,7 +59,7 @@ public class ZoneVolume extends Volume {
|
||||
// Load blocks directly from disk and onto the map (i.e. no more in-memory warzone blocks)
|
||||
int reset = 0;
|
||||
try {
|
||||
reset = ZoneVolumeMapper.load(this, this.zone.getName(), this.getWorld(), false);
|
||||
reset = ZoneVolumeMapper.load(this, this.zone.getName(), this.getWorld(), false, 0, Integer.MAX_VALUE);
|
||||
} catch (SQLException ex) {
|
||||
War.war.log("Failed to load warzone " + zone.getName() + ": " + ex.getMessage(), Level.WARNING);
|
||||
ex.printStackTrace();
|
||||
@ -66,6 +68,30 @@ public class ZoneVolume extends Volume {
|
||||
this.isSaved = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset a section of blocks in the warzone.
|
||||
*
|
||||
* @param start
|
||||
* Starting position for reset.
|
||||
* @param total
|
||||
* Amount of blocks to reset.
|
||||
* @throws SQLException
|
||||
*/
|
||||
public void resetSection(int start, int total) throws SQLException {
|
||||
ZoneVolumeMapper.load(this, this.zone.getName(), this.getWorld(), false, start, total);
|
||||
}
|
||||
|
||||
@Override
|
||||
/**
|
||||
* Reset the blocks in this warzone at the speed defined in WarConfig#RESETSPEED.
|
||||
* The job will automatically spawn new instances of itself to run every tick until it is done resetting all blocks.
|
||||
*/
|
||||
public void resetBlocksAsJob() {
|
||||
PartialZoneResetJob job = new PartialZoneResetJob(zone, War.war
|
||||
.getWarConfig().getInt(WarConfig.RESETSPEED));
|
||||
War.war.getServer().getScheduler().runTask(War.war, job);
|
||||
}
|
||||
|
||||
public void setNorthwest(Location block) throws NotNorthwestException, TooSmallException, TooBigException {
|
||||
// northwest defaults to top block
|
||||
Location topBlock = new Location(block.getWorld(), block.getX(), block.getWorld().getMaxHeight(), block.getZ());
|
||||
|
@ -92,6 +92,7 @@ zone.battle.end = The battle is over. Team {0} lost: {1} died and the
|
||||
zone.battle.newscores = New scores - {0}
|
||||
zone.battle.next = The battle was interrupted. Resetting warzone {0}...
|
||||
zone.battle.reset = A new battle begins. Resetting warzone...
|
||||
zone.battle.resetprogress = Reset progress: {0}%, {1} seconds
|
||||
zone.bomb.broadcast = {0} blew up team {1}''s spawn. Team {2} scores one point.
|
||||
zone.cake.broadcast = {0} captured cake {1}. Team {2} scores one point and gets a full lifepool.
|
||||
zone.flagcapture.broadcast = {0} captured team {1}''s flag. Team {2} scores one point.
|
||||
|
Loading…
Reference in New Issue
Block a user