mirror of https://github.com/taoneill/war.git
Merge pull request #846 from taoneill/feature/113savefiles
Zone database structure update
This commit is contained in:
commit
3470a0cc54
|
@ -18,6 +18,7 @@ import net.milkbowl.vault.economy.Economy;
|
|||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.enchantments.Enchantment;
|
||||
|
@ -50,10 +51,10 @@ public class War extends JavaPlugin {
|
|||
static final boolean HIDE_BLANK_MESSAGES = true;
|
||||
public static War war;
|
||||
private static ResourceBundle messages = ResourceBundle.getBundle("messages");
|
||||
private final List<String> zoneMakerNames = new ArrayList<String>();
|
||||
private final List<OfflinePlayer> zoneMakerNames = new ArrayList<>();
|
||||
private final List<String> commandWhitelist = new ArrayList<String>();
|
||||
private final List<Warzone> incompleteZones = new ArrayList<Warzone>();
|
||||
private final List<String> zoneMakersImpersonatingPlayers = new ArrayList<String>();
|
||||
private final List<OfflinePlayer> zoneMakersImpersonatingPlayers = new ArrayList<>();
|
||||
private final HashMap<String, String> wandBearers = new HashMap<String, String>(); // playername to zonename
|
||||
private final List<String> deadlyAdjectives = new ArrayList<String>();
|
||||
private final List<String> killerVerbs = new ArrayList<String>();
|
||||
|
@ -221,7 +222,6 @@ public class War extends JavaPlugin {
|
|||
this.getDefaultInventories().setReward(reward);
|
||||
|
||||
this.getCommandWhitelist().add("who");
|
||||
this.getZoneMakerNames().add("tommytony");
|
||||
this.setKillstreakReward(new KillstreakReward());
|
||||
this.setMysqlConfig(new MySQLConfig());
|
||||
|
||||
|
@ -1066,14 +1066,14 @@ public class War extends JavaPlugin {
|
|||
*/
|
||||
public boolean isZoneMaker(Player player) {
|
||||
// sort out disguised first
|
||||
for (String disguised : this.zoneMakersImpersonatingPlayers) {
|
||||
if (disguised.equals(player.getName())) {
|
||||
for (OfflinePlayer disguised : this.zoneMakersImpersonatingPlayers) {
|
||||
if (disguised.isOnline() && disguised.getPlayer().equals(player)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (String zoneMaker : this.zoneMakerNames) {
|
||||
if (zoneMaker.equals(player.getName())) {
|
||||
for (OfflinePlayer zoneMaker : this.zoneMakerNames) {
|
||||
if (zoneMaker.isOnline() && zoneMaker.getPlayer().equals(player)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -1107,7 +1107,7 @@ public class War extends JavaPlugin {
|
|||
// lost his sword, or new warzone
|
||||
if (zoneName.equals(alreadyHaveWand)) {
|
||||
// same zone, give him a new sword
|
||||
player.getInventory().addItem(new ItemStack(Material.WOODEN_SWORD, 1, (byte) 8));
|
||||
player.getInventory().addItem(Compat.createDamagedIS(Material.WOODEN_SWORD, 1, 8));
|
||||
this.msg(player, "Here's a new sword for zone " + zoneName + ".");
|
||||
}
|
||||
}
|
||||
|
@ -1116,8 +1116,7 @@ public class War extends JavaPlugin {
|
|||
this.badMsg(player, "Your inventory is full. Please drop an item and try again.");
|
||||
} else {
|
||||
this.wandBearers.put(player.getName(), zoneName);
|
||||
player.getInventory().addItem(new ItemStack(Material.WOODEN_SWORD, 1, (byte) 8));
|
||||
// player.getWorld().dropItem(player.getLocation(), new ItemStack(Material.WOOD_SWORD));
|
||||
player.getInventory().addItem(Compat.createDamagedIS(Material.WOODEN_SWORD, 1, 8));
|
||||
this.msg(player, "You now have a wand for zone " + zoneName + ". Left-click with wooden sword for corner 1. Right-click for corner 2.");
|
||||
War.war.log(player.getName() + " now has a wand for warzone " + zoneName, Level.INFO);
|
||||
}
|
||||
|
@ -1136,10 +1135,8 @@ public class War extends JavaPlugin {
|
|||
}
|
||||
|
||||
public void removeWandBearer(Player player) {
|
||||
if (this.wandBearers.containsKey(player.getName())) {
|
||||
this.wandBearers.remove(player.getName());
|
||||
}
|
||||
}
|
||||
|
||||
public Warzone zoneOfZoneWallAtProximity(Location location) {
|
||||
for (Warzone zone : this.warzones) {
|
||||
|
@ -1150,7 +1147,7 @@ public class War extends JavaPlugin {
|
|||
return null;
|
||||
}
|
||||
|
||||
public List<String> getZoneMakerNames() {
|
||||
public List<OfflinePlayer> getZoneMakerNames() {
|
||||
return this.zoneMakerNames;
|
||||
}
|
||||
|
||||
|
@ -1162,7 +1159,7 @@ public class War extends JavaPlugin {
|
|||
return ZoneLobby.getLobbyByLocation(location) != null;
|
||||
}
|
||||
|
||||
public List<String> getZoneMakersImpersonatingPlayers() {
|
||||
public List<OfflinePlayer> getZoneMakersImpersonatingPlayers() {
|
||||
return this.zoneMakersImpersonatingPlayers;
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,6 @@ import com.tommytony.war.job.LogKillsDeathsJob;
|
|||
import com.tommytony.war.job.LogKillsDeathsJob.KillsDeathsRecord;
|
||||
import com.tommytony.war.job.ZoneTimeJob;
|
||||
import com.tommytony.war.mapper.LoadoutYmlMapper;
|
||||
import com.tommytony.war.mapper.VolumeMapper;
|
||||
import com.tommytony.war.mapper.ZoneVolumeMapper;
|
||||
import com.tommytony.war.structure.*;
|
||||
import com.tommytony.war.utility.*;
|
||||
|
@ -2087,7 +2086,7 @@ public class Warzone {
|
|||
}
|
||||
|
||||
public Volume loadStructure(String volName, World world) throws SQLException {
|
||||
return loadStructure(volName, world, ZoneVolumeMapper.getZoneConnection(volume, name, world));
|
||||
return loadStructure(volName, world, ZoneVolumeMapper.getZoneConnection(volume, name));
|
||||
}
|
||||
|
||||
public Volume loadStructure(String volName, Connection zoneConnection) throws SQLException {
|
||||
|
@ -2096,12 +2095,6 @@ public class Warzone {
|
|||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
package com.tommytony.war.command;
|
||||
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
|
||||
import com.tommytony.war.War;
|
||||
import com.tommytony.war.Warzone;
|
||||
import com.tommytony.war.mapper.VolumeMapper;
|
||||
import com.tommytony.war.mapper.WarYmlMapper;
|
||||
import com.tommytony.war.structure.WarHub;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import java.util.logging.Level;
|
||||
|
||||
/**
|
||||
* Deletes the warhub.
|
||||
|
@ -30,7 +28,7 @@ public class DeleteWarhubCommand extends AbstractWarAdminCommand {
|
|||
if (War.war.getWarHub() != null) {
|
||||
// reset existing hub
|
||||
War.war.getWarHub().getVolume().resetBlocks();
|
||||
VolumeMapper.delete(War.war.getWarHub().getVolume());
|
||||
VolumeMapper.deleteSimpleVolume(War.war.getWarHub().getVolume());
|
||||
War.war.setWarHub((WarHub) null);
|
||||
for (Warzone zone : War.war.getWarzones()) {
|
||||
if (zone.getLobby() != null) {
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
package com.tommytony.war.command;
|
||||
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
|
||||
import com.tommytony.war.War;
|
||||
import com.tommytony.war.mapper.WarYmlMapper;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.logging.Level;
|
||||
|
||||
/**
|
||||
* Makes a player zonemaker and other way round.
|
||||
|
@ -21,8 +21,8 @@ public class ZoneMakerCommand extends AbstractWarCommand {
|
|||
|
||||
if (sender instanceof Player) { // i hate java for this.
|
||||
if (!War.war.isZoneMaker((Player) sender)) {
|
||||
for (String name : War.war.getZoneMakersImpersonatingPlayers()) {
|
||||
if (((Player) sender).getName().equals(name)) {
|
||||
for (OfflinePlayer offlinePlayer : War.war.getZoneMakersImpersonatingPlayers()) {
|
||||
if (offlinePlayer.isOnline() && sender.equals(offlinePlayer.getPlayer())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -41,30 +41,29 @@ public class ZoneMakerCommand extends AbstractWarCommand {
|
|||
|
||||
if (War.war.isZoneMaker(player)) {
|
||||
if (this.args.length == 0) {
|
||||
War.war.getZoneMakersImpersonatingPlayers().add(player.getName());
|
||||
War.war.getZoneMakersImpersonatingPlayers().add(player);
|
||||
this.msg("You are now impersonating a regular player. Type /zonemaker again to toggle back to war maker mode.");
|
||||
} else if (this.args.length == 1) {
|
||||
OfflinePlayer other = Bukkit.getOfflinePlayer(this.args[0]);
|
||||
// make someone zonemaker or remove the right
|
||||
if (War.war.getZoneMakerNames().contains(this.args[0])) {
|
||||
if (War.war.getZoneMakerNames().contains(other)) {
|
||||
// kick
|
||||
War.war.getZoneMakerNames().remove(this.args[0]);
|
||||
War.war.getZoneMakerNames().remove(other);
|
||||
this.msg(this.args[0] + " is not a zone maker anymore.");
|
||||
// TODO store zone makers using UUIDs
|
||||
Player kickedMaker = War.war.getServer().getPlayer(this.args[0]);
|
||||
if (kickedMaker != null) {
|
||||
War.war.msg(kickedMaker, player.getName() + " took away your warzone maker priviledges.");
|
||||
War.war.log(player.getName() + " took away zonemaker rights from " + kickedMaker, Level.INFO);
|
||||
if (other.isOnline()) {
|
||||
War.war.msg(other.getPlayer(), player.getName() + " took away your warzone maker priviledges.");
|
||||
War.war.log(player.getName() + " took away zonemaker rights from " + other.getName(), Level.INFO);
|
||||
}
|
||||
} else {
|
||||
// add
|
||||
War.war.getZoneMakerNames().add(this.args[0]);
|
||||
War.war.getZoneMakerNames().add(other);
|
||||
this.msg(this.args[0] + " is now a zone maker.");
|
||||
Player newMaker = War.war.getServer().getPlayer(this.args[0]);
|
||||
if (newMaker != null) {
|
||||
War.war.msg(newMaker, player.getName() + " made you warzone maker.");
|
||||
War.war.log(player.getName() + " made " + newMaker + " a zonemaker", Level.INFO);
|
||||
if (other.isOnline()) {
|
||||
War.war.msg(other.getPlayer(), player.getName() + " made you warzone maker.");
|
||||
War.war.log(player.getName() + " made " + other.getName() + " a zonemaker", Level.INFO);
|
||||
}
|
||||
}
|
||||
WarYmlMapper.save();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
@ -73,7 +72,7 @@ public class ZoneMakerCommand extends AbstractWarCommand {
|
|||
return false;
|
||||
}
|
||||
|
||||
War.war.getZoneMakersImpersonatingPlayers().remove(player.getName());
|
||||
War.war.getZoneMakersImpersonatingPlayers().remove(player);
|
||||
this.msg("You are back as a zone maker.");
|
||||
WarYmlMapper.save();
|
||||
}
|
||||
|
|
|
@ -1,24 +1,25 @@
|
|||
package com.tommytony.war.job;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.MessageFormat;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.*;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import com.tommytony.war.War;
|
||||
import com.tommytony.war.Warzone;
|
||||
import com.tommytony.war.mapper.ZoneVolumeMapper;
|
||||
import com.tommytony.war.structure.ZoneLobby;
|
||||
import com.tommytony.war.volume.ZoneVolume;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.bukkit.command.CommandSender;
|
||||
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;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.MessageFormat;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class PartialZoneResetJob extends BukkitRunnable implements Cloneable {
|
||||
|
||||
|
@ -61,7 +62,7 @@ public class PartialZoneResetJob extends BukkitRunnable implements Cloneable {
|
|||
public void run() {
|
||||
try {
|
||||
if (conn == null || conn.isClosed()) {
|
||||
conn = ZoneVolumeMapper.getZoneConnection(volume, zone.getName(), volume.getWorld());
|
||||
conn = ZoneVolumeMapper.getZoneConnection(volume, zone.getName());
|
||||
}
|
||||
if (completed >= total) {
|
||||
int airChanges = 0;
|
||||
|
|
|
@ -1,69 +0,0 @@
|
|||
package com.tommytony.war.job;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
|
||||
|
||||
import com.tommytony.war.War;
|
||||
import com.tommytony.war.Warzone;
|
||||
import com.tommytony.war.mapper.VolumeMapper;
|
||||
import com.tommytony.war.structure.WarHub;
|
||||
import com.tommytony.war.volume.Volume;
|
||||
|
||||
public class RestoreWarhubJob implements Runnable {
|
||||
|
||||
private final String hubStr;
|
||||
|
||||
public RestoreWarhubJob(String hubStr) {
|
||||
this.hubStr = hubStr;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
String[] hubStrSplit = this.hubStr.split(",");
|
||||
|
||||
int hubX = Integer.parseInt(hubStrSplit[0]);
|
||||
int hubY = Integer.parseInt(hubStrSplit[1]);
|
||||
int hubZ = Integer.parseInt(hubStrSplit[2]);
|
||||
World world = null;
|
||||
String worldName;
|
||||
String hubOrientation = "west";
|
||||
if (hubStrSplit.length > 3) {
|
||||
worldName = hubStrSplit[3];
|
||||
world = War.war.getServer().getWorld(worldName);
|
||||
if (hubStrSplit.length > 4) {
|
||||
hubOrientation = hubStrSplit[4];
|
||||
}
|
||||
} else {
|
||||
worldName = "DEFAULT";
|
||||
world = War.war.getServer().getWorlds().get(0); // default to first world
|
||||
}
|
||||
if (world != null) {
|
||||
Location hubLocation = new Location(world, hubX, hubY, hubZ);
|
||||
WarHub hub = new WarHub(hubLocation, hubOrientation);
|
||||
War.war.setWarHub(hub);
|
||||
Volume vol;
|
||||
try {
|
||||
vol = VolumeMapper.loadVolume("warhub", "", world);
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
hub.setVolume(vol);
|
||||
hub.getVolume().resetBlocks();
|
||||
hub.initialize();
|
||||
|
||||
// In the previous job started by the mapper, warzones were created, but their lobbies are missing the war hub gate (because it didn't exist yet)
|
||||
for (Warzone zone : War.war.getWarzones()) {
|
||||
if (zone.getLobby() != null) {
|
||||
zone.getLobby().getVolume().resetBlocks();
|
||||
zone.getLobby().initialize();
|
||||
}
|
||||
}
|
||||
War.war.log("Warhub ready.", Level.INFO);
|
||||
} else {
|
||||
War.war.log("Failed to restore warhub. The specified world (name: " + worldName + ") does not exist!", Level.WARNING);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,18 +1,16 @@
|
|||
package com.tommytony.war.job;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import com.tommytony.war.War;
|
||||
import com.tommytony.war.Warzone;
|
||||
import com.tommytony.war.mapper.VolumeMapper;
|
||||
import com.tommytony.war.structure.WarHub;
|
||||
import com.tommytony.war.volume.Volume;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class RestoreYmlWarhubJob implements Runnable {
|
||||
|
||||
|
@ -54,7 +52,7 @@ public class RestoreYmlWarhubJob implements Runnable {
|
|||
War.war.setWarHub(hub);
|
||||
Volume vol;
|
||||
try {
|
||||
vol = VolumeMapper.loadVolume("warhub", "", world);
|
||||
vol = VolumeMapper.loadSimpleVolume("warhub", world);
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
package com.tommytony.war.job;
|
||||
|
||||
import com.tommytony.war.War;
|
||||
import com.tommytony.war.mapper.ZoneVolumeMapper;
|
||||
import com.tommytony.war.volume.Volume;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
|
||||
public class ZoneVolumeSaveJob extends BukkitRunnable {
|
||||
private final Volume volume;
|
||||
private final String zoneName;
|
||||
|
||||
public ZoneVolumeSaveJob(Volume volume, String zoneName) {
|
||||
this.volume = volume;
|
||||
this.zoneName = zoneName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
ZoneVolumeMapper.save(this.volume, this.zoneName);
|
||||
} catch (SQLException ex) {
|
||||
War.war.log(ex.getMessage(), Level.SEVERE);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,407 +0,0 @@
|
|||
package com.tommytony.war.mapper;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.logging.Logger;
|
||||
import java.io.FileInputStream;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* Used for accessing and creating .[properties] files, reads them as utf-8, saves as utf-8. Internationalization is key importance especially for character codes.
|
||||
*
|
||||
* @author Nijikokun
|
||||
* @version 1.0.4, %G%
|
||||
*/
|
||||
public final class PropertiesFile {
|
||||
|
||||
private static final Logger log = Logger.getLogger("Minecraft");
|
||||
private String fileName;
|
||||
private Properties props = new Properties();
|
||||
private FileInputStream inputStream;
|
||||
private FileOutputStream outputStream;
|
||||
|
||||
// private List<String> lines = new ArrayList<String>();
|
||||
// private Map<String, String> props = new HashMap<String, String>();
|
||||
|
||||
/**
|
||||
* Creates or opens a properties file using specified filename
|
||||
*
|
||||
* @param fileName
|
||||
*/
|
||||
public PropertiesFile(String fileName) {
|
||||
this.fileName = fileName;
|
||||
|
||||
File file = new File(fileName);
|
||||
|
||||
try {
|
||||
if (file.exists()) {
|
||||
this.load();
|
||||
} else {
|
||||
this.save();
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
PropertiesFile.log.severe("[PropertiesFile] Unable to load " + fileName + "!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The loader for property files, it reads the file as UTF8 or converts the string into UTF8. Used for simple runthrough's, loading, or reloading of the file.
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public void load() throws IOException {
|
||||
this.inputStream = new FileInputStream(this.fileName);
|
||||
this.props.load(this.inputStream);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes out the <code>key=value</code> properties that were changed into a .[properties] file in UTF8.
|
||||
*/
|
||||
public void save() {
|
||||
try {
|
||||
this.outputStream = new FileOutputStream(this.fileName);
|
||||
this.props.store(this.outputStream, null);
|
||||
} catch (IOException ex) {
|
||||
PropertiesFile.log.severe("[PropertiesFile] Unable to save " + this.fileName + "!");
|
||||
}
|
||||
}
|
||||
|
||||
public void close() {
|
||||
if (this.outputStream != null) {
|
||||
try {
|
||||
this.outputStream.close();
|
||||
} catch (IOException e) {
|
||||
PropertiesFile.log.severe("[PropertiesFile] Failed to close " + this.fileName + " writer!");
|
||||
}
|
||||
} else if (this.inputStream != null) {
|
||||
try {
|
||||
this.inputStream.close();
|
||||
} catch (IOException e) {
|
||||
PropertiesFile.log.severe("[PropertiesFile] Failed to close " + this.fileName + " reader!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Map of all <code>key=value</code> properties in the file as <code><key (java.lang.String), value (java.lang.String)></code> <br />
|
||||
* <br />
|
||||
* Example: <blockquote>
|
||||
*
|
||||
* <pre>
|
||||
* PropertiesFile settings = new PropertiesFile("settings.properties");
|
||||
* Map<String, String> mappedSettings;
|
||||
*
|
||||
* try {
|
||||
* mappedSettings = settings.returnMap();
|
||||
* } catch (Exception ex) {
|
||||
* log.info("Failed mapping settings.properties");
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* </blockquote>
|
||||
*
|
||||
* @return <code>map</code> - Simple Map HashMap of the entire <code>key=value</code> as <code><key (java.lang.String), value (java.lang.String)></code>
|
||||
* @throws Exception
|
||||
* If the properties file doesn't exist.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public Map<String, String> returnMap() throws Exception {
|
||||
return (Map<String, String>) this.props.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if the .[properties] file contains the given <code>key</code>.
|
||||
*
|
||||
* @param var
|
||||
* The key we are going to be checking the existance of.
|
||||
* @return <code>Boolean</code> - True if the <code>key</code> exists, false if it cannot be found.
|
||||
*/
|
||||
public boolean containsKey(String var) {
|
||||
return this.props.containsKey(var);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if this <code>key</code> exists in the .[properties] file.
|
||||
*
|
||||
* @param var
|
||||
* The key we are grabbing the value of.
|
||||
* @return <code>java.lang.String</code> - True if the <code>key</code> exists, false if it cannot be found.
|
||||
*/
|
||||
public String getProperty(String var) {
|
||||
return this.props.getProperty(var);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a key from the file if it exists. This will save() which will invoke a load() on the file.
|
||||
*
|
||||
* @see #save()
|
||||
* @param var
|
||||
* The <code>key</code> that will be removed from the file
|
||||
*/
|
||||
public void removeKey(String var) {
|
||||
if (this.props.containsKey(var)) {
|
||||
this.props.remove(var);
|
||||
this.save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the existance of a <code>key</code>.
|
||||
*
|
||||
* @see #containsKey(java.lang.String)
|
||||
* @param key
|
||||
* The <code>key</code> in question of existance.
|
||||
* @return <code>Boolean</code> - True for existance, false for <code>key</code> found.
|
||||
*/
|
||||
public boolean keyExists(String key) {
|
||||
return this.containsKey(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the <code>key</code> given as a <code>String</code>, however we do not set a string if no <code>key</code> is found.
|
||||
*
|
||||
* @see #getProperty(java.lang.String)
|
||||
* @param key
|
||||
* The <code>key</code> we will retrieve the property from, if no <code>key</code> is found default to "" or empty.
|
||||
*/
|
||||
public String getString(String key) {
|
||||
if (this.containsKey(key)) {
|
||||
return this.getProperty(key);
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the <code>key</code> given as a <code>String</code>. If it is not found, it will invoke saving the default <code>value</code> to the properties file.
|
||||
*
|
||||
* @see #setString(java.lang.String, java.lang.String)
|
||||
* @see #getProperty(java.lang.String)
|
||||
* @param key
|
||||
* The key that we will be grabbing the value from, if no value is found set and return <code>value</code>
|
||||
* @param value
|
||||
* The default value that we will be setting if no prior <code>key</code> is found.
|
||||
* @return java.lang.String Either we will return the default value or a prior existing value depending on existance.
|
||||
*/
|
||||
public String getString(String key, String value) {
|
||||
if (this.containsKey(key)) {
|
||||
return this.getProperty(key);
|
||||
}
|
||||
|
||||
this.setString(key, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the value given as a <code>String</code> on the specified key.
|
||||
*
|
||||
* @see #save()
|
||||
* @param key
|
||||
* The <code>key</code> that we will be addressing the <code>value</code> to.
|
||||
* @param value
|
||||
* The <code>value</code> we will be setting inside the <code>.[properties]</code> file.
|
||||
*/
|
||||
public void setString(String key, String value) {
|
||||
this.props.put(key, value);
|
||||
this.save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the <code>key</code> given in a Integer, however we do not set a string if no <code>key</code> is found.
|
||||
*
|
||||
* @see #getProperty(String var)
|
||||
* @param key
|
||||
* The <code>key</code> we will retrieve the property from, if no <code>key</code> is found default to 0
|
||||
*/
|
||||
public int getInt(String key) {
|
||||
if (this.containsKey(key)) {
|
||||
return Integer.parseInt(this.getProperty(key));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the int value of a key
|
||||
*
|
||||
* @see #setInt(String key, int value)
|
||||
* @param key
|
||||
* The key that we will be grabbing the value from, if no value is found set and return <code>value</code>
|
||||
* @param value
|
||||
* The default value that we will be setting if no prior <code>key</code> is found.
|
||||
* @return <code>Integer</code> - Either we will return the default value or a prior existing value depending on existance.
|
||||
*/
|
||||
public int getInt(String key, int value) {
|
||||
if (this.containsKey(key)) {
|
||||
return Integer.parseInt(this.getProperty(key));
|
||||
}
|
||||
|
||||
this.setInt(key, value);
|
||||
return value;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the value given as a <code>int</code> on the specified key.
|
||||
*
|
||||
* @see #save()
|
||||
* @param key
|
||||
* The <code>key</code> that we will be addressing the <code>value</code> to.
|
||||
* @param value
|
||||
* The <code>value</code> we will be setting inside the <code>.[properties]</code> file.
|
||||
*/
|
||||
public void setInt(String key, int value) {
|
||||
this.props.put(key, String.valueOf(value));
|
||||
|
||||
this.save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the <code>key</code> given in a Double, however we do not set a string if no <code>key</code> is found.
|
||||
*
|
||||
* @see #getProperty(String var)
|
||||
* @param key
|
||||
* The <code>key</code> we will retrieve the property from, if no <code>key</code> is found default to 0.0
|
||||
*/
|
||||
public double getDouble(String key) {
|
||||
if (this.containsKey(key)) {
|
||||
return Double.parseDouble(this.getProperty(key));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the double value of a key
|
||||
*
|
||||
* @see #setDouble(String key, double value)
|
||||
* @param key
|
||||
* The key that we will be grabbing the value from, if no value is found set and return <code>value</code>
|
||||
* @param value
|
||||
* The default value that we will be setting if no prior <code>key</code> is found.
|
||||
* @return <code>Double</code> - Either we will return the default value or a prior existing value depending on existance.
|
||||
*/
|
||||
public double getDouble(String key, double value) {
|
||||
if (this.containsKey(key)) {
|
||||
return Double.parseDouble(this.getProperty(key));
|
||||
}
|
||||
|
||||
this.setDouble(key, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the value given as a <code>double</code> on the specified key.
|
||||
*
|
||||
* @see #save()
|
||||
* @param key
|
||||
* The <code>key</code> that we will be addressing the <code>value</code> to.
|
||||
* @param value
|
||||
* The <code>value</code> we will be setting inside the <code>.[properties]</code> file.
|
||||
*/
|
||||
public void setDouble(String key, double value) {
|
||||
this.props.put(key, String.valueOf(value));
|
||||
|
||||
this.save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the <code>key</code> given in a Long, however we do not set a string if no <code>key</code> is found.
|
||||
*
|
||||
* @see #getProperty(String var)
|
||||
* @param key
|
||||
* The <code>key</code> we will retrieve the property from, if no <code>key</code> is found default to 0L
|
||||
*/
|
||||
public long getLong(String key) {
|
||||
if (this.containsKey(key)) {
|
||||
return Long.parseLong(this.getProperty(key));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the long value of a key
|
||||
*
|
||||
* @see #setLong(String key, long value)
|
||||
* @param key
|
||||
* The key that we will be grabbing the value from, if no value is found set and return <code>value</code>
|
||||
* @param value
|
||||
* The default value that we will be setting if no prior <code>key</code> is found.
|
||||
* @return <code>Long</code> - Either we will return the default value or a prior existing value depending on existance.
|
||||
*/
|
||||
public long getLong(String key, long value) {
|
||||
if (this.containsKey(key)) {
|
||||
return Long.parseLong(this.getProperty(key));
|
||||
}
|
||||
|
||||
this.setLong(key, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the value given as a <code>long</code> on the specified key.
|
||||
*
|
||||
* @see #save()
|
||||
* @param key
|
||||
* The <code>key</code> that we will be addressing the <code>value</code> to.
|
||||
* @param value
|
||||
* The <code>value</code> we will be setting inside the <code>.[properties]</code> file.
|
||||
*/
|
||||
public void setLong(String key, long value) {
|
||||
this.props.put(key, String.valueOf(value));
|
||||
|
||||
this.save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the <code>key</code> given in a Boolean, however we do not set a string if no <code>key</code> is found.
|
||||
*
|
||||
* @see #getProperty(String var)
|
||||
* @param key
|
||||
* The <code>key</code> we will retrieve the property from, if no <code>key</code> is found default to false
|
||||
*/
|
||||
public boolean getBoolean(String key) {
|
||||
if (this.containsKey(key)) {
|
||||
return Boolean.parseBoolean(this.getProperty(key));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the boolean value of a key
|
||||
*
|
||||
* @see #setBoolean(String key, boolean value)
|
||||
* @param key
|
||||
* The key that we will be grabbing the value from, if no value is found set and return <code>value</code>
|
||||
* @param value
|
||||
* The default value that we will be setting if no prior <code>key</code> is found.
|
||||
* @return <code>Boolean</code> - Either we will return the default value or a prior existing value depending on existance.
|
||||
*/
|
||||
public boolean getBoolean(String key, boolean value) {
|
||||
if (this.containsKey(key)) {
|
||||
return Boolean.parseBoolean(this.getProperty(key));
|
||||
}
|
||||
|
||||
this.setBoolean(key, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the value given as a <code>boolean</code> on the specified key.
|
||||
*
|
||||
* @see #save()
|
||||
* @param key
|
||||
* The <code>key</code> that we will be addressing the <code>value</code> to.
|
||||
* @param value
|
||||
* The <code>value</code> we will be setting inside the <code>.[properties]</code> file.
|
||||
*/
|
||||
public void setBoolean(String key, boolean value) {
|
||||
this.props.put(key, String.valueOf(value));
|
||||
|
||||
this.save();
|
||||
}
|
||||
}
|
|
@ -2,15 +2,21 @@ package com.tommytony.war.mapper;
|
|||
|
||||
import com.tommytony.war.War;
|
||||
import com.tommytony.war.volume.Volume;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.bukkit.*;
|
||||
import org.bukkit.block.*;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.entity.*;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.InventoryHolder;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.io.File;
|
||||
import java.sql.*;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.*;
|
||||
import java.util.logging.Level;
|
||||
|
||||
/**
|
||||
|
@ -20,43 +26,17 @@ import java.util.logging.Level;
|
|||
*/
|
||||
public class VolumeMapper {
|
||||
|
||||
public static Volume loadVolume(String volumeName, String zoneName, World world) throws SQLException {
|
||||
Volume volume = new Volume(volumeName, world);
|
||||
VolumeMapper.load(volume, zoneName, world);
|
||||
return volume;
|
||||
protected static final String delim = "-------mcwar iSdgraIyMvOanTEJjZgocczfuG------";
|
||||
protected static final int DATABASE_VERSION = 3;
|
||||
|
||||
private static String getBlockDescriptor(Location loc, String type, String data, String metadata) {
|
||||
return String.format("<%d,%d,%d> type: %s, data: %s, meta: %s", loc.getBlockX(), loc.getBlockY(), loc.getBlockZ(), type, data, metadata);
|
||||
}
|
||||
|
||||
public static void load(Volume volume, String zoneName, World world) throws SQLException {
|
||||
File databaseFile = new File(War.war.getDataFolder(), String.format(
|
||||
"/dat/volume-%s.sl3", volume.getName()));
|
||||
if (!zoneName.isEmpty()) {
|
||||
databaseFile = new File(War.war.getDataFolder(),
|
||||
String.format("/dat/warzone-%s/volume-%s.sl3", zoneName,
|
||||
volume.getName()));
|
||||
}
|
||||
if (!databaseFile.exists()) {
|
||||
// dropped nimitz compatibility with the MC 1.13 update
|
||||
War.war.log("Volume " + volume.getName() + " for zone " + zoneName + " not found. Will not attempt converting legacy War version formats.", Level.WARNING);
|
||||
return;
|
||||
}
|
||||
Connection databaseConnection = DriverManager.getConnection("jdbc:sqlite:" + databaseFile.getPath());
|
||||
public static void loadCorners(Connection databaseConnection, Volume volume, World world, String prefix) throws SQLException {
|
||||
Validate.isTrue(!databaseConnection.isClosed());
|
||||
Statement stmt = databaseConnection.createStatement();
|
||||
ResultSet versionQuery = stmt.executeQuery("PRAGMA user_version");
|
||||
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();
|
||||
}
|
||||
} else if (version < DATABASE_VERSION) {
|
||||
switch (version) {
|
||||
// Run some update SQL for each old version
|
||||
}
|
||||
}
|
||||
ResultSet cornerQuery = stmt.executeQuery("SELECT * FROM corners");
|
||||
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();
|
||||
|
@ -64,40 +44,217 @@ public class VolumeMapper {
|
|||
cornerQuery.close();
|
||||
volume.setCornerOne(corner1);
|
||||
volume.setCornerTwo(corner2);
|
||||
ResultSet query = stmt.executeQuery("SELECT * FROM blocks");
|
||||
stmt.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the given volume
|
||||
*
|
||||
* @param databaseConnection Open connection to zone database
|
||||
* @param volume Volume to load
|
||||
* @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 loadBlocks(Connection databaseConnection, Volume volume, int start, int total, boolean[][][] changes, boolean inMemory, String prefix) throws SQLException {
|
||||
Validate.isTrue(!databaseConnection.isClosed());
|
||||
if (inMemory) {
|
||||
volume.getBlocks().clear();
|
||||
}
|
||||
final Block corner1 = volume.getCornerOne().getBlock();
|
||||
Statement stmt = databaseConnection.createStatement();
|
||||
Map<Integer, String> stringCache = new HashMap<>();
|
||||
stringCache.put(0, null);
|
||||
ResultSet cacheQuery = stmt.executeQuery("SELECT * FROM "+ prefix +"strings");
|
||||
while (cacheQuery.next()) {
|
||||
stringCache.put(cacheQuery.getInt("id"), cacheQuery.getString("type"));
|
||||
}
|
||||
cacheQuery.close();
|
||||
int minX = volume.getMinX(), minY = volume.getMinY(), minZ = volume.getMinZ();
|
||||
int changed = 0;
|
||||
ResultSet query = stmt.executeQuery("SELECT * FROM "+ prefix + "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();
|
||||
ItemStack data = new ItemStack(Material.valueOf(query.getString("type")), 0, query.getShort("data"));
|
||||
modify.setType(data.getType());
|
||||
modify.setData(data.getData());
|
||||
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();
|
||||
// Load information from database, or null if not set
|
||||
String type = stringCache.get(query.getInt("type"));
|
||||
String data = stringCache.get(query.getInt("data"));
|
||||
String metadata = stringCache.get(query.getInt("metadata"));
|
||||
|
||||
// Try to look up the material. May fail due to mods or MC updates.
|
||||
Material mat = Material.getMaterial(type);
|
||||
if (mat == null) {
|
||||
War.war.getLogger().log(Level.WARNING, "Failed to parse block type. " + getBlockDescriptor(modify.getLocation(), type, data, metadata));
|
||||
continue;
|
||||
}
|
||||
// Try to get the block data (damage, non-tile information) using the 1.13 functions
|
||||
BlockData bdata = null;
|
||||
try {
|
||||
if (data != null) {
|
||||
bdata = Bukkit.createBlockData(data);
|
||||
}
|
||||
} catch (IllegalArgumentException iae) {
|
||||
War.war.getLogger().log(Level.WARNING, "Exception loading some block data. " + getBlockDescriptor(modify.getLocation(), type, data, metadata), iae);
|
||||
}
|
||||
// Update the block type/data in memory if they have changed
|
||||
boolean updatedType = false;
|
||||
if (modify.getType() != mat) {
|
||||
modify.setType(mat);
|
||||
updatedType = true;
|
||||
}
|
||||
boolean updatedData = false;
|
||||
if (bdata != null && !modify.getBlockData().equals(bdata)) {
|
||||
modify.setBlockData(bdata);
|
||||
updatedData = true;
|
||||
}
|
||||
if (!inMemory && (updatedType || updatedData)) {
|
||||
// Update the type & data if it has changed, needed here for tile entity check
|
||||
modify.update(true, false); // No-physics update, preventing the need for deferring blocks
|
||||
relative = corner1.getRelative(x, y, z);
|
||||
modify = relative.getState();
|
||||
}
|
||||
// Try to update the tile entity data
|
||||
if (metadata != null) {
|
||||
try {
|
||||
if (modify instanceof Sign) {
|
||||
final String[] lines = metadata.split("\n");
|
||||
for (int i = 0; i < lines.length; i++) {
|
||||
((Sign) modify).setLine(i, lines[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Containers
|
||||
if (modify instanceof Container) {
|
||||
YamlConfiguration config = new YamlConfiguration();
|
||||
config.loadFromString(metadata);
|
||||
Inventory inv = ((Container) modify).getSnapshotInventory();
|
||||
inv.clear();
|
||||
int slot = 0;
|
||||
for (Object obj : config.getList("items")) {
|
||||
if (obj instanceof ItemStack) {
|
||||
inv.setItem(slot, (ItemStack) obj);
|
||||
}
|
||||
++slot;
|
||||
}
|
||||
}
|
||||
|
||||
// Records
|
||||
if (modify instanceof Jukebox) {
|
||||
((Jukebox) modify).setPlaying(Material.valueOf(metadata));
|
||||
}
|
||||
|
||||
// Skulls
|
||||
if (modify instanceof Skull) {
|
||||
UUID playerId = UUID.fromString(metadata);
|
||||
OfflinePlayer player = Bukkit.getOfflinePlayer(playerId);
|
||||
((Skull) modify).setOwningPlayer(player);
|
||||
}
|
||||
|
||||
// Command blocks
|
||||
if (modify instanceof CommandBlock) {
|
||||
final String[] commandArray = metadata.split("\n");
|
||||
((CommandBlock) modify).setName(commandArray[0]);
|
||||
((CommandBlock) modify).setCommand(commandArray[1]);
|
||||
}
|
||||
|
||||
// Creature spawner
|
||||
if (modify instanceof CreatureSpawner) {
|
||||
((CreatureSpawner) modify).setSpawnedType(EntityType.valueOf(metadata));
|
||||
}
|
||||
|
||||
if (!inMemory) {
|
||||
modify.update(true, false);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
War.war.getLogger().log(Level.WARNING, "Exception loading some tile entity data. " + getBlockDescriptor(modify.getLocation(), type, data, metadata), ex);
|
||||
}
|
||||
}
|
||||
|
||||
if (inMemory) {
|
||||
volume.getBlocks().add(modify);
|
||||
}
|
||||
}
|
||||
query.close();
|
||||
stmt.close();
|
||||
databaseConnection.close();
|
||||
return changed;
|
||||
}
|
||||
|
||||
public static final int DATABASE_VERSION = 1;
|
||||
public static void save(Volume volume, String zoneName) throws SQLException {
|
||||
File databaseFile = new File(War.war.getDataFolder(), String.format(
|
||||
"/dat/volume-%s.sl3", volume.getName()));
|
||||
if (!zoneName.isEmpty()) {
|
||||
databaseFile = new File(War.war.getDataFolder(),
|
||||
String.format("/dat/warzone-%s/volume-%s.sl3", zoneName,
|
||||
volume.getName()));
|
||||
/**
|
||||
* Load saved entities.
|
||||
*
|
||||
* @param databaseConnection Open databaseConnection to warzone DB file.
|
||||
* @param volume Volume for warzone.
|
||||
* @return number affected
|
||||
* @throws SQLException SQLite error
|
||||
*/
|
||||
public static int loadEntities(Connection databaseConnection, Volume volume) throws SQLException {
|
||||
Validate.isTrue(!databaseConnection.isClosed());
|
||||
// first, clear entities from the area
|
||||
for (Entity e : volume.getWorld().getEntitiesByClass(Hanging.class)) {
|
||||
if (volume.contains(e.getLocation())) {
|
||||
e.remove();
|
||||
}
|
||||
Connection databaseConnection = DriverManager
|
||||
.getConnection("jdbc:sqlite:" + databaseFile.getPath());
|
||||
}
|
||||
int changed = 0;
|
||||
Statement stmt = databaseConnection.createStatement();
|
||||
final Block corner1 = volume.getCornerOne().getBlock();
|
||||
Location test = new Location(volume.getWorld(), 0, 253, 0); // admins pls don't build stuff here kthx
|
||||
ResultSet query = stmt.executeQuery("SELECT * FROM entities ORDER BY rowid");
|
||||
while (query.next()) {
|
||||
double x = query.getDouble("x"), y = query.getDouble("y"), z = query.getDouble("z");
|
||||
changed++;
|
||||
// translate from relative DB location to absolute
|
||||
Location absolute = corner1.getLocation().clone().add(x, y, z);
|
||||
int type = query.getInt("type");
|
||||
String facing = query.getString("facing");
|
||||
String metadata = query.getString("metadata");
|
||||
BlockFace face = BlockFace.valueOf(facing);
|
||||
|
||||
// Spawn the paintings in the sky somewhere, works because I can only get them to spawn north/south
|
||||
test.getBlock().setType(Material.AIR);
|
||||
test.add(2, 0, 0).getBlock().setType(Material.STONE);
|
||||
|
||||
try {
|
||||
if (type == 1) {
|
||||
Painting p = (Painting) volume.getWorld().spawnEntity(test.clone().add(0, 0, 1), EntityType.PAINTING);
|
||||
Art art = Art.valueOf(metadata);
|
||||
p.teleport(calculatePainting(art, face, absolute));
|
||||
p.setFacingDirection(face, true);
|
||||
p.setArt(art, true);
|
||||
} else if (type == 2) {
|
||||
ItemFrame itemFrame = (ItemFrame) volume.getWorld().spawnEntity(test.clone().add(0, 0, 1), EntityType.ITEM_FRAME);
|
||||
itemFrame.teleport(absolute);
|
||||
itemFrame.setFacingDirection(face, true);
|
||||
String[] args = metadata.split(delim);
|
||||
itemFrame.setRotation(Rotation.valueOf(args[0]));
|
||||
YamlConfiguration config = new YamlConfiguration();
|
||||
config.loadFromString(args[1]);
|
||||
itemFrame.setItem(config.getItemStack("item"));
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
War.war.getLogger().log(Level.WARNING, "Exception loading entity. x:" + x + " y:" + y + " z:" + z + " type:" + type, ex);
|
||||
}
|
||||
}
|
||||
test.getBlock().setType(Material.AIR);
|
||||
query.close();
|
||||
stmt.close();
|
||||
return changed;
|
||||
}
|
||||
|
||||
public static void saveCorners(Connection databaseConnection, Volume volume, String prefix) throws SQLException {
|
||||
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)");
|
||||
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");
|
||||
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+"corners");
|
||||
stmt.close();
|
||||
PreparedStatement cornerStmt = databaseConnection
|
||||
.prepareStatement("INSERT INTO corners SELECT 1 AS pos, ? AS x, ? AS y, ? AS z UNION SELECT 2, ?, ?, ?");
|
||||
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());
|
||||
|
@ -106,37 +263,296 @@ public class VolumeMapper {
|
|||
cornerStmt.setInt(6, volume.getCornerTwo().getBlockZ());
|
||||
cornerStmt.executeUpdate();
|
||||
cornerStmt.close();
|
||||
PreparedStatement dataStmt = databaseConnection
|
||||
.prepareStatement("INSERT INTO blocks VALUES (?, ?, ?, ?, ?)");
|
||||
databaseConnection.setAutoCommit(false);
|
||||
final int batchSize = 1000;
|
||||
}
|
||||
|
||||
public static int saveEntities(Connection databaseConnection, Volume volume) throws SQLException {
|
||||
Statement stmt = databaseConnection.createStatement();
|
||||
stmt.executeUpdate("CREATE TABLE IF NOT EXISTS entities (x NUMERIC, y NUMERIC, z NUMERIC, type SMALLINT, facing TEXT, metadata TEXT)");
|
||||
stmt.executeUpdate("DELETE FROM entities");
|
||||
stmt.close();
|
||||
PreparedStatement entityStmt = databaseConnection.prepareStatement("INSERT INTO entities (x, y, z, type, facing, metadata) VALUES (?, ?, ?, ?, ?, ?)");
|
||||
int i = 0;
|
||||
for (Entity e : volume.getWorld().getEntities()) {
|
||||
if (volume.contains(e.getLocation()) && e instanceof Hanging) {
|
||||
entityStmt.setDouble(1, e.getLocation().getX() - volume.getCornerOne().getBlockX());
|
||||
entityStmt.setDouble(2, e.getLocation().getY() - volume.getCornerOne().getBlockY());
|
||||
entityStmt.setDouble(3, e.getLocation().getZ() - volume.getCornerOne().getBlockZ());
|
||||
entityStmt.setString(5, ((Hanging) e).getFacing().name());
|
||||
if (e instanceof Painting) {
|
||||
Painting p = (Painting) e;
|
||||
entityStmt.setInt(4, 1);
|
||||
entityStmt.setString(6, p.getArt().name());
|
||||
} else if (e instanceof ItemFrame) {
|
||||
ItemFrame itemFrame = (ItemFrame) e;
|
||||
YamlConfiguration config = new YamlConfiguration();
|
||||
config.set("item", itemFrame.getItem());
|
||||
entityStmt.setInt(4, 2);
|
||||
entityStmt.setString(6, itemFrame.getRotation().name() + delim + config.saveToString());
|
||||
} else {
|
||||
entityStmt.setInt(4, 0);
|
||||
entityStmt.setString(6, "");
|
||||
}
|
||||
entityStmt.addBatch();
|
||||
++i;
|
||||
}
|
||||
}
|
||||
entityStmt.executeBatch();
|
||||
entityStmt.close();
|
||||
return i;
|
||||
}
|
||||
|
||||
public static int saveBlocks(Connection databaseConnection, Volume volume, String prefix) throws SQLException {
|
||||
Statement stmt = databaseConnection.createStatement();
|
||||
stmt.executeUpdate("CREATE TABLE IF NOT EXISTS "+prefix+"blocks (x BIGINT, y BIGINT, z BIGINT, type BIGINT, data BIGINT, metadata BIGINT)");
|
||||
stmt.executeUpdate("CREATE TABLE IF NOT EXISTS "+prefix+"strings (id INTEGER PRIMARY KEY NOT NULL UNIQUE, type TEXT)");
|
||||
stmt.executeUpdate("DELETE FROM "+prefix+"blocks");
|
||||
stmt.executeUpdate("DELETE FROM "+prefix+"strings");
|
||||
stmt.close();
|
||||
Map<String, Integer> stringCache = new HashMap<>();
|
||||
int cachei = 1;
|
||||
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(1).getDurability());
|
||||
long startTime = System.currentTimeMillis();
|
||||
PreparedStatement dataStmt = databaseConnection.prepareStatement("INSERT INTO "+prefix+"blocks (x, y, z, type, data, metadata) VALUES (?, ?, ?, ?, ?, ?)");
|
||||
databaseConnection.setAutoCommit(false);
|
||||
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);
|
||||
if (block.getType() == Material.AIR) {
|
||||
continue; // Do not save air blocks to the file anymore.
|
||||
}
|
||||
int typeid, dataid, metaid;
|
||||
// Save even more space by writing each string only once
|
||||
if (stringCache.containsKey(block.getType().name())) {
|
||||
typeid = stringCache.get(block.getType().name());
|
||||
} else {
|
||||
typeid = cachei;
|
||||
stringCache.put(block.getType().name(), cachei++);
|
||||
}
|
||||
// Save new-style data
|
||||
if (BlockData.class.isAssignableFrom(block.getType().data)) {
|
||||
String data = block.getBlockData().getAsString();
|
||||
if (stringCache.containsKey(data)) {
|
||||
dataid = stringCache.get(data);
|
||||
} else {
|
||||
dataid = cachei;
|
||||
stringCache.put(data, cachei++);
|
||||
}
|
||||
} else {
|
||||
dataid = 0;
|
||||
}
|
||||
|
||||
// Save tile entities
|
||||
BlockState state = block.getState();
|
||||
String metadata = "";
|
||||
if (state instanceof Sign) {
|
||||
metadata = StringUtils.join(((Sign) state).getLines(), "\n");
|
||||
} else if (state instanceof InventoryHolder) {
|
||||
List<ItemStack> items = Arrays.asList(((InventoryHolder) state).getInventory().getContents());
|
||||
YamlConfiguration config = new YamlConfiguration();
|
||||
// Serialize to config, then store config in database
|
||||
config.set("items", items);
|
||||
metadata = config.saveToString();
|
||||
} else if (state instanceof Jukebox) {
|
||||
metadata = ((Jukebox) state).getPlaying().toString();
|
||||
} else if (state instanceof Skull) {
|
||||
OfflinePlayer player = ((Skull) state).getOwningPlayer();
|
||||
metadata = player == null ? "" : player.getUniqueId().toString();
|
||||
} else if (state instanceof CommandBlock) {
|
||||
metadata = ((CommandBlock) state).getName() + "\n" + ((CommandBlock) state).getCommand();
|
||||
} else if (state instanceof CreatureSpawner) {
|
||||
metadata = ((CreatureSpawner) state).getSpawnedType().toString();
|
||||
}
|
||||
if (metadata.isEmpty()) {
|
||||
metaid = 0;
|
||||
} else if (stringCache.containsKey(metadata)) {
|
||||
metaid = stringCache.get(metadata);
|
||||
} else {
|
||||
metaid = cachei;
|
||||
stringCache.put(metadata, cachei++);
|
||||
}
|
||||
|
||||
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.setInt(4, typeid);
|
||||
dataStmt.setInt(5, dataid);
|
||||
dataStmt.setInt(6, metaid);
|
||||
|
||||
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 volume {0} , {1} seconds elapsed.", new Object[] {volume.getName(), seconds});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
dataStmt.executeBatch(); // insert remaining records
|
||||
databaseConnection.commit();
|
||||
dataStmt.close();
|
||||
databaseConnection.close();
|
||||
PreparedStatement stringStmt = databaseConnection.prepareStatement("INSERT INTO "+prefix+"strings (id, type) VALUES (?, ?)");
|
||||
for (Map.Entry<String, Integer> mapping : stringCache.entrySet()) {
|
||||
stringStmt.setInt(1, mapping.getValue());
|
||||
stringStmt.setString(2, mapping.getKey());
|
||||
stringStmt.addBatch();
|
||||
}
|
||||
stringStmt.executeBatch();
|
||||
databaseConnection.commit();
|
||||
databaseConnection.setAutoCommit(true);
|
||||
return changed;
|
||||
|
||||
}
|
||||
|
||||
public static void delete(Volume volume) {
|
||||
File volFile = new File(War.war.getDataFolder(), String.format(
|
||||
"/dat/volume-%s.sl3", volume.getName()));
|
||||
/**
|
||||
* Save a simple volume, like the WarHub.
|
||||
*
|
||||
* @param volume Volume to save (takes corner data and loads from world).
|
||||
* @return amount of changed blocks
|
||||
* @throws SQLException
|
||||
*/
|
||||
public static int saveSimpleVolume(Volume volume) throws SQLException {
|
||||
File volFile = new File(War.war.getDataFolder(), String.format("/dat/volume-%s.sl3", volume.getName()));
|
||||
Connection databaseConnection = getConnection(volFile);
|
||||
int changed = 0;
|
||||
saveCorners(databaseConnection, volume, "");
|
||||
saveBlocks(databaseConnection, volume, "");
|
||||
databaseConnection.close();
|
||||
return changed;
|
||||
}
|
||||
|
||||
public static boolean deleteSimpleVolume(Volume volume) {
|
||||
File volFile = new File(War.war.getDataFolder(), String.format("/dat/volume-%s.sl3", volume.getName()));
|
||||
boolean deletedData = volFile.delete();
|
||||
if (!deletedData) {
|
||||
War.war.log("Failed to delete file " + volFile.getName(), Level.WARNING);
|
||||
}
|
||||
return deletedData;
|
||||
}
|
||||
|
||||
public static Volume loadSimpleVolume(String volumeName, World world) throws SQLException {
|
||||
File volFile = new File(War.war.getDataFolder(), String.format("/dat/volume-%s.sl3", volumeName));
|
||||
Connection databaseConnection = getConnection(volFile);
|
||||
int version = checkConvert(databaseConnection);
|
||||
Volume v = new Volume(volumeName, world);
|
||||
switch (version) {
|
||||
case 1:
|
||||
case 2:
|
||||
War.war.log(volumeName + " cannot be migrated from War 1.9 due to breaking MC1.13 changes - please resave.", Level.WARNING);
|
||||
loadCorners(databaseConnection, v, world, "");
|
||||
convertSchema2_3(databaseConnection, "", true);
|
||||
return v;
|
||||
case 3:
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException(String.format("Unsupported volume format (was already converted to version: %d, current format: %d)", version, DATABASE_VERSION));
|
||||
}
|
||||
loadCorners(databaseConnection, v, world, "");
|
||||
loadBlocks(databaseConnection, v, 0, 0, null, true, "");
|
||||
return v;
|
||||
}
|
||||
|
||||
protected static Connection getConnection(File filename) throws SQLException {
|
||||
return DriverManager.getConnection("jdbc:sqlite:" + filename.getPath());
|
||||
}
|
||||
|
||||
protected static int checkConvert(Connection databaseConnection) throws SQLException {
|
||||
Statement stmt = databaseConnection.createStatement();
|
||||
ResultSet versionQuery = stmt.executeQuery("PRAGMA user_version");
|
||||
int version = versionQuery.getInt("user_version");
|
||||
versionQuery.close();
|
||||
stmt.close();
|
||||
return version;
|
||||
}
|
||||
|
||||
protected static void convertSchema2_3(Connection databaseConnection, String prefix, boolean isSimple) throws SQLException {
|
||||
Statement stmt = databaseConnection.createStatement();
|
||||
stmt.executeUpdate("DROP TABLE " + prefix + "blocks");
|
||||
stmt.executeUpdate("CREATE TABLE IF NOT EXISTS "+prefix+"blocks (x BIGINT, y BIGINT, z BIGINT, type BIGINT, data BIGINT, metadata BIGINT)");
|
||||
stmt.executeUpdate("CREATE TABLE IF NOT EXISTS "+prefix+"strings (id INTEGER PRIMARY KEY NOT NULL UNIQUE, type TEXT)");
|
||||
stmt.executeUpdate("PRAGMA user_version = " + DATABASE_VERSION);
|
||||
stmt.close();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Finds the correct location to place a painting based on its characteristics.
|
||||
* Credit goes to whatever forum I got this from.
|
||||
*
|
||||
* @param art Painting type
|
||||
* @param facing Block face
|
||||
* @param loc Desired location
|
||||
* @return Corrected location
|
||||
*/
|
||||
private static Location calculatePainting(Art art, BlockFace facing, Location loc) {
|
||||
switch(art) {
|
||||
|
||||
// 1x1
|
||||
case ALBAN:
|
||||
case AZTEC:
|
||||
case AZTEC2:
|
||||
case BOMB:
|
||||
case KEBAB:
|
||||
case PLANT:
|
||||
case WASTELAND:
|
||||
return loc; // No calculation needed.
|
||||
|
||||
// 1x2
|
||||
case GRAHAM:
|
||||
case WANDERER:
|
||||
return loc.getBlock().getLocation().add(0, -1, 0);
|
||||
|
||||
// 2x1
|
||||
case CREEBET:
|
||||
case COURBET:
|
||||
case POOL:
|
||||
case SEA:
|
||||
case SUNSET: // Use same as 4x3
|
||||
|
||||
// 4x3
|
||||
case DONKEY_KONG:
|
||||
case SKELETON:
|
||||
if(facing == BlockFace.WEST)
|
||||
return loc.getBlock().getLocation().add(0, 0, -1);
|
||||
else if(facing == BlockFace.SOUTH)
|
||||
return loc.getBlock().getLocation().add(-1, 0, 0);
|
||||
else
|
||||
return loc;
|
||||
|
||||
// 2x2
|
||||
case BUST:
|
||||
case MATCH:
|
||||
case SKULL_AND_ROSES:
|
||||
case STAGE:
|
||||
case VOID:
|
||||
case WITHER: // Use same as 4x2
|
||||
|
||||
// 4x2
|
||||
case FIGHTERS: // Use same as 4x4
|
||||
|
||||
// 4x4
|
||||
case BURNING_SKULL:
|
||||
case PIGSCENE:
|
||||
case POINTER:
|
||||
if(facing == BlockFace.WEST)
|
||||
return loc.getBlock().getLocation().add(0, -1, -1);
|
||||
else if(facing == BlockFace.SOUTH)
|
||||
return loc.getBlock().getLocation().add(-1, -1, 0);
|
||||
else
|
||||
return loc.add(0, -1, 0);
|
||||
|
||||
// Unsupported artwork
|
||||
default:
|
||||
return loc;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,18 +1,5 @@
|
|||
package com.tommytony.war.mapper;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
|
||||
import com.tommytony.war.War;
|
||||
import com.tommytony.war.Warzone;
|
||||
import com.tommytony.war.config.KillstreakReward;
|
||||
|
@ -21,6 +8,20 @@ import com.tommytony.war.job.RestoreYmlWarhubJob;
|
|||
import com.tommytony.war.job.RestoreYmlWarzonesJob;
|
||||
import com.tommytony.war.structure.WarHub;
|
||||
import com.tommytony.war.utility.Direction;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class WarYmlMapper {
|
||||
|
||||
|
@ -56,9 +57,14 @@ public class WarYmlMapper {
|
|||
List<String> makers = warRootSection.getStringList("war.info.zonemakers");
|
||||
War.war.getZoneMakerNames().clear();
|
||||
for (String makerName : makers) {
|
||||
if (makerName != null && !makerName.equals("")) {
|
||||
War.war.getZoneMakerNames().add(makerName);
|
||||
OfflinePlayer player;
|
||||
try {
|
||||
UUID id = UUID.fromString(makerName);
|
||||
player = Bukkit.getOfflinePlayer(id);
|
||||
} catch (IllegalArgumentException iae) {
|
||||
player = Bukkit.getOfflinePlayer(makerName);
|
||||
}
|
||||
War.war.getZoneMakerNames().add(player);
|
||||
}
|
||||
|
||||
// command whitelist
|
||||
|
@ -148,7 +154,11 @@ public class WarYmlMapper {
|
|||
warInfoSection.set("warzones", warzones);
|
||||
|
||||
// zone makers
|
||||
warInfoSection.set("zonemakers", War.war.getZoneMakerNames());
|
||||
List<String> zonemakers = new ArrayList<>();
|
||||
for (OfflinePlayer zonemaker : War.war.getZoneMakerNames()) {
|
||||
zonemakers.add(zonemaker.getUniqueId().toString());
|
||||
}
|
||||
warInfoSection.set("zonemakers", zonemakers);
|
||||
|
||||
// whitelisted commands during a game
|
||||
warInfoSection.set("commandwhitelist", War.war.getCommandWhitelist());
|
||||
|
@ -190,7 +200,7 @@ public class WarYmlMapper {
|
|||
hubConfigSection.set("materials.light", War.war.getWarhubMaterials().getLightBlock());
|
||||
|
||||
try {
|
||||
VolumeMapper.save(hub.getVolume(), "");
|
||||
VolumeMapper.saveSimpleVolume(hub.getVolume());
|
||||
} catch (SQLException e) {
|
||||
// who really even cares
|
||||
War.war.getLogger().log(Level.WARNING, "Failed to save warhub volume blocks", e);
|
||||
|
|
|
@ -1,5 +1,21 @@
|
|||
package com.tommytony.war.mapper;
|
||||
|
||||
import com.tommytony.war.Team;
|
||||
import com.tommytony.war.War;
|
||||
import com.tommytony.war.Warzone;
|
||||
import com.tommytony.war.config.TeamConfig;
|
||||
import com.tommytony.war.config.TeamKind;
|
||||
import com.tommytony.war.config.WarzoneConfig;
|
||||
import com.tommytony.war.structure.*;
|
||||
import com.tommytony.war.utility.Direction;
|
||||
import com.tommytony.war.volume.Volume;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.sql.Connection;
|
||||
|
@ -10,24 +26,6 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import com.tommytony.war.config.WarzoneConfig;
|
||||
import com.tommytony.war.structure.*;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import com.tommytony.war.Team;
|
||||
import com.tommytony.war.War;
|
||||
import com.tommytony.war.Warzone;
|
||||
import com.tommytony.war.config.TeamConfig;
|
||||
import com.tommytony.war.config.TeamKind;
|
||||
import com.tommytony.war.utility.Direction;
|
||||
import com.tommytony.war.volume.Volume;
|
||||
import com.tommytony.war.volume.ZoneVolume;
|
||||
|
||||
public class WarzoneYmlMapper {
|
||||
|
||||
public static Warzone load(String name) { // removed createNewVolume, as it did nothing
|
||||
|
@ -304,7 +302,7 @@ public class WarzoneYmlMapper {
|
|||
}
|
||||
Connection connection = null;
|
||||
try {
|
||||
connection = ZoneVolumeMapper.getZoneConnection(warzone.getVolume(), warzone.getName(), warzone.getWorld());
|
||||
connection = ZoneVolumeMapper.getZoneConnection(warzone.getVolume(), warzone.getName());
|
||||
} catch (SQLException e) {
|
||||
War.war.getLogger().log(Level.WARNING, "Failed to load warzone structures volume", e);
|
||||
}
|
||||
|
@ -658,7 +656,7 @@ public class WarzoneYmlMapper {
|
|||
}
|
||||
Connection connection = null;
|
||||
try {
|
||||
connection = ZoneVolumeMapper.getZoneConnection(warzone.getVolume(), warzone.getName(), warzone.getWorld());
|
||||
connection = ZoneVolumeMapper.getZoneConnection(warzone.getVolume(), warzone.getName());
|
||||
} catch (SQLException e) {
|
||||
War.war.getLogger().log(Level.WARNING, "Failed to load warzone structures volume", e);
|
||||
}
|
||||
|
|
|
@ -1,38 +1,19 @@
|
|||
package com.tommytony.war.mapper;
|
||||
|
||||
import java.io.File;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.bukkit.*;
|
||||
import org.bukkit.Note.Tone;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.bukkit.block.CommandBlock;
|
||||
import org.bukkit.block.CreatureSpawner;
|
||||
import org.bukkit.block.Jukebox;
|
||||
import org.bukkit.block.NoteBlock;
|
||||
import org.bukkit.block.Sign;
|
||||
import org.bukkit.block.Skull;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.entity.*;
|
||||
import org.bukkit.inventory.InventoryHolder;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import com.tommytony.war.War;
|
||||
import com.tommytony.war.volume.Volume;
|
||||
import com.tommytony.war.volume.ZoneVolume;
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.bukkit.World;
|
||||
|
||||
import java.io.File;
|
||||
import java.sql.Connection;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
|
||||
/**
|
||||
* Loads and saves zone blocks to SQLite3 database.
|
||||
|
@ -40,387 +21,84 @@ import com.tommytony.war.volume.ZoneVolume;
|
|||
* @author cmastudios
|
||||
* @since 1.8
|
||||
*/
|
||||
public class ZoneVolumeMapper {
|
||||
|
||||
private static final int DATABASE_VERSION = 2;
|
||||
private static final String delim = "-------mcwar iSdgraIyMvOanTEJjZgocczfuG------";
|
||||
public class ZoneVolumeMapper extends VolumeMapper {
|
||||
|
||||
/**
|
||||
* 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 Connection getZoneConnection(ZoneVolume volume, String zoneName, World world) throws SQLException {
|
||||
public static Connection getZoneConnection(ZoneVolume volume, String zoneName) throws SQLException {
|
||||
File databaseFile = new File(War.war.getDataFolder(), String.format("/dat/warzone-%s/volume-%s.sl3", zoneName, volume.getName()));
|
||||
if (!databaseFile.exists()) {
|
||||
// dropped nimitz compatibility with the MC 1.13 update
|
||||
War.war.log("Warzone " + zoneName + " not found - creating new file. Will not attempt converting legacy War version formats.", Level.WARNING);
|
||||
}
|
||||
Connection databaseConnection = DriverManager.getConnection("jdbc:sqlite:" + databaseFile.getPath());
|
||||
Statement stmt = databaseConnection.createStatement();
|
||||
ResultSet versionQuery = stmt.executeQuery("PRAGMA user_version");
|
||||
int version = versionQuery.getInt("user_version");
|
||||
versionQuery.close();
|
||||
if (version > DATABASE_VERSION) {
|
||||
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
|
||||
Connection databaseConnection = getConnection(databaseFile);
|
||||
int version = checkConvert(databaseConnection);
|
||||
switch (version) {
|
||||
// Run some update SQL for each old version
|
||||
case 0: // new file
|
||||
break;
|
||||
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);
|
||||
case 2:
|
||||
War.war.log(zoneName + " cannot be migrated from War 1.9 due to breaking MC1.13 changes - please resave.", Level.WARNING);
|
||||
convertSchema2_3(databaseConnection, "", false);
|
||||
for (String prefix : getStructures(databaseConnection)) {
|
||||
convertSchema2_3(databaseConnection, prefix, false);
|
||||
}
|
||||
|
||||
break;
|
||||
case 3:
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException(String.format("Unsupported volume format (was already converted to version: %d, current format: %d)", version, DATABASE_VERSION));
|
||||
}
|
||||
return databaseConnection;
|
||||
}
|
||||
|
||||
private static List<String> getStructures(Connection databaseConnection) throws SQLException {
|
||||
List<String> structures = new ArrayList<>();
|
||||
Statement stmt = databaseConnection.createStatement();
|
||||
ResultSet q = stmt.executeQuery("SELECT name FROM sqlite_master WHERE type='table'");
|
||||
while (q.next()) {
|
||||
String name = q.getString("name");
|
||||
if (name.contains("structure") && name.contains("corners")) {
|
||||
structures.add(name.replace("corners", ""));
|
||||
}
|
||||
}
|
||||
q.close();
|
||||
stmt.close();
|
||||
return structures;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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"));
|
||||
cornerQuery.next();
|
||||
final Block corner2 = world.getBlockAt(cornerQuery.getInt("x"), cornerQuery.getInt("y"), cornerQuery.getInt("z"));
|
||||
cornerQuery.close();
|
||||
volume.setCornerOne(corner1);
|
||||
volume.setCornerTwo(corner2);
|
||||
if (onlyLoadCorners) {
|
||||
stmt.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");
|
||||
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"));
|
||||
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("metadata").split("\n");
|
||||
for (int i = 0; i < lines.length; i++) {
|
||||
((Sign) modify).setLine(i, lines[i]);
|
||||
}
|
||||
modify.update(true, false);
|
||||
public static int reloadZoneBlocks(Connection databaseConnection, ZoneVolume volume, int start, int total, boolean[][][] changes) throws SQLException {
|
||||
return loadBlocks(databaseConnection, volume, start, total, changes, false, "");
|
||||
}
|
||||
|
||||
// Containers
|
||||
if (modify instanceof InventoryHolder) {
|
||||
YamlConfiguration config = new YamlConfiguration();
|
||||
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("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("metadata")));
|
||||
modify.update(true, false);
|
||||
}
|
||||
|
||||
// Skulls
|
||||
if (modify instanceof Skull) {
|
||||
String[] opts = query.getString("metadata").split("\n");
|
||||
if (!opts[0].isEmpty()) {
|
||||
// TODO upgrade to store owning players by UUID
|
||||
((Skull) modify).setOwningPlayer(Bukkit.getOfflinePlayer(opts[0]));
|
||||
}
|
||||
((Skull) modify).setSkullType(SkullType.valueOf(opts[1]));
|
||||
((Skull) modify).setRotation(BlockFace.valueOf(opts[2]));
|
||||
modify.update(true, false);
|
||||
}
|
||||
|
||||
// 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("metadata")));
|
||||
modify.update(true, false);
|
||||
}
|
||||
} catch (Exception 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);
|
||||
}
|
||||
}
|
||||
query.close();
|
||||
stmt.close();
|
||||
return changed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load saved entities.
|
||||
*
|
||||
* @param connection Open connection to warzone DB file.
|
||||
* @param volume Volume for warzone.
|
||||
* @return number affected
|
||||
* @throws SQLException SQLite error
|
||||
*/
|
||||
public static int loadEntities(Connection connection, ZoneVolume volume) throws SQLException {
|
||||
Validate.isTrue(!connection.isClosed());
|
||||
// first, clear entities from the area
|
||||
for (Entity e : volume.getWorld().getEntitiesByClass(Hanging.class)) {
|
||||
if (volume.contains(e.getLocation())) {
|
||||
e.remove();
|
||||
}
|
||||
}
|
||||
int changed = 0;
|
||||
Statement stmt = connection.createStatement();
|
||||
ResultSet cornerQuery = stmt.executeQuery("SELECT * FROM corners");
|
||||
cornerQuery.next();
|
||||
final Block corner1 = volume.getWorld().getBlockAt(cornerQuery.getInt("x"), cornerQuery.getInt("y"), cornerQuery.getInt("z"));
|
||||
cornerQuery.close();
|
||||
Location test = new Location(volume.getWorld(), 0, 253, 0); // admins pls don't build stuff here kthx
|
||||
// TODO move this to a migration step
|
||||
stmt.executeUpdate("CREATE TABLE IF NOT EXISTS entities (x NUMERIC, y NUMERIC, z NUMERIC, type SMALLINT, facing TEXT, metadata TEXT)");
|
||||
ResultSet query = stmt.executeQuery("SELECT * FROM entities ORDER BY rowid");
|
||||
while (query.next()) {
|
||||
double x = query.getDouble("x"), y = query.getDouble("y"), z = query.getDouble("z");
|
||||
changed++;
|
||||
// translate from relative DB location to absolute
|
||||
Location absolute = corner1.getLocation().clone().add(x, y, z);
|
||||
int type = query.getInt("type");
|
||||
String facing = query.getString("facing");
|
||||
String metadata = query.getString("metadata");
|
||||
BlockFace face = BlockFace.valueOf(facing);
|
||||
|
||||
// Spawn the paintings in the sky somewhere, works because I can only get them to spawn north/south
|
||||
test.getBlock().setType(Material.AIR);
|
||||
test.add(2, 0, 0).getBlock().setType(Material.STONE);
|
||||
|
||||
try {
|
||||
if (type == 1) {
|
||||
Painting p = (Painting) volume.getWorld().spawnEntity(test.clone().add(0, 0, 1), EntityType.PAINTING);
|
||||
Art art = Art.valueOf(metadata);
|
||||
p.teleport(calculatePainting(art, face, absolute));
|
||||
p.setFacingDirection(face, true);
|
||||
p.setArt(art, true);
|
||||
} else if (type == 2) {
|
||||
ItemFrame itemFrame = (ItemFrame) volume.getWorld().spawnEntity(test.clone().add(0, 0, 1), EntityType.ITEM_FRAME);
|
||||
itemFrame.teleport(absolute);
|
||||
itemFrame.setFacingDirection(face, true);
|
||||
String[] args = metadata.split(delim);
|
||||
itemFrame.setRotation(Rotation.valueOf(args[0]));
|
||||
YamlConfiguration config = new YamlConfiguration();
|
||||
config.loadFromString(args[1]);
|
||||
itemFrame.setItem(config.getItemStack("item"));
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
War.war.getLogger().log(Level.WARNING, "Exception loading entity. x:" + x + " y:" + y + " z:" + z + " type:" + type, ex);
|
||||
}
|
||||
}
|
||||
test.getBlock().setType(Material.AIR);
|
||||
query.close();
|
||||
stmt.close();
|
||||
return changed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the correct location to place a painting based on its characteristics.
|
||||
* Credit goes to whatever forum I got this from.
|
||||
*
|
||||
* @param art Painting type
|
||||
* @param facing Block face
|
||||
* @param loc Desired location
|
||||
* @return Corrected location
|
||||
*/
|
||||
private static Location calculatePainting(Art art, BlockFace facing, Location loc) {
|
||||
switch(art) {
|
||||
|
||||
// 1x1
|
||||
case ALBAN:
|
||||
case AZTEC:
|
||||
case AZTEC2:
|
||||
case BOMB:
|
||||
case KEBAB:
|
||||
case PLANT:
|
||||
case WASTELAND:
|
||||
return loc; // No calculation needed.
|
||||
|
||||
// 1x2
|
||||
case GRAHAM:
|
||||
case WANDERER:
|
||||
return loc.getBlock().getLocation().add(0, -1, 0);
|
||||
|
||||
// 2x1
|
||||
case CREEBET:
|
||||
case COURBET:
|
||||
case POOL:
|
||||
case SEA:
|
||||
case SUNSET: // Use same as 4x3
|
||||
|
||||
// 4x3
|
||||
case DONKEY_KONG:
|
||||
case SKELETON:
|
||||
if(facing == BlockFace.WEST)
|
||||
return loc.getBlock().getLocation().add(0, 0, -1);
|
||||
else if(facing == BlockFace.SOUTH)
|
||||
return loc.getBlock().getLocation().add(-1, 0, 0);
|
||||
else
|
||||
return loc;
|
||||
|
||||
// 2x2
|
||||
case BUST:
|
||||
case MATCH:
|
||||
case SKULL_AND_ROSES:
|
||||
case STAGE:
|
||||
case VOID:
|
||||
case WITHER: // Use same as 4x2
|
||||
|
||||
// 4x2
|
||||
case FIGHTERS: // Use same as 4x4
|
||||
|
||||
// 4x4
|
||||
case BURNING_SKULL:
|
||||
case PIGSCENE:
|
||||
case POINTER:
|
||||
if(facing == BlockFace.WEST)
|
||||
return loc.getBlock().getLocation().add(0, -1, -1);
|
||||
else if(facing == BlockFace.SOUTH)
|
||||
return loc.getBlock().getLocation().add(-1, -1, 0);
|
||||
else
|
||||
return loc.add(0, -1, 0);
|
||||
|
||||
// Unsupported artwork
|
||||
default:
|
||||
return loc;
|
||||
}
|
||||
}
|
||||
|
||||
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(1).getDurability());
|
||||
dataStmt.addBatch();
|
||||
if (++changed % batchSize == 0) {
|
||||
dataStmt.executeBatch();
|
||||
}
|
||||
}
|
||||
dataStmt.executeBatch(); // insert remaining records
|
||||
databaseConnection.commit();
|
||||
databaseConnection.setAutoCommit(true);
|
||||
dataStmt.close();
|
||||
return changed;
|
||||
String prefix = String.format("structure_%d_", volume.getName().hashCode() & Integer.MAX_VALUE);
|
||||
saveCorners(databaseConnection, volume, prefix);
|
||||
return saveBlocks(databaseConnection, volume, prefix);
|
||||
}
|
||||
|
||||
public static void loadStructure(Volume volume, Connection databaseConnection) throws SQLException {
|
||||
String prefix = String.format("structure_%d", volume.getName().hashCode() & Integer.MAX_VALUE);
|
||||
String prefix = String.format("structure_%d_", volume.getName().hashCode() & Integer.MAX_VALUE);
|
||||
World world = volume.getWorld();
|
||||
Validate.notNull(world, String.format("Cannot find the warzone for %s", prefix));
|
||||
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();
|
||||
loadCorners(databaseConnection, volume, world, prefix);
|
||||
loadBlocks(databaseConnection, volume, 0, 0, null, true, prefix);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -431,8 +109,7 @@ public class ZoneVolumeMapper {
|
|||
* @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());
|
||||
Connection databaseConnection = getZoneConnection(volume, zoneName);
|
||||
Statement stmt = databaseConnection.createStatement();
|
||||
ResultSet sizeQuery = stmt.executeQuery("SELECT COUNT(*) AS total FROM blocks");
|
||||
int size = sizeQuery.getInt("total");
|
||||
|
@ -450,166 +127,18 @@ public class ZoneVolumeMapper {
|
|||
* @return amount of changed blocks
|
||||
* @throws SQLException
|
||||
*/
|
||||
public static int save(Volume volume, String zoneName) throws SQLException {
|
||||
long startTime = System.currentTimeMillis();
|
||||
int changed = 0;
|
||||
public static int saveZoneBlocksAndEntities(ZoneVolume volume, String zoneName) throws SQLException {
|
||||
File warzoneDir = new File(War.war.getDataFolder().getPath() + "/dat/warzone-" + zoneName);
|
||||
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, metadata BLOB)");
|
||||
stmt.executeUpdate("CREATE TABLE IF NOT EXISTS entities (x NUMERIC, y NUMERIC, z NUMERIC, type SMALLINT, facing TEXT, metadata TEXT)");
|
||||
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");
|
||||
stmt.executeUpdate("DELETE FROM entities");
|
||||
stmt.close();
|
||||
PreparedStatement cornerStmt = databaseConnection.prepareStatement("INSERT INTO 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 entityStmt = databaseConnection.prepareStatement("INSERT INTO entities (x, y, z, type, facing, metadata) VALUES (?, ?, ?, ?, ?, ?)");
|
||||
for (Entity e : volume.getWorld().getEntities()) {
|
||||
if (volume.contains(e.getLocation()) && e instanceof Hanging) {
|
||||
entityStmt.setDouble(1, e.getLocation().getX() - volume.getCornerOne().getBlockX());
|
||||
entityStmt.setDouble(2, e.getLocation().getY() - volume.getCornerOne().getBlockY());
|
||||
entityStmt.setDouble(3, e.getLocation().getZ() - volume.getCornerOne().getBlockZ());
|
||||
entityStmt.setString(5, ((Hanging) e).getFacing().name());
|
||||
if (e instanceof Painting) {
|
||||
Painting p = (Painting) e;
|
||||
entityStmt.setInt(4, 1);
|
||||
entityStmt.setString(6, p.getArt().name());
|
||||
} else if (e instanceof ItemFrame) {
|
||||
ItemFrame itemFrame = (ItemFrame) e;
|
||||
YamlConfiguration config = new YamlConfiguration();
|
||||
config.set("item", itemFrame.getItem());
|
||||
entityStmt.setInt(4, 2);
|
||||
entityStmt.setString(6, itemFrame.getRotation().name() + delim + config.saveToString());
|
||||
} else {
|
||||
entityStmt.setInt(4, 0);
|
||||
entityStmt.setString(6, "");
|
||||
}
|
||||
entityStmt.addBatch();
|
||||
}
|
||||
}
|
||||
entityStmt.executeBatch();
|
||||
entityStmt.close();
|
||||
PreparedStatement dataStmt = databaseConnection.prepareStatement("INSERT INTO blocks (x, y, z, type, data, metadata) VALUES (?, ?, ?, ?, ?, ?)");
|
||||
databaseConnection.setAutoCommit(false);
|
||||
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);
|
||||
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(1).getDurability());
|
||||
if (state instanceof Sign) {
|
||||
final String signText = StringUtils.join(((Sign) block.getState()).getLines(), "\n");
|
||||
dataStmt.setString(6, signText);
|
||||
} 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(6, config.saveToString());
|
||||
} else if (state instanceof NoteBlock) {
|
||||
Note note = ((NoteBlock) block.getState()).getNote();
|
||||
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) {
|
||||
// TODO upgrade to store owning player by UUID
|
||||
dataStmt.setString(6, String.format("%s\n%s\n%s",
|
||||
((Skull) block.getState()).hasOwner() ? ((Skull) block.getState()).getOwningPlayer().getName() : "",
|
||||
// TODO remove deprecation when Spigot updates their docs about the replacement
|
||||
((Skull) block.getState()).getSkullType().toString(),
|
||||
((Skull) block.getState()).getRotation().toString()));
|
||||
} else if (state instanceof CommandBlock) {
|
||||
dataStmt.setString(6, ((CommandBlock) block.getState()).getName()
|
||||
+ "\n" + ((CommandBlock) block.getState()).getCommand());
|
||||
} else if (state instanceof CreatureSpawner) {
|
||||
dataStmt.setString(6, ((CreatureSpawner) block.getState()).getSpawnedType().toString());
|
||||
} else {
|
||||
dataStmt.setString(6, "");
|
||||
}
|
||||
|
||||
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});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
dataStmt.executeBatch(); // insert remaining records
|
||||
databaseConnection.commit();
|
||||
dataStmt.close();
|
||||
databaseConnection.setAutoCommit(true);
|
||||
Connection databaseConnection = getZoneConnection(volume, zoneName);
|
||||
int changed = 0;
|
||||
saveCorners(databaseConnection, volume, "");
|
||||
saveBlocks(databaseConnection, volume, "");
|
||||
saveEntities(databaseConnection, volume);
|
||||
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;
|
||||
}
|
||||
|
||||
static Location rebase(final Location base, final Location exact) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,5 @@
|
|||
package com.tommytony.war.structure;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.bukkit.material.Sign;
|
||||
|
||||
import com.tommytony.war.Team;
|
||||
import com.tommytony.war.War;
|
||||
import com.tommytony.war.Warzone;
|
||||
|
@ -19,6 +7,17 @@ import com.tommytony.war.config.TeamConfig;
|
|||
import com.tommytony.war.config.WarzoneConfig;
|
||||
import com.tommytony.war.utility.Direction;
|
||||
import com.tommytony.war.volume.Volume;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.bukkit.material.Sign;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -236,8 +235,7 @@ public class WarHub {
|
|||
/**
|
||||
* Resets the sign of the given warzone
|
||||
*
|
||||
* @param Warzone
|
||||
* zone
|
||||
* @param zone
|
||||
*/
|
||||
public void resetZoneSign(Warzone zone) {
|
||||
BlockFace left;
|
||||
|
|
|
@ -1,13 +1,5 @@
|
|||
package com.tommytony.war.volume;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
|
||||
import com.tommytony.war.Team;
|
||||
import com.tommytony.war.War;
|
||||
import com.tommytony.war.Warzone;
|
||||
|
@ -15,6 +7,13 @@ import com.tommytony.war.config.WarConfig;
|
|||
import com.tommytony.war.job.PartialZoneResetJob;
|
||||
import com.tommytony.war.mapper.ZoneVolumeMapper;
|
||||
import com.tommytony.war.structure.Monument;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.logging.Level;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -36,7 +35,7 @@ public class ZoneVolume extends Volume {
|
|||
// Save blocks directly to disk (i.e. don't put everything in memory)
|
||||
int saved = 0;
|
||||
try {
|
||||
saved = ZoneVolumeMapper.save(this, this.zone.getName());
|
||||
saved = ZoneVolumeMapper.saveZoneBlocksAndEntities(this, this.zone.getName());
|
||||
} catch (SQLException ex) {
|
||||
War.war.log("Failed to save warzone " + zone.getName() + ": " + ex.getMessage(), Level.WARNING);
|
||||
ex.printStackTrace();
|
||||
|
@ -51,8 +50,8 @@ public class ZoneVolume extends Volume {
|
|||
}
|
||||
|
||||
public void loadCorners() throws SQLException {
|
||||
Connection conn = ZoneVolumeMapper.getZoneConnection(this, this.zone.getName(), this.getWorld());
|
||||
ZoneVolumeMapper.load(conn, this, this.getWorld(), true, 0, 0, null);
|
||||
Connection conn = ZoneVolumeMapper.getZoneConnection(this, this.zone.getName());
|
||||
ZoneVolumeMapper.loadCorners(conn, this, this.getWorld(), "");
|
||||
this.isSaved = true;
|
||||
}
|
||||
|
||||
|
@ -60,8 +59,8 @@ public class ZoneVolume extends Volume {
|
|||
public void resetBlocks() {
|
||||
// Load blocks directly from disk and onto the map (i.e. no more in-memory warzone blocks)
|
||||
try {
|
||||
Connection conn = ZoneVolumeMapper.getZoneConnection(this, this.zone.getName(), this.getWorld());
|
||||
ZoneVolumeMapper.load(conn, this, this.getWorld(), false, 0, Integer.MAX_VALUE, null);
|
||||
Connection conn = ZoneVolumeMapper.getZoneConnection(this, this.zone.getName());
|
||||
ZoneVolumeMapper.reloadZoneBlocks(conn, this, 0, Integer.MAX_VALUE, null);
|
||||
} catch (SQLException ex) {
|
||||
War.war.log("Failed to load warzone " + zone.getName() + ": " + ex.getMessage(), Level.WARNING);
|
||||
ex.printStackTrace();
|
||||
|
@ -82,7 +81,7 @@ public class ZoneVolume extends Volume {
|
|||
* @throws SQLException
|
||||
*/
|
||||
public int resetSection(Connection conn, int start, int total, boolean[][][] changes) throws SQLException {
|
||||
return ZoneVolumeMapper.load(conn, this, this.getWorld(), false, start, total, changes);
|
||||
return ZoneVolumeMapper.reloadZoneBlocks(conn, this, start, total, changes);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue