mirror of
https://github.com/taoneill/war.git
synced 2024-11-14 06:17:01 +01:00
Merge pull request #718 from taoneill/nimitz-v2
Optimize nimitz file store Tested on bukkit server 1.7.2 with a warzone originally created with war 1.7.4, therefore upgrade works properly.
This commit is contained in:
commit
59779eb0e5
@ -7,7 +7,6 @@ import java.util.*;
|
||||
import java.util.logging.FileHandler;
|
||||
import java.util.logging.Formatter;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Location;
|
||||
@ -116,16 +115,16 @@ public class War extends JavaPlugin {
|
||||
}
|
||||
|
||||
/**
|
||||
* @see JavaPlugin.onEnable()
|
||||
* @see War.loadWar()
|
||||
* @see JavaPlugin#onEnable()
|
||||
* @see War#loadWar()
|
||||
*/
|
||||
public void onEnable() {
|
||||
this.loadWar();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see JavaPlugin.onDisable()
|
||||
* @see War.unloadWar()
|
||||
* @see JavaPlugin#onDisable()
|
||||
* @see War#unloadWar()
|
||||
*/
|
||||
public void onDisable() {
|
||||
this.unloadWar();
|
||||
@ -207,6 +206,7 @@ public class War extends JavaPlugin {
|
||||
warzoneDefaultConfig.put(WarzoneConfig.XPKILLMETER, false);
|
||||
warzoneDefaultConfig.put(WarzoneConfig.SOUPHEALING, false);
|
||||
warzoneDefaultConfig.put(WarzoneConfig.ALLOWENDER, true);
|
||||
warzoneDefaultConfig.put(WarzoneConfig.RESETBLOCKS, true);
|
||||
|
||||
teamDefaultConfig.put(TeamConfig.FLAGMUSTBEHOME, true);
|
||||
teamDefaultConfig.put(TeamConfig.FLAGPOINTSONLY, false);
|
||||
@ -342,7 +342,7 @@ public class War extends JavaPlugin {
|
||||
}
|
||||
|
||||
/**
|
||||
* @see JavaPlugin.onCommand()
|
||||
* @see org.bukkit.command.CommandExecutor#onCommand(org.bukkit.command.CommandSender, org.bukkit.command.Command, String, String[])
|
||||
*/
|
||||
public boolean onCommand(CommandSender sender, Command cmd, String commandLabel, String[] args) {
|
||||
return this.commandHandler.handle(sender, cmd, args);
|
||||
@ -379,11 +379,6 @@ public class War extends JavaPlugin {
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public ItemStack copyStack(ItemStack originalStack) {
|
||||
return originalStack.clone();
|
||||
}
|
||||
|
||||
public void safelyEnchant(ItemStack target, Enchantment enchantment, int level) {
|
||||
if (level > enchantment.getMaxLevel()) {
|
||||
target.addUnsafeEnchantment(enchantment, level);
|
||||
@ -983,10 +978,8 @@ public class War extends JavaPlugin {
|
||||
/**
|
||||
* Colors the teams and examples in messages
|
||||
*
|
||||
* @param String
|
||||
* str message-string
|
||||
* @param String
|
||||
* msgColor current message-color
|
||||
* @param str message-string
|
||||
* @param msgColor current message-color
|
||||
* @return String Message with colored teams
|
||||
*/
|
||||
private String colorKnownTokens(String str, ChatColor msgColor) {
|
||||
@ -1014,18 +1007,11 @@ public class War extends JavaPlugin {
|
||||
/**
|
||||
* Logs a specified message with a specified level
|
||||
*
|
||||
* @param String
|
||||
* str message to log
|
||||
* @param Level
|
||||
* lvl level to use
|
||||
* @param str message to log
|
||||
* @param lvl level to use
|
||||
*/
|
||||
public void log(String str, Level lvl) {
|
||||
// Log to Bukkit console
|
||||
this.getLogger().log(lvl, str);
|
||||
|
||||
// if (this.warLogger != null) {
|
||||
// this.warLogger.log(lvl, str);
|
||||
// }
|
||||
}
|
||||
|
||||
// the only way to find a zone that has only one corner
|
||||
|
@ -1,5 +1,9 @@
|
||||
package com.tommytony.war;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@ -10,6 +14,8 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import com.tommytony.war.mapper.VolumeMapper;
|
||||
import com.tommytony.war.mapper.ZoneVolumeMapper;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.bukkit.Bukkit;
|
||||
@ -1028,7 +1034,11 @@ public class Warzone {
|
||||
} else {
|
||||
// A new battle starts. Reset the zone but not the teams.
|
||||
this.broadcast("zone.battle.reset");
|
||||
this.reinitialize();
|
||||
if (this.getWarzoneConfig().getBoolean(WarzoneConfig.RESETBLOCKS)) {
|
||||
this.reinitialize();
|
||||
} else {
|
||||
this.initializeZone();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -1146,9 +1156,10 @@ public class Warzone {
|
||||
team.resetPoints();
|
||||
team.setRemainingLives(team.getTeamConfig().resolveInt(TeamConfig.LIFEPOOL));
|
||||
}
|
||||
this.getVolume().resetBlocksAsJob();
|
||||
this.initializeZoneAsJob();
|
||||
War.war.log("Last player left warzone " + this.getName() + ". Warzone blocks resetting automatically...", Level.INFO);
|
||||
if (!this.isReinitializing()) {
|
||||
this.reinitialize();
|
||||
War.war.getLogger().log(Level.INFO, "Last player left warzone {0}. Warzone blocks resetting automatically...", new Object[] {this.getName()});
|
||||
}
|
||||
}
|
||||
|
||||
WarPlayerLeaveEvent event1 = new WarPlayerLeaveEvent(player.getName());
|
||||
@ -1342,7 +1353,11 @@ public class Warzone {
|
||||
t.getPlayers().clear(); // empty the team
|
||||
t.resetSign();
|
||||
}
|
||||
this.reinitialize();
|
||||
if (this.getWarzoneConfig().getBoolean(WarzoneConfig.RESETBLOCKS)) {
|
||||
this.reinitialize();
|
||||
} else {
|
||||
this.initializeZone();
|
||||
}
|
||||
}
|
||||
|
||||
public void rewardPlayer(Player player, Map<Integer, ItemStack> reward) {
|
||||
@ -1827,6 +1842,39 @@ public class Warzone {
|
||||
return this.getTeleport();
|
||||
}
|
||||
|
||||
public Volume loadStructure(String volName, World world) throws SQLException {
|
||||
return loadStructure(volName, world, ZoneVolumeMapper.getZoneConnection(volume, name, world));
|
||||
}
|
||||
|
||||
public Volume loadStructure(String volName, Connection zoneConnection) throws SQLException {
|
||||
return loadStructure(volName, world, zoneConnection);
|
||||
}
|
||||
|
||||
public Volume loadStructure(String volName, World world, Connection zoneConnection) throws SQLException {
|
||||
Volume volume = new Volume(volName, world);
|
||||
if (!containsTable(String.format("structure_%d_corners", volName.hashCode() & Integer.MAX_VALUE), zoneConnection)) {
|
||||
volume = VolumeMapper.loadVolume(volName, name, world);
|
||||
ZoneVolumeMapper.saveStructure(volume, zoneConnection);
|
||||
War.war.getLogger().log(Level.INFO, "Stuffed structure {0} into database for warzone {1}", new Object[] {volName, name});
|
||||
return volume;
|
||||
}
|
||||
ZoneVolumeMapper.loadStructure(volume, zoneConnection);
|
||||
return volume;
|
||||
}
|
||||
|
||||
private boolean containsTable(String table, Connection connection) throws SQLException {
|
||||
PreparedStatement stmt = connection.prepareStatement("SELECT COUNT(*) AS ct FROM sqlite_master WHERE type = ? AND name = ?");
|
||||
stmt.setString(1, "table");
|
||||
stmt.setString(2, table);
|
||||
ResultSet resultSet = stmt.executeQuery();
|
||||
try {
|
||||
return resultSet.next() && resultSet.getInt("ct") > 0;
|
||||
} finally {
|
||||
resultSet.close();
|
||||
stmt.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a player has stolen from a warzone flag, bomb, or cake.
|
||||
* @param suspect Player to check.
|
||||
|
@ -53,6 +53,8 @@ public class JoinCommand extends AbstractWarCommand {
|
||||
}
|
||||
if (zone.getWarzoneConfig().getBoolean(WarzoneConfig.DISABLED)) {
|
||||
this.badMsg("join.disabled");
|
||||
} else if (zone.isReinitializing()) {
|
||||
this.badMsg("join.disabled");
|
||||
} else if (zone.getWarzoneConfig().getBoolean(WarzoneConfig.AUTOASSIGN)) {
|
||||
this.badMsg("join.aarequired");
|
||||
} else if (!zone.getWarzoneConfig().getBoolean(WarzoneConfig.JOINMIDBATTLE) && zone.isEnoughPlayers()) {
|
||||
|
@ -78,7 +78,7 @@ public class RenameZoneCommand extends AbstractZoneMakerCommand {
|
||||
|
||||
// Load new warzone
|
||||
War.war.log("Loading zone " + newName + "...", Level.INFO);
|
||||
Warzone newZone = WarzoneYmlMapper.load(newName, false);
|
||||
Warzone newZone = WarzoneYmlMapper.load(newName);
|
||||
War.war.getWarzones().add(newZone);
|
||||
try {
|
||||
newZone.getVolume().loadCorners();
|
||||
|
@ -25,7 +25,8 @@ public enum WarzoneConfig {
|
||||
SCOREBOARD (ScoreboardType.class),
|
||||
XPKILLMETER (Boolean.class),
|
||||
SOUPHEALING (Boolean.class),
|
||||
ALLOWENDER (Boolean.class);
|
||||
ALLOWENDER (Boolean.class),
|
||||
RESETBLOCKS (Boolean.class);
|
||||
|
||||
|
||||
private final Class<?> configType;
|
||||
|
@ -20,6 +20,7 @@ import org.bukkit.event.block.Action;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.bukkit.event.player.*;
|
||||
import org.bukkit.inventory.InventoryHolder;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.getspout.spoutapi.SpoutManager;
|
||||
import org.getspout.spoutapi.player.SpoutPlayer;
|
||||
@ -47,7 +48,6 @@ import java.util.logging.Level;
|
||||
|
||||
/**
|
||||
* @author tommytony, Tim Düsterhus
|
||||
* @package bukkit.tommytony.war
|
||||
*/
|
||||
public class WarPlayerListener implements Listener {
|
||||
private java.util.Random random = new java.util.Random();
|
||||
@ -56,7 +56,7 @@ public class WarPlayerListener implements Listener {
|
||||
/**
|
||||
* Correctly removes quitting players from warzones
|
||||
*
|
||||
* @see PlayerListener.onPlayerQuit()
|
||||
* @see PlayerQuitEvent
|
||||
*/
|
||||
@EventHandler
|
||||
public void onPlayerQuit(final PlayerQuitEvent event) {
|
||||
@ -155,7 +155,6 @@ public class WarPlayerListener implements Listener {
|
||||
player.getInventory().containsAtLeast(team.getKind().getBlockHead(), MINIMUM_TEAM_BLOCKS)) {
|
||||
// Can't pick up a second precious block
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -236,20 +235,26 @@ public class WarPlayerListener implements Listener {
|
||||
War.war.badMsg(player, "use.ender");
|
||||
}
|
||||
Team team = Team.getTeamByPlayerName(player.getName());
|
||||
if (team != null && event.getAction() == Action.RIGHT_CLICK_BLOCK && event.getClickedBlock().getType() == Material.ENCHANTMENT_TABLE && team.getTeamConfig().resolveBoolean(TeamConfig.XPKILLMETER)) {
|
||||
if (zone != null && team != null && event.getAction() == Action.RIGHT_CLICK_BLOCK && event.getClickedBlock().getType() == Material.ENCHANTMENT_TABLE && team.getTeamConfig().resolveBoolean(TeamConfig.XPKILLMETER)) {
|
||||
event.setCancelled(true);
|
||||
War.war.badMsg(player, "use.enchant");
|
||||
if (zone.getAuthors().contains(player.getName())) {
|
||||
War.war.badMsg(player, "use.xpkillmeter");
|
||||
}
|
||||
}
|
||||
if (team != null && event.getAction() == Action.RIGHT_CLICK_BLOCK && event.getClickedBlock().getType() == Material.ANVIL && team.getTeamConfig().resolveBoolean(TeamConfig.XPKILLMETER)) {
|
||||
if (zone != null && team != null && event.getAction() == Action.RIGHT_CLICK_BLOCK && event.getClickedBlock().getType() == Material.ANVIL && team.getTeamConfig().resolveBoolean(TeamConfig.XPKILLMETER)) {
|
||||
event.setCancelled(true);
|
||||
War.war.badMsg(player, "use.anvil");
|
||||
if (zone.getAuthors().contains(player.getName())) {
|
||||
War.war.badMsg(player, "use.xpkillmeter");
|
||||
}
|
||||
}
|
||||
if (zone != null && team != null && event.getAction() == Action.RIGHT_CLICK_BLOCK
|
||||
&& event.getClickedBlock().getState() instanceof InventoryHolder
|
||||
&& zone.isFlagThief(player.getName())) {
|
||||
event.setCancelled(true);
|
||||
War.war.badMsg(player, "drop.flag.disabled");
|
||||
}
|
||||
}
|
||||
|
||||
if (event.getAction() == Action.RIGHT_CLICK_BLOCK
|
||||
@ -337,7 +342,7 @@ public class WarPlayerListener implements Listener {
|
||||
if (locLobby != null && currentTeam == null && locLobby.isInAnyGate(playerLoc)) {
|
||||
Warzone zone = locLobby.getZone();
|
||||
Team locTeamGate = locLobby.getTeamGate(playerLoc);
|
||||
if (zone.getWarzoneConfig().getBoolean(WarzoneConfig.DISABLED)) {
|
||||
if (zone.getWarzoneConfig().getBoolean(WarzoneConfig.DISABLED) || zone.isReinitializing()) {
|
||||
War.war.badMsg(player, "join.disabled");
|
||||
event.setTo(zone.getTeleport());
|
||||
} else if (!zone.getWarzoneConfig().getBoolean(WarzoneConfig.JOINMIDBATTLE) && zone.isEnoughPlayers()) {
|
||||
@ -376,7 +381,7 @@ public class WarPlayerListener implements Listener {
|
||||
if (zone != null && zone.getTeleport() != null) {
|
||||
if (zone.getWarzoneConfig().getBoolean(WarzoneConfig.AUTOJOIN)
|
||||
&& zone.getTeams().size() >= 1 && currentTeam == null) {
|
||||
if (zone.getWarzoneConfig().getBoolean(WarzoneConfig.DISABLED)) {
|
||||
if (zone.getWarzoneConfig().getBoolean(WarzoneConfig.DISABLED) || zone.isReinitializing()) {
|
||||
War.war.badMsg(player, "join.disabled");
|
||||
event.setTo(hub.getLocation());
|
||||
} else if (!zone.getWarzoneConfig().getBoolean(WarzoneConfig.JOINMIDBATTLE) && zone.isEnoughPlayers()) {
|
||||
@ -787,7 +792,6 @@ public class WarPlayerListener implements Listener {
|
||||
Warzone zone = Warzone.getZoneByLocation(playerLoc);
|
||||
event.setTo(zone.getTeleport());
|
||||
War.war.badMsg(player, "zone.noteamnotice");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@ -803,13 +807,9 @@ public class WarPlayerListener implements Listener {
|
||||
List<Loadout> loadouts = new ArrayList<Loadout>(playerTeam.getInventories().resolveNewLoadouts());
|
||||
for (Iterator<Loadout> it = loadouts.iterator(); it.hasNext();) {
|
||||
Loadout ldt = it.next();
|
||||
if ("first".equals(ldt.getName())) {
|
||||
if (ldt.getName().equals("first") ||
|
||||
(ldt.requiresPermission() && !event.getPlayer().hasPermission(ldt.getPermission()))) {
|
||||
it.remove();
|
||||
continue;
|
||||
}
|
||||
if (ldt.requiresPermission() && !event.getPlayer().hasPermission(ldt.getPermission())) {
|
||||
it.remove();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
int currentIndex = (selection.getSelectedIndex() + 1) % loadouts.size();
|
||||
|
@ -1,12 +1,16 @@
|
||||
package com.tommytony.war.job;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.*;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import com.tommytony.war.mapper.ZoneVolumeMapper;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
@ -29,55 +33,82 @@ public class PartialZoneResetJob extends BukkitRunnable implements Cloneable {
|
||||
private int completed = 0;
|
||||
private final long startTime = System.currentTimeMillis();
|
||||
private long messageCounter = System.currentTimeMillis();
|
||||
boolean[][][] changes;
|
||||
public static final long MESSAGE_INTERVAL = 7500;
|
||||
// Ticks between job runs
|
||||
public static final int JOB_INTERVAL = 1;
|
||||
private int totalChanges = 0;
|
||||
private NumberFormat formatter = new DecimalFormat("#0.00");
|
||||
private Connection conn;
|
||||
|
||||
/**
|
||||
* Reset a warzone's blocks at a certain speed.
|
||||
*
|
||||
* @param volume
|
||||
* @param zone
|
||||
* Warzone to reset.
|
||||
* @param speed
|
||||
* Blocks to modify per #INTERVAL.
|
||||
*/
|
||||
public PartialZoneResetJob(Warzone zone, int speed) {
|
||||
public PartialZoneResetJob(Warzone zone, int speed) throws SQLException {
|
||||
this.zone = zone;
|
||||
this.volume = zone.getVolume();
|
||||
this.speed = speed;
|
||||
this.total = volume.size();
|
||||
this.total = volume.getTotalSavedBlocks();
|
||||
this.changes = new boolean[volume.getSizeX()][volume.getSizeY()][volume.getSizeZ()];
|
||||
}
|
||||
|
||||
@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);
|
||||
this.sendMessageToAllWarzonePlayers(message);
|
||||
if (conn == null || conn.isClosed()) {
|
||||
conn = ZoneVolumeMapper.getZoneConnection(volume, zone.getName(), volume.getWorld());
|
||||
}
|
||||
if (completed >= total) {
|
||||
int airChanges = 0;
|
||||
int minX = volume.getMinX(), minY = volume.getMinY(), minZ = volume.getMinZ();
|
||||
air: for (int x = volume.getMinX(); x <= volume.getMaxX(); x++) {
|
||||
for (int y = volume.getMinY(); y <= volume.getMaxY(); y++) {
|
||||
for (int z = volume.getMinZ(); z <= volume.getMaxZ(); z++) {
|
||||
int xi = x - minX, yi = y - minY, zi = z - minZ;
|
||||
if (!changes[xi][yi][zi]) {
|
||||
changes[xi][yi][zi] = true;
|
||||
airChanges++;
|
||||
BlockState state = volume.getWorld().getBlockAt(x, y, z).getState();
|
||||
if (state.getType() != Material.AIR) {
|
||||
state.setType(Material.AIR);
|
||||
state.update(true, false);
|
||||
}
|
||||
if (airChanges >= speed) {
|
||||
this.displayStatusMessage();
|
||||
break air;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
totalChanges += airChanges;
|
||||
if (this.doneAir()) {
|
||||
String secondsAsText = formatter.format(((double)(System.currentTimeMillis() - startTime)) / 1000);
|
||||
String message = MessageFormat.format(
|
||||
War.war.getString("zone.battle.resetcomplete"), secondsAsText);
|
||||
this.sendMessageToAllWarzonePlayers(message);
|
||||
PartialZoneResetJob.setSenderToNotify(zone, null); // stop notifying for this zone
|
||||
zone.initializeZone();
|
||||
War.war.getLogger().log(Level.INFO, "Finished reset cycle for warzone {0} (took {1} seconds)",
|
||||
new Object[]{volume.getName(), secondsAsText});
|
||||
conn.close();
|
||||
} else {
|
||||
War.war.getServer().getScheduler().runTaskLater(War.war, this.clone(), JOB_INTERVAL);
|
||||
}
|
||||
War.war.getServer().getScheduler()
|
||||
.runTaskLater(War.war, this.clone(), JOB_INTERVAL);
|
||||
} else {
|
||||
long seconds = (System.currentTimeMillis() - startTime) / 1000;
|
||||
String message = MessageFormat.format(
|
||||
War.war.getString("zone.battle.resetcomplete"), seconds);
|
||||
this.sendMessageToAllWarzonePlayers(message);
|
||||
PartialZoneResetJob.setSenderToNotify(zone, null); // stop notifying for this zone
|
||||
zone.initializeZone();
|
||||
War.war.getLogger().info(
|
||||
"Finished reset cycle for warzone " + volume.getName() + " (took " + seconds + " seconds)");
|
||||
int solidChanges = volume.resetSection(conn, completed, speed, changes);
|
||||
completed += solidChanges;
|
||||
totalChanges += solidChanges;
|
||||
this.displayStatusMessage();
|
||||
War.war.getServer().getScheduler().runTaskLater(War.war, this.clone(), JOB_INTERVAL);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
War.war.getLogger().log(Level.WARNING,
|
||||
"Failed to load zone during reset loop", e);
|
||||
War.war.getLogger().log(Level.WARNING, "Failed to load zone during reset loop", e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -96,6 +127,18 @@ public class PartialZoneResetJob extends BukkitRunnable implements Cloneable {
|
||||
}
|
||||
}
|
||||
|
||||
private void displayStatusMessage() {
|
||||
if (System.currentTimeMillis() - messageCounter > MESSAGE_INTERVAL) {
|
||||
String secondsAsText = formatter.format(((double)(System.currentTimeMillis() - startTime)) / 1000);
|
||||
messageCounter = System.currentTimeMillis();
|
||||
int percent = (int) (((double) totalChanges / (double) volume.size()) * 100);
|
||||
String message = MessageFormat.format(
|
||||
War.war.getString("zone.battle.resetprogress"),
|
||||
percent, secondsAsText);
|
||||
this.sendMessageToAllWarzonePlayers(message);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PartialZoneResetJob clone() {
|
||||
try {
|
||||
@ -108,4 +151,19 @@ public class PartialZoneResetJob extends BukkitRunnable implements Cloneable {
|
||||
public static void setSenderToNotify(Warzone warzone, CommandSender sender) {
|
||||
PartialZoneResetJob.sendersToNotify.put(warzone, sender);
|
||||
}
|
||||
|
||||
private boolean doneAir() {
|
||||
int minX = volume.getMinX(), minY = volume.getMinY(), minZ = volume.getMinZ();
|
||||
for (int x = volume.getMinX(); x <= volume.getMaxX(); x++) {
|
||||
for (int y = volume.getMinY(); y <= volume.getMaxY(); y++) {
|
||||
for (int z = volume.getMinZ(); z <= volume.getMaxZ(); z++) {
|
||||
int xi = x - minX, yi = y - minY, zi = z - minZ;
|
||||
if (!changes[xi][yi][zi]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -12,11 +12,9 @@ import com.tommytony.war.mapper.WarzoneYmlMapper;
|
||||
public class RestoreYmlWarzonesJob implements Runnable {
|
||||
|
||||
private final List<String> warzones;
|
||||
private final boolean newWarInstall;
|
||||
|
||||
public RestoreYmlWarzonesJob(List<String> warzones, boolean newWarInstall) {
|
||||
public RestoreYmlWarzonesJob(List<String> warzones) {
|
||||
this.warzones = warzones;
|
||||
this.newWarInstall = newWarInstall;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
@ -25,7 +23,7 @@ public class RestoreYmlWarzonesJob implements Runnable {
|
||||
for (String warzoneName : this.warzones) {
|
||||
if (warzoneName != null && !warzoneName.equals("")) {
|
||||
War.war.log("Loading zone " + warzoneName + "...", Level.INFO);
|
||||
Warzone zone = WarzoneYmlMapper.load(warzoneName, !this.newWarInstall);
|
||||
Warzone zone = WarzoneYmlMapper.load(warzoneName);
|
||||
if (zone != null) { // could have failed, would've been logged already
|
||||
War.war.getWarzones().add(zone);
|
||||
try {
|
||||
|
@ -48,7 +48,7 @@ public class WarYmlMapper {
|
||||
|
||||
// warzones
|
||||
List<String> warzones = warRootSection.getStringList("war.info.warzones");
|
||||
RestoreYmlWarzonesJob restoreWarzones = new RestoreYmlWarzonesJob(warzones, newWar); // during conversion, this should execute just after the RestoreTxtWarzonesJob
|
||||
RestoreYmlWarzonesJob restoreWarzones = new RestoreYmlWarzonesJob(warzones); // during conversion, this should execute just after the RestoreTxtWarzonesJob
|
||||
if (War.war.getServer().getScheduler().scheduleSyncDelayedTask(War.war, restoreWarzones) == -1) {
|
||||
War.war.log("Failed to schedule warzone-restore job. No warzone was loaded.", Level.WARNING);
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package com.tommytony.war.mapper;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
@ -32,7 +33,7 @@ import com.tommytony.war.volume.ZoneVolume;
|
||||
public class WarzoneYmlMapper {
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public static Warzone load(String name, boolean createNewVolume) {
|
||||
public static Warzone load(String name) { // removed createNewVolume, as it did nothing
|
||||
File warzoneTxtFile = new File(War.war.getDataFolder().getPath() + "/warzone-" + name + ".txt");
|
||||
File warzoneYmlFile = new File(War.war.getDataFolder().getPath() + "/warzone-" + name + ".yml");
|
||||
|
||||
@ -275,16 +276,16 @@ public class WarzoneYmlMapper {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (createNewVolume) {
|
||||
ZoneVolume zoneVolume = new ZoneVolume(warzone.getName(), world, warzone);
|
||||
warzone.setVolume(zoneVolume);
|
||||
Connection connection = null;
|
||||
try {
|
||||
connection = ZoneVolumeMapper.getZoneConnection(warzone.getVolume(), warzone.getName(), warzone.getWorld());
|
||||
} catch (SQLException e) {
|
||||
War.war.getLogger().log(Level.WARNING, "Failed to load warzone structures volume", e);
|
||||
}
|
||||
|
||||
// monument blocks
|
||||
for (Monument monument : warzone.getMonuments()) {
|
||||
try {
|
||||
monument.setVolume(VolumeMapper.loadVolume(monument.getName(), warzone.getName(), world));
|
||||
monument.setVolume(warzone.loadStructure(monument.getName(), connection));
|
||||
} catch (SQLException e) {
|
||||
War.war.getLogger().log(Level.WARNING, "Failed to load warzone structures volume", e);
|
||||
}
|
||||
@ -293,7 +294,7 @@ public class WarzoneYmlMapper {
|
||||
// bomb blocks
|
||||
for (Bomb bomb : warzone.getBombs()) {
|
||||
try {
|
||||
bomb.setVolume(VolumeMapper.loadVolume("bomb-" + bomb.getName(), warzone.getName(), world));
|
||||
bomb.setVolume(warzone.loadStructure("bomb-" + bomb.getName(), connection));
|
||||
} catch (SQLException e) {
|
||||
War.war.getLogger().log(Level.WARNING, "Failed to load warzone structures volume", e);
|
||||
}
|
||||
@ -302,7 +303,7 @@ public class WarzoneYmlMapper {
|
||||
// cake blocks
|
||||
for (Cake cake : warzone.getCakes()) {
|
||||
try {
|
||||
cake.setVolume(VolumeMapper.loadVolume("cake-" + cake.getName(), warzone.getName(), world));
|
||||
cake.setVolume(warzone.loadStructure("cake-" + cake.getName(), connection));
|
||||
} catch (SQLException e) {
|
||||
War.war.getLogger().log(Level.WARNING, "Failed to load warzone structures volume", e);
|
||||
}
|
||||
@ -312,14 +313,14 @@ public class WarzoneYmlMapper {
|
||||
for (Team team : warzone.getTeams()) {
|
||||
for (Location teamSpawn : team.getTeamSpawns()) {
|
||||
try {
|
||||
team.setSpawnVolume(teamSpawn, VolumeMapper.loadVolume(team.getName() + team.getTeamSpawns().indexOf(teamSpawn), warzone.getName(), world));
|
||||
team.setSpawnVolume(teamSpawn, warzone.loadStructure(team.getName() + team.getTeamSpawns().indexOf(teamSpawn), connection));
|
||||
} catch (SQLException e) {
|
||||
War.war.getLogger().log(Level.WARNING, "Failed to load warzone structures volume", e);
|
||||
}
|
||||
}
|
||||
if (team.getTeamFlag() != null) {
|
||||
try {
|
||||
team.setFlagVolume(VolumeMapper.loadVolume(team.getName() + "flag", warzone.getName(), world));
|
||||
team.setFlagVolume(warzone.loadStructure(team.getName() + "flag", connection));
|
||||
} catch (SQLException e) {
|
||||
War.war.getLogger().log(Level.WARNING, "Failed to load warzone structures volume", e);
|
||||
}
|
||||
@ -399,7 +400,7 @@ public class WarzoneYmlMapper {
|
||||
// create the lobby
|
||||
Volume lobbyVolume = null;
|
||||
try {
|
||||
lobbyVolume = VolumeMapper.loadVolume("lobby", warzone.getName(), lobbyWorld);
|
||||
lobbyVolume = warzone.loadStructure("lobby", lobbyWorld, connection);
|
||||
} catch (SQLException e) {
|
||||
War.war.getLogger().log(Level.WARNING, "Failed to load warzone lobby", e);
|
||||
}
|
||||
@ -443,6 +444,10 @@ public class WarzoneYmlMapper {
|
||||
(short) floorMaterialSection.getInt("data")));
|
||||
}
|
||||
}
|
||||
try {
|
||||
connection.close();
|
||||
} catch (SQLException ignored) {
|
||||
}
|
||||
|
||||
return warzone;
|
||||
}
|
||||
@ -649,11 +654,16 @@ public class WarzoneYmlMapper {
|
||||
flagSection.set("yaw", toIntYaw(teamFlag.getYaw()));
|
||||
}
|
||||
}
|
||||
|
||||
Connection connection = null;
|
||||
try {
|
||||
connection = ZoneVolumeMapper.getZoneConnection(warzone.getVolume(), warzone.getName(), warzone.getWorld());
|
||||
} catch (SQLException e) {
|
||||
War.war.getLogger().log(Level.WARNING, "Failed to load warzone structures volume", e);
|
||||
}
|
||||
// monument blocks
|
||||
for (Monument monument : warzone.getMonuments()) {
|
||||
try {
|
||||
VolumeMapper.save(monument.getVolume(), warzone.getName());
|
||||
ZoneVolumeMapper.saveStructure(monument.getVolume(), connection);
|
||||
} catch (SQLException e) {
|
||||
War.war.getLogger().log(Level.WARNING, "Failed to save warzone structures volume", e);
|
||||
}
|
||||
@ -662,7 +672,7 @@ public class WarzoneYmlMapper {
|
||||
// bomb blocks
|
||||
for (Bomb bomb : warzone.getBombs()) {
|
||||
try {
|
||||
VolumeMapper.save(bomb.getVolume(), warzone.getName());
|
||||
ZoneVolumeMapper.saveStructure(bomb.getVolume(), connection);
|
||||
} catch (SQLException e) {
|
||||
War.war.getLogger().log(Level.WARNING, "Failed to save warzone structures volume", e);
|
||||
}
|
||||
@ -671,7 +681,7 @@ public class WarzoneYmlMapper {
|
||||
// cake blocks
|
||||
for (Cake cake : warzone.getCakes()) {
|
||||
try {
|
||||
VolumeMapper.save(cake.getVolume(), warzone.getName());
|
||||
ZoneVolumeMapper.saveStructure(cake.getVolume(), connection);
|
||||
} catch (SQLException e) {
|
||||
War.war.getLogger().log(Level.WARNING, "Failed to save warzone structures volume", e);
|
||||
}
|
||||
@ -681,14 +691,14 @@ public class WarzoneYmlMapper {
|
||||
for (Team team : teams) {
|
||||
for (Volume volume : team.getSpawnVolumes().values()) {
|
||||
try {
|
||||
VolumeMapper.save(volume, warzone.getName());
|
||||
ZoneVolumeMapper.saveStructure(volume, connection);
|
||||
} catch (SQLException e) {
|
||||
War.war.getLogger().log(Level.WARNING, "Failed to save warzone structures volume", e);
|
||||
}
|
||||
}
|
||||
if (team.getFlagVolume() != null) {
|
||||
try {
|
||||
VolumeMapper.save(team.getFlagVolume(), warzone.getName());
|
||||
ZoneVolumeMapper.saveStructure(team.getFlagVolume(), connection);
|
||||
} catch (SQLException e) {
|
||||
War.war.getLogger().log(Level.WARNING, "Failed to save warzone structures volume", e);
|
||||
}
|
||||
@ -697,11 +707,15 @@ public class WarzoneYmlMapper {
|
||||
|
||||
if (warzone.getLobby() != null) {
|
||||
try {
|
||||
VolumeMapper.save(warzone.getLobby().getVolume(), warzone.getName());
|
||||
ZoneVolumeMapper.saveStructure(warzone.getLobby().getVolume(), connection);
|
||||
} catch (SQLException e) {
|
||||
War.war.getLogger().log(Level.WARNING, "Failed to save warzone structures volume", e);
|
||||
}
|
||||
}
|
||||
try {
|
||||
connection.close();
|
||||
} catch (SQLException ignored) {
|
||||
}
|
||||
|
||||
// Save to disk
|
||||
try {
|
||||
|
@ -7,7 +7,7 @@ import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.sql.Types;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
@ -46,29 +46,23 @@ import com.tommytony.war.volume.ZoneVolume;
|
||||
*/
|
||||
public class ZoneVolumeMapper {
|
||||
|
||||
public static final int DATABASE_VERSION = 1;
|
||||
public static final int DATABASE_VERSION = 2;
|
||||
|
||||
/**
|
||||
* Loads the given volume
|
||||
*
|
||||
* @param ZoneVolume volume Volume to load
|
||||
* @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
|
||||
* Get a connection to the warzone database, converting blocks if not found.
|
||||
* @param volume zone volume to load
|
||||
* @param zoneName warzone to load
|
||||
* @param world world containing this warzone
|
||||
* @return an open connection to the sqlite file
|
||||
* @throws SQLException
|
||||
*/
|
||||
public static int load(ZoneVolume volume, String zoneName, World world, boolean onlyLoadCorners, int start, int total) throws SQLException {
|
||||
int changed = 0;
|
||||
public static Connection getZoneConnection(ZoneVolume volume, String zoneName, World world) throws SQLException {
|
||||
File databaseFile = new File(War.war.getDataFolder(), String.format("/dat/warzone-%s/volume-%s.sl3", zoneName, volume.getName()));
|
||||
if (!databaseFile.exists()) {
|
||||
// Convert warzone to nimitz file format.
|
||||
changed = PreNimitzZoneVolumeMapper.load(volume, zoneName, world, onlyLoadCorners);
|
||||
PreNimitzZoneVolumeMapper.load(volume, zoneName, world, false);
|
||||
ZoneVolumeMapper.save(volume, zoneName);
|
||||
War.war.log("Warzone " + zoneName + " converted to nimitz format!", Level.INFO);
|
||||
return changed;
|
||||
}
|
||||
Connection databaseConnection = DriverManager.getConnection("jdbc:sqlite:" + databaseFile.getPath());
|
||||
Statement stmt = databaseConnection.createStatement();
|
||||
@ -76,17 +70,46 @@ public class ZoneVolumeMapper {
|
||||
int version = versionQuery.getInt("user_version");
|
||||
versionQuery.close();
|
||||
if (version > DATABASE_VERSION) {
|
||||
try {
|
||||
throw new IllegalStateException("Unsupported zone format " + version);
|
||||
} finally {
|
||||
stmt.close();
|
||||
databaseConnection.close();
|
||||
}
|
||||
stmt.close();
|
||||
databaseConnection.close();
|
||||
|
||||
// Can't load this too-recent format
|
||||
throw new IllegalStateException(String.format("Unsupported zone format (was already converted to version: %d, current format: %d)", version, DATABASE_VERSION));
|
||||
} else if (version < DATABASE_VERSION) {
|
||||
stmt.close();
|
||||
|
||||
// We need to migrate to newest schema
|
||||
switch (version) {
|
||||
// Run some update SQL for each old version
|
||||
case 1:
|
||||
// Run update from 1 to 2
|
||||
updateFromVersionOneToTwo(zoneName, databaseConnection);
|
||||
|
||||
// How to continue this pattern: (@tommytony multiple in one shouldn't be needed, just don't put a break in the switch)
|
||||
// case 2:
|
||||
// // Run update from 2 to 3
|
||||
// updateFromVersionTwoToTree(zoneName, databaseConnection);
|
||||
}
|
||||
|
||||
}
|
||||
return databaseConnection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the given volume
|
||||
*
|
||||
* @param databaseConnection Open connection to zone database
|
||||
* @param volume Volume to load
|
||||
* @param world The world the zone is located
|
||||
* @param onlyLoadCorners Should only the corners be loaded
|
||||
* @param start Starting position to load blocks at
|
||||
* @param total Amount of blocks to read
|
||||
* @return Changed blocks
|
||||
* @throws SQLException Error communicating with SQLite3 database
|
||||
*/
|
||||
public static int load(Connection databaseConnection, ZoneVolume volume, World world, boolean onlyLoadCorners, int start, int total, boolean[][][] changes) throws SQLException {
|
||||
Validate.isTrue(!databaseConnection.isClosed());
|
||||
Statement stmt = databaseConnection.createStatement();
|
||||
ResultSet cornerQuery = stmt.executeQuery("SELECT * FROM corners");
|
||||
cornerQuery.next();
|
||||
final Block corner1 = world.getBlockAt(cornerQuery.getInt("x"), cornerQuery.getInt("y"), cornerQuery.getInt("z"));
|
||||
@ -97,69 +120,190 @@ public class ZoneVolumeMapper {
|
||||
volume.setCornerTwo(corner2);
|
||||
if (onlyLoadCorners) {
|
||||
stmt.close();
|
||||
databaseConnection.close();
|
||||
return 0;
|
||||
}
|
||||
int minX = volume.getMinX(), minY = volume.getMinY(), minZ = volume.getMinZ();
|
||||
int changed = 0;
|
||||
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();
|
||||
changed++;
|
||||
Block relative = corner1.getRelative(x, y, z);
|
||||
int xi = relative.getX() - minX, yi = relative.getY() - minY, zi = relative.getZ() - minZ;
|
||||
if (changes != null) {
|
||||
changes[xi][yi][zi] = true;
|
||||
}
|
||||
BlockState modify = relative.getState();
|
||||
ItemStack data = new ItemStack(Material.valueOf(query.getString("type")), 0, query.getShort("data"));
|
||||
modify.setType(data.getType());
|
||||
modify.setData(data.getData());
|
||||
modify.update(true, false); // No-physics update, preventing the need for deferring blocks
|
||||
modify = corner1.getRelative(x, y, z).getState(); // Grab a new instance
|
||||
if (modify.getType() != data.getType() || !modify.getData().equals(data.getData())) {
|
||||
// Update the type & data if it has changed
|
||||
modify.setType(data.getType());
|
||||
modify.setData(data.getData());
|
||||
modify.update(true, false); // No-physics update, preventing the need for deferring blocks
|
||||
modify = corner1.getRelative(x, y, z).getState(); // Grab a new instance
|
||||
}
|
||||
if (query.getString("metadata") == null || query.getString("metadata").isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
if (modify instanceof Sign) {
|
||||
final String[] lines = query.getString("sign").split("\n");
|
||||
final String[] lines = query.getString("metadata").split("\n");
|
||||
for (int i = 0; i < lines.length; i++) {
|
||||
((Sign) modify).setLine(i, lines[i]);
|
||||
}
|
||||
modify.update(true, false);
|
||||
}
|
||||
if (modify instanceof InventoryHolder && query.getString("container") != null) {
|
||||
|
||||
// Containers
|
||||
if (modify instanceof InventoryHolder) {
|
||||
YamlConfiguration config = new YamlConfiguration();
|
||||
config.loadFromString(query.getString("container"));
|
||||
config.loadFromString(query.getString("metadata"));
|
||||
((InventoryHolder) modify).getInventory().clear();
|
||||
for (Object obj : config.getList("items")) {
|
||||
if (obj instanceof ItemStack) {
|
||||
((InventoryHolder) modify).getInventory().addItem((ItemStack) obj);
|
||||
}
|
||||
}
|
||||
modify.update(true, false);
|
||||
}
|
||||
|
||||
// Notes
|
||||
if (modify instanceof NoteBlock) {
|
||||
String[] split = query.getString("note").split("\n");
|
||||
String[] split = query.getString("metadata").split("\n");
|
||||
Note note = new Note(Integer.parseInt(split[1]), Tone.valueOf(split[0]), Boolean.parseBoolean(split[2]));
|
||||
((NoteBlock) modify).setNote(note);
|
||||
modify.update(true, false);
|
||||
}
|
||||
|
||||
// Records
|
||||
if (modify instanceof Jukebox) {
|
||||
((Jukebox) modify).setPlaying(Material.valueOf(query.getString("record")));
|
||||
((Jukebox) modify).setPlaying(Material.valueOf(query.getString("metadata")));
|
||||
modify.update(true, false);
|
||||
}
|
||||
if (modify instanceof Skull && query.getString("skull") != null) {
|
||||
String[] opts = query.getString("skull").split("\n");
|
||||
|
||||
// Skulls
|
||||
if (modify instanceof Skull) {
|
||||
String[] opts = query.getString("metadata").split("\n");
|
||||
((Skull) modify).setOwner(opts[0]);
|
||||
((Skull) modify).setSkullType(SkullType.valueOf(opts[1]));
|
||||
((Skull) modify).setRotation(BlockFace.valueOf(opts[2]));
|
||||
modify.update(true, false);
|
||||
}
|
||||
if (modify instanceof CommandBlock && query.getString("command") != null) {
|
||||
final String[] commandArray = query.getString("command").split("\n");
|
||||
|
||||
// Command blocks
|
||||
if (modify instanceof CommandBlock) {
|
||||
final String[] commandArray = query.getString("metadata").split("\n");
|
||||
((CommandBlock) modify).setName(commandArray[0]);
|
||||
((CommandBlock) modify).setCommand(commandArray[1]);
|
||||
modify.update(true, false);
|
||||
}
|
||||
|
||||
// Creature spawner
|
||||
if (modify instanceof CreatureSpawner) {
|
||||
((CreatureSpawner) modify).setSpawnedType(EntityType.valueOf(query.getString("mobid")));
|
||||
((CreatureSpawner) modify).setSpawnedType(EntityType.valueOf(query.getString("metadata")));
|
||||
modify.update(true, false);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
War.war.getLogger().log(Level.WARNING, "Exception loading some tile data", ex);
|
||||
War.war.getLogger().log(Level.WARNING, "Exception loading some tile data. x:" + x + " y:" + y + " z:" + z + " type:" + modify.getType().toString() + " data:" + modify.getData().toString(), ex);
|
||||
}
|
||||
modify.update(true, false);
|
||||
changed++;
|
||||
}
|
||||
query.close();
|
||||
stmt.close();
|
||||
databaseConnection.close();
|
||||
return changed;
|
||||
}
|
||||
|
||||
public static int saveStructure(Volume volume, Connection databaseConnection) throws SQLException {
|
||||
Statement stmt = databaseConnection.createStatement();
|
||||
stmt.executeUpdate("PRAGMA user_version = " + DATABASE_VERSION);
|
||||
// Storing zonemaker-inputted name could result in injection or undesirable behavior.
|
||||
String prefix = String.format("structure_%d", volume.getName().hashCode() & Integer.MAX_VALUE);
|
||||
stmt.executeUpdate("CREATE TABLE IF NOT EXISTS " + prefix +
|
||||
"_blocks (x BIGINT, y BIGINT, z BIGINT, type TEXT, data SMALLINT)");
|
||||
stmt.executeUpdate("CREATE TABLE IF NOT EXISTS " + prefix +
|
||||
"_corners (pos INTEGER PRIMARY KEY NOT NULL UNIQUE, x INTEGER NOT NULL, y INTEGER NOT NULL, z INTEGER NOT NULL)");
|
||||
stmt.executeUpdate("DELETE FROM " + prefix + "_blocks");
|
||||
stmt.executeUpdate("DELETE FROM " + prefix + "_corners");
|
||||
stmt.close();
|
||||
PreparedStatement cornerStmt = databaseConnection
|
||||
.prepareStatement("INSERT INTO " + prefix + "_corners SELECT 1 AS pos, ? AS x, ? AS y, ? AS z UNION SELECT 2, ?, ?, ?");
|
||||
cornerStmt.setInt(1, volume.getCornerOne().getBlockX());
|
||||
cornerStmt.setInt(2, volume.getCornerOne().getBlockY());
|
||||
cornerStmt.setInt(3, volume.getCornerOne().getBlockZ());
|
||||
cornerStmt.setInt(4, volume.getCornerTwo().getBlockX());
|
||||
cornerStmt.setInt(5, volume.getCornerTwo().getBlockY());
|
||||
cornerStmt.setInt(6, volume.getCornerTwo().getBlockZ());
|
||||
cornerStmt.executeUpdate();
|
||||
cornerStmt.close();
|
||||
PreparedStatement dataStmt = databaseConnection
|
||||
.prepareStatement("INSERT INTO " + prefix + "_blocks VALUES (?, ?, ?, ?, ?)");
|
||||
databaseConnection.setAutoCommit(false);
|
||||
final int batchSize = 1000;
|
||||
int changed = 0;
|
||||
for (BlockState block : volume.getBlocks()) {
|
||||
final Location relLoc = ZoneVolumeMapper.rebase(
|
||||
volume.getCornerOne(), block.getLocation());
|
||||
dataStmt.setInt(1, relLoc.getBlockX());
|
||||
dataStmt.setInt(2, relLoc.getBlockY());
|
||||
dataStmt.setInt(3, relLoc.getBlockZ());
|
||||
dataStmt.setString(4, block.getType().toString());
|
||||
dataStmt.setShort(5, block.getData().toItemStack().getDurability());
|
||||
dataStmt.addBatch();
|
||||
if (++changed % batchSize == 0) {
|
||||
dataStmt.executeBatch();
|
||||
}
|
||||
}
|
||||
dataStmt.executeBatch(); // insert remaining records
|
||||
databaseConnection.commit();
|
||||
databaseConnection.setAutoCommit(true);
|
||||
dataStmt.close();
|
||||
return changed;
|
||||
}
|
||||
|
||||
public static void loadStructure(Volume volume, Connection databaseConnection) throws SQLException {
|
||||
String prefix = String.format("structure_%d", volume.getName().hashCode() & Integer.MAX_VALUE);
|
||||
World world = volume.getWorld();
|
||||
Statement stmt = databaseConnection.createStatement();
|
||||
ResultSet cornerQuery = stmt.executeQuery("SELECT * FROM " + prefix + "_corners");
|
||||
cornerQuery.next();
|
||||
final Block corner1 = world.getBlockAt(cornerQuery.getInt("x"), cornerQuery.getInt("y"), cornerQuery.getInt("z"));
|
||||
cornerQuery.next();
|
||||
final Block corner2 = world.getBlockAt(cornerQuery.getInt("x"), cornerQuery.getInt("y"), cornerQuery.getInt("z"));
|
||||
cornerQuery.close();
|
||||
volume.setCornerOne(corner1);
|
||||
volume.setCornerTwo(corner2);
|
||||
volume.getBlocks().clear();
|
||||
ResultSet query = stmt.executeQuery("SELECT * FROM " + prefix + "_blocks");
|
||||
while (query.next()) {
|
||||
int x = query.getInt("x"), y = query.getInt("y"), z = query.getInt("z");
|
||||
BlockState modify = corner1.getRelative(x, y, z).getState();
|
||||
ItemStack data = new ItemStack(Material.valueOf(query.getString("type")), 0, query.getShort("data"));
|
||||
modify.setType(data.getType());
|
||||
modify.setData(data.getData());
|
||||
volume.getBlocks().add(modify);
|
||||
}
|
||||
query.close();
|
||||
stmt.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get total saved blocks for a warzone. This should only be called on nimitz-format warzones.
|
||||
* @param volume Warzone volume
|
||||
* @param zoneName Name of zone file
|
||||
* @return Total saved blocks
|
||||
* @throws SQLException
|
||||
*/
|
||||
public static int getTotalSavedBlocks(ZoneVolume volume, String zoneName) throws SQLException {
|
||||
File databaseFile = new File(War.war.getDataFolder(), String.format("/dat/warzone-%s/volume-%s.sl3", zoneName, volume.getName()));
|
||||
Connection databaseConnection = DriverManager.getConnection("jdbc:sqlite:" + databaseFile.getPath());
|
||||
Statement stmt = databaseConnection.createStatement();
|
||||
ResultSet sizeQuery = stmt.executeQuery("SELECT COUNT(*) AS total FROM blocks");
|
||||
int size = sizeQuery.getInt("total");
|
||||
sizeQuery.close();
|
||||
stmt.close();
|
||||
databaseConnection.close();
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save all war zone blocks to a SQLite3 database file.
|
||||
*
|
||||
@ -169,14 +313,17 @@ public class ZoneVolumeMapper {
|
||||
* @throws SQLException
|
||||
*/
|
||||
public static int save(Volume volume, String zoneName) throws SQLException {
|
||||
long startTime = System.currentTimeMillis();
|
||||
int changed = 0;
|
||||
File warzoneDir = new File(War.war.getDataFolder().getPath() + "/dat/warzone-" + zoneName);
|
||||
warzoneDir.mkdirs();
|
||||
if (!warzoneDir.exists() && !warzoneDir.mkdirs()) {
|
||||
throw new RuntimeException("Failed to create warzone data directory");
|
||||
}
|
||||
File databaseFile = new File(War.war.getDataFolder(), String.format("/dat/warzone-%s/volume-%s.sl3", zoneName, volume.getName()));
|
||||
Connection databaseConnection = DriverManager.getConnection("jdbc:sqlite:" + databaseFile.getPath());
|
||||
Statement stmt = databaseConnection.createStatement();
|
||||
stmt.executeUpdate("PRAGMA user_version = " + DATABASE_VERSION);
|
||||
stmt.executeUpdate("CREATE TABLE IF NOT EXISTS blocks (x BIGINT, y BIGINT, z BIGINT, type TEXT, data SMALLINT, sign TEXT, container BLOB, note INT, record TEXT, skull TEXT, command TEXT, mobid TEXT)");
|
||||
stmt.executeUpdate("CREATE TABLE IF NOT EXISTS blocks (x BIGINT, y BIGINT, z BIGINT, type TEXT, data SMALLINT, metadata BLOB)");
|
||||
stmt.executeUpdate("CREATE TABLE IF NOT EXISTS corners (pos INTEGER PRIMARY KEY NOT NULL UNIQUE, x INTEGER NOT NULL, y INTEGER NOT NULL, z INTEGER NOT NULL)");
|
||||
stmt.executeUpdate("DELETE FROM blocks");
|
||||
stmt.executeUpdate("DELETE FROM corners");
|
||||
@ -190,67 +337,57 @@ public class ZoneVolumeMapper {
|
||||
cornerStmt.setInt(6, volume.getCornerTwo().getBlockZ());
|
||||
cornerStmt.executeUpdate();
|
||||
cornerStmt.close();
|
||||
PreparedStatement dataStmt = databaseConnection.prepareStatement("INSERT INTO blocks VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
||||
PreparedStatement dataStmt = databaseConnection.prepareStatement("INSERT INTO blocks (x, y, z, type, data, metadata) VALUES (?, ?, ?, ?, ?, ?)");
|
||||
databaseConnection.setAutoCommit(false);
|
||||
final int batchSize = 1000;
|
||||
final int batchSize = 10000;
|
||||
for (int i = 0, x = volume.getMinX(); i < volume.getSizeX(); i++, x++) {
|
||||
for (int j = 0, y = volume.getMinY(); j < volume.getSizeY(); j++, y++) {
|
||||
for (int k = 0, z = volume.getMinZ(); k < volume.getSizeZ(); k++, z++) {
|
||||
// Make sure we are using zone volume-relative coords
|
||||
final Block block = volume.getWorld().getBlockAt(x, y, z);
|
||||
final Location relLoc = rebase(volume.getCornerOne(), block.getLocation());
|
||||
dataStmt.setInt(1, relLoc.getBlockX());
|
||||
dataStmt.setInt(2, relLoc.getBlockY());
|
||||
dataStmt.setInt(3, relLoc.getBlockZ());
|
||||
dataStmt.setString(4, block.getType().toString());
|
||||
dataStmt.setShort(5, block.getState().getData().toItemStack().getDurability());
|
||||
if (block.getState() instanceof Sign) {
|
||||
if (block.getType() == Material.AIR) {
|
||||
continue; // Do not save air blocks to the file anymore.
|
||||
}
|
||||
final BlockState state = block.getState();
|
||||
dataStmt.setInt(1, block.getX() - volume.getCornerOne().getBlockX());
|
||||
dataStmt.setInt(2, block.getY() - volume.getCornerOne().getBlockY());
|
||||
dataStmt.setInt(3, block.getZ() - volume.getCornerOne().getBlockZ());
|
||||
dataStmt.setString(4, block.getType().name());
|
||||
dataStmt.setShort(5, state.getData().toItemStack().getDurability());
|
||||
if (state instanceof Sign) {
|
||||
final String signText = StringUtils.join(((Sign) block.getState()).getLines(), "\n");
|
||||
dataStmt.setString(6, signText);
|
||||
} else {
|
||||
dataStmt.setNull(6, Types.VARCHAR);
|
||||
}
|
||||
if (block.getState() instanceof InventoryHolder) {
|
||||
} else if (state instanceof InventoryHolder) {
|
||||
List<ItemStack> items = Arrays.asList(((InventoryHolder) block.getState()).getInventory().getContents());
|
||||
YamlConfiguration config = new YamlConfiguration();
|
||||
// Serialize to config, then store config in database
|
||||
config.set("items", items);
|
||||
dataStmt.setString(7, config.saveToString());
|
||||
} else {
|
||||
dataStmt.setNull(7, Types.BLOB);
|
||||
}
|
||||
if (block.getState() instanceof NoteBlock) {
|
||||
dataStmt.setString(6, config.saveToString());
|
||||
} else if (state instanceof NoteBlock) {
|
||||
Note note = ((NoteBlock) block.getState()).getNote();
|
||||
dataStmt.setString(8, note.getTone().toString() + '\n' + note.getOctave() + '\n' + note.isSharped());
|
||||
} else {
|
||||
dataStmt.setNull(8, Types.VARCHAR);
|
||||
}
|
||||
if (block.getState() instanceof Jukebox) {
|
||||
dataStmt.setString(9, ((Jukebox) block.getState()).getPlaying().toString());
|
||||
} else {
|
||||
dataStmt.setNull(9, Types.VARCHAR);
|
||||
}
|
||||
if (block.getState() instanceof Skull) {
|
||||
dataStmt.setString(10, String.format("%s\n%s\n%s",
|
||||
dataStmt.setString(6, note.getTone().toString() + '\n' + note.getOctave() + '\n' + note.isSharped());
|
||||
} else if (state instanceof Jukebox) {
|
||||
dataStmt.setString(6, ((Jukebox) block.getState()).getPlaying().toString());
|
||||
} else if (state instanceof Skull) {
|
||||
dataStmt.setString(6, String.format("%s\n%s\n%s",
|
||||
((Skull) block.getState()).getOwner(),
|
||||
((Skull) block.getState()).getSkullType().toString(),
|
||||
((Skull) block.getState()).getRotation().toString()));
|
||||
} else {
|
||||
dataStmt.setNull(10, Types.VARCHAR);
|
||||
}
|
||||
if (block.getState() instanceof CommandBlock) {
|
||||
dataStmt.setString(11, ((CommandBlock) block.getState()).getName()
|
||||
} else if (state instanceof CommandBlock) {
|
||||
dataStmt.setString(6, ((CommandBlock) block.getState()).getName()
|
||||
+ "\n" + ((CommandBlock) block.getState()).getCommand());
|
||||
} else {
|
||||
dataStmt.setNull(11, Types.VARCHAR);
|
||||
}
|
||||
if (block.getState() instanceof CreatureSpawner) {
|
||||
dataStmt.setString(12, ((CreatureSpawner) block.getState()).getSpawnedType().toString());
|
||||
} else {
|
||||
dataStmt.setNull(12, Types.VARCHAR);
|
||||
} else if (state instanceof CreatureSpawner) {
|
||||
dataStmt.setString(6, ((CreatureSpawner) block.getState()).getSpawnedType().toString());
|
||||
}
|
||||
|
||||
dataStmt.addBatch();
|
||||
|
||||
if (++changed % batchSize == 0) {
|
||||
dataStmt.executeBatch();
|
||||
if ((System.currentTimeMillis() - startTime) >= 5000L) {
|
||||
String seconds = new DecimalFormat("#0.00").format((double) (System.currentTimeMillis() - startTime) / 1000.0D);
|
||||
War.war.getLogger().log(Level.FINE, "Still saving warzone {0} , {1} seconds elapsed.", new Object[] {zoneName, seconds});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -259,15 +396,49 @@ public class ZoneVolumeMapper {
|
||||
databaseConnection.commit();
|
||||
dataStmt.close();
|
||||
databaseConnection.close();
|
||||
String seconds = new DecimalFormat("#0.00").format((double) (System.currentTimeMillis() - startTime) / 1000.0D);
|
||||
War.war.getLogger().log(Level.INFO, "Saved warzone {0} in {1} seconds.", new Object[] {zoneName, seconds});
|
||||
return changed;
|
||||
}
|
||||
|
||||
public static Location rebase(final Location base, final Location exact) {
|
||||
Validate.isTrue(base.getWorld().equals(exact.getWorld()),
|
||||
"Locations must be in the same world");
|
||||
return new Location(base.getWorld(),
|
||||
exact.getBlockX() - base.getBlockX(),
|
||||
exact.getBlockY() - base.getBlockY(),
|
||||
exact.getBlockZ() - base.getBlockZ());
|
||||
}
|
||||
|
||||
private static void updateFromVersionOneToTwo(String zoneName, Connection connection) throws SQLException {
|
||||
War.war.log("Migrating warzone " + zoneName + " from v1 to v2 of schema...", Level.INFO);
|
||||
|
||||
// We want to do this in a transaction
|
||||
connection.setAutoCommit(false);
|
||||
|
||||
Statement stmt = connection.createStatement();
|
||||
|
||||
// We want to combine all extra columns into a single metadata BLOB one. To delete some columns we need to drop the table so we use a temp one.
|
||||
stmt.executeUpdate("CREATE TEMPORARY TABLE blocks_backup(x BIGINT, y BIGINT, z BIGINT, type TEXT, data SMALLINT, sign TEXT, container BLOB, note INT, record TEXT, skull TEXT, command TEXT, mobid TEXT)");
|
||||
stmt.executeUpdate("INSERT INTO blocks_backup SELECT x, y, z, type, data, sign, container, note, record, skull, command, mobid FROM blocks");
|
||||
stmt.executeUpdate("DROP TABLE blocks");
|
||||
stmt.executeUpdate("CREATE TABLE blocks(x BIGINT, y BIGINT, z BIGINT, type TEXT, data SMALLINT, metadata BLOB)");
|
||||
stmt.executeUpdate("INSERT INTO blocks SELECT x, y, z, type, data, coalesce(container, sign, note, record, skull, command, mobid) FROM blocks_backup");
|
||||
stmt.executeUpdate("DROP TABLE blocks_backup");
|
||||
stmt.executeUpdate("PRAGMA user_version = 2");
|
||||
stmt.close();
|
||||
|
||||
// Commit transaction
|
||||
connection.commit();
|
||||
|
||||
connection.setAutoCommit(true);
|
||||
|
||||
War.war.log("Warzone " + zoneName + " converted! Compacting database...", Level.INFO);
|
||||
|
||||
// Pack the database otherwise we won't get any space savings.
|
||||
// This rebuilds the database completely and takes some time.
|
||||
stmt = connection.createStatement();
|
||||
stmt.execute("VACUUM");
|
||||
stmt.close();
|
||||
|
||||
War.war.log("Migration of warzone " + zoneName + " to v2 of schema finished.", Level.INFO);
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ public class WarLogFormatter extends Formatter {
|
||||
b.append(" [");
|
||||
b.append(arg0.getLevel());
|
||||
b.append("] ");
|
||||
b.append(arg0.getMessage());
|
||||
b.append(formatMessage(arg0));
|
||||
b.append(System.getProperty("line.separator"));
|
||||
return b.toString();
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.tommytony.war.volume;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.logging.Level;
|
||||
|
||||
@ -50,35 +51,47 @@ public class ZoneVolume extends Volume {
|
||||
}
|
||||
|
||||
public void loadCorners() throws SQLException {
|
||||
ZoneVolumeMapper.load(this, this.zone.getName(), this.getWorld(), true, 0, 0);
|
||||
Connection conn = ZoneVolumeMapper.getZoneConnection(this, this.zone.getName(), this.getWorld());
|
||||
ZoneVolumeMapper.load(conn, this, this.getWorld(), true, 0, 0, null);
|
||||
this.isSaved = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetBlocks() {
|
||||
// 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, 0, Integer.MAX_VALUE);
|
||||
Connection conn = ZoneVolumeMapper.getZoneConnection(this, this.zone.getName(), this.getWorld());
|
||||
ZoneVolumeMapper.load(conn, this, this.getWorld(), false, 0, Integer.MAX_VALUE, null);
|
||||
} catch (SQLException ex) {
|
||||
War.war.log("Failed to load warzone " + zone.getName() + ": " + ex.getMessage(), Level.WARNING);
|
||||
ex.printStackTrace();
|
||||
}
|
||||
War.war.log("Reset " + reset + " blocks in warzone " + this.zone.getName() + ".", java.util.logging.Level.INFO);
|
||||
War.war.log("Reset warzone " + this.zone.getName() + ".", java.util.logging.Level.INFO);
|
||||
this.isSaved = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset a section of blocks in the warzone.
|
||||
*
|
||||
* @param conn Open connection to warzone database file.
|
||||
* @param start
|
||||
* Starting position for reset.
|
||||
* @param total
|
||||
* Amount of blocks to reset.
|
||||
* @return Changed block count.
|
||||
* @throws SQLException
|
||||
*/
|
||||
public void resetSection(int start, int total) throws SQLException {
|
||||
ZoneVolumeMapper.load(this, this.zone.getName(), this.getWorld(), false, start, total);
|
||||
public int resetSection(Connection conn, int start, int total, boolean[][][] changes) throws SQLException {
|
||||
return ZoneVolumeMapper.load(conn, this, this.getWorld(), false, start, total, changes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get total saved blocks for this warzone. This should only be called on nimitz-format warzones.
|
||||
* @return Total saved blocks
|
||||
* @throws SQLException
|
||||
*/
|
||||
public int getTotalSavedBlocks() throws SQLException {
|
||||
return ZoneVolumeMapper.getTotalSavedBlocks(this, this.zone.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -87,9 +100,12 @@ public class ZoneVolume extends Volume {
|
||||
* 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);
|
||||
try {
|
||||
PartialZoneResetJob job = new PartialZoneResetJob(zone, War.war.getWarConfig().getInt(WarConfig.RESETSPEED));
|
||||
War.war.getServer().getScheduler().runTask(War.war, job);
|
||||
} catch (SQLException e) {
|
||||
War.war.getLogger().log(Level.WARNING, "Failed to reset warzone - cannot get count of saved blocks", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void setNorthwest(Location block) throws NotNorthwestException, TooSmallException, TooBigException {
|
||||
@ -239,10 +255,7 @@ public class ZoneVolume extends Volume {
|
||||
|
||||
private static final int MIN_SIZE = 10;
|
||||
public boolean tooSmall() {
|
||||
if (!this.hasTwoCorners()) {
|
||||
return false;
|
||||
}
|
||||
return this.getSizeX() < MIN_SIZE || this.getSizeY() < MIN_SIZE || this.getSizeZ() < MIN_SIZE;
|
||||
return this.hasTwoCorners() && (this.getSizeX() < MIN_SIZE || this.getSizeY() < MIN_SIZE || this.getSizeZ() < MIN_SIZE);
|
||||
}
|
||||
|
||||
private static final int MAX_SIZE_DEFAULT = 750;
|
||||
|
Loading…
Reference in New Issue
Block a user