diff --git a/sponge/pom.xml b/sponge/pom.xml
index 8627a4a..217c217 100644
--- a/sponge/pom.xml
+++ b/sponge/pom.xml
@@ -108,7 +108,7 @@
org.spongepowered
- spongeapi
+ SpongeAPI
1.0.0-SNAPSHOT
diff --git a/sponge/src/main/java/com/tommytony/war/WarConfig.java b/sponge/src/main/java/com/tommytony/war/WarConfig.java
index 7a6177b..84b4493 100644
--- a/sponge/src/main/java/com/tommytony/war/WarConfig.java
+++ b/sponge/src/main/java/com/tommytony/war/WarConfig.java
@@ -1,6 +1,7 @@
package com.tommytony.war;
import com.google.common.collect.ImmutableList;
+import com.tommytony.war.zone.ZoneConfig;
import org.spongepowered.api.entity.Player;
import java.io.Closeable;
@@ -17,6 +18,7 @@ import java.util.UUID;
*/
public class WarConfig implements Closeable {
private final WarPlugin plugin;
+ private final ZoneConfig zoneDefaults;
/**
* Database configuration descriptor.
*/
@@ -39,6 +41,7 @@ public class WarConfig implements Closeable {
stmt.executeUpdate("CREATE TABLE IF NOT EXISTS zones (name TEXT)");
stmt.executeUpdate("CREATE TABLE IF NOT EXISTS zonemakers (uuid TEXT)");
}
+ zoneDefaults = new ZoneConfig(conn, "zone_settings");
}
/**
@@ -55,12 +58,21 @@ public class WarConfig implements Closeable {
if (result.next()) {
return result.getInt(1);
} else {
- return (int) setting.defaultValue;
+ return (Integer) setting.defaultValue;
}
}
}
}
+ /**
+ * Get access to zone default settings.
+ *
+ * @return controller of server zone defaults.
+ */
+ public ZoneConfig getZoneDefaults() {
+ return zoneDefaults;
+ }
+
/**
* Load all the enabled war zones on the server.
*
diff --git a/sponge/src/main/java/com/tommytony/war/WarPlugin.java b/sponge/src/main/java/com/tommytony/war/WarPlugin.java
index 12c8370..70178ec 100644
--- a/sponge/src/main/java/com/tommytony/war/WarPlugin.java
+++ b/sponge/src/main/java/com/tommytony/war/WarPlugin.java
@@ -4,6 +4,7 @@ import org.apache.logging.log4j.Logger;
import org.spongepowered.api.Game;
import org.spongepowered.api.event.SpongeEventHandler;
import org.spongepowered.api.event.state.PreInitializationEvent;
+import org.spongepowered.api.event.state.ServerStartedEvent;
import org.spongepowered.api.event.state.ServerStartingEvent;
import org.spongepowered.api.plugin.Plugin;
@@ -37,6 +38,11 @@ public class WarPlugin {
config = new WarConfig(this, new File(dataDir, "war.sl3"));
}
+ @SpongeEventHandler
+ public void onStart(ServerStartedEvent event) {
+ // register commands
+ }
+
public Game getGame() {
return game;
}
@@ -45,6 +51,10 @@ public class WarPlugin {
return logger;
}
+ public File getDataDir() {
+ return dataDir;
+ }
+
public WarConfig getConfig() {
return config;
}
diff --git a/sponge/src/main/java/com/tommytony/war/zone/Warzone.java b/sponge/src/main/java/com/tommytony/war/zone/Warzone.java
new file mode 100644
index 0000000..9047a52
--- /dev/null
+++ b/sponge/src/main/java/com/tommytony/war/zone/Warzone.java
@@ -0,0 +1,54 @@
+package com.tommytony.war.zone;
+
+import com.tommytony.war.WarPlugin;
+
+import java.sql.SQLException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Representation of a war zone area, blocks, and settings.
+ * Each zone is to only have one instance of Warzone.class at any given time while the server is running.
+ */
+public class Warzone {
+ private static Pattern zoneName = Pattern.compile("[./%]+");
+ private final WarPlugin plugin;
+ private final String name;
+ private final ZoneStorage db;
+ private final ZoneConfig config;
+
+ /**
+ * Load or create a war zone from the war settings store.
+ *
+ * @param plugin Instance of the plugin War.
+ * @param name Name of the zone. Used to locate the zone data file.
+ */
+ public Warzone(WarPlugin plugin, String name) {
+ this.plugin = plugin;
+ this.name = name;
+ try {
+ this.db = new ZoneStorage(this, plugin);
+ this.config = new ZoneConfig(db.getConnection(), "settings", plugin.getConfig().getZoneDefaults());
+ } catch (SQLException ex) {
+ plugin.getLogger().error("Failed to load zone database and settings", ex);
+ throw new RuntimeException("Can't create/load database for warzone " + name);
+ }
+ }
+
+ /**
+ * Check if the specified name is valid for a zone.
+ * Specifically, characters that could be part of a file path or web URL.
+ * '.', '/', and '%' are invalid.
+ *
+ * @param name Potential zone name to check.
+ * @return true if the name can be used.
+ */
+ public static boolean zoneNameValid(String name) {
+ Matcher m = zoneName.matcher(name);
+ return m.find();
+ }
+
+ public String getName() {
+ return name;
+ }
+}
diff --git a/sponge/src/main/java/com/tommytony/war/zone/ZoneConfig.java b/sponge/src/main/java/com/tommytony/war/zone/ZoneConfig.java
new file mode 100644
index 0000000..4722c48
--- /dev/null
+++ b/sponge/src/main/java/com/tommytony/war/zone/ZoneConfig.java
@@ -0,0 +1,75 @@
+package com.tommytony.war.zone;
+
+import java.sql.*;
+
+/**
+ * The zone configuration settings database.
+ */
+public class ZoneConfig {
+ /**
+ * Database configuration descriptor.
+ */
+ private final Connection conn;
+ /**
+ * Table of values to manage. May be a table in a zone database or the main war database.
+ */
+ private final String table;
+ /**
+ * Root zone config, for fallback. Null if this is the war main settings.
+ */
+ private final ZoneConfig parent;
+
+ /**
+ * Manages a zone configuration section.
+ *
+ * @param database Active database to use.
+ * @param table Table name to use in database. Created if it does not exist. Needs to be trusted input.
+ * @param parent Parent zone config, for fallback. Could be zone config for a team or war global for zones.
+ * @throws SQLException
+ */
+ public ZoneConfig(Connection database, String table, ZoneConfig parent) throws SQLException {
+ this.conn = database;
+ this.table = table;
+ this.parent = parent;
+ try (Statement stmt = conn.createStatement()) {
+ stmt.executeUpdate(String.format("CREATE TABLE IF NOT EXISTS %s (option TEXT, value BLOB)", table));
+ }
+ }
+
+ /**
+ * Manages a zone configuration section.
+ *
+ * @param database Active database to use.
+ * @param table Table name to use in database. Created if it does not exist. Needs to be trusted input.
+ * @throws SQLException
+ */
+ public ZoneConfig(Connection database, String table) throws SQLException {
+ this(database, table, null);
+ }
+
+ /**
+ * Get the value of an integer setting.
+ *
+ * @param setting The type of setting to look up.
+ * @return the value of the setting or the default if not found.
+ * @throws SQLException
+ */
+ public int getInt(ZoneSetting setting) throws SQLException {
+ try (PreparedStatement stmt = conn.prepareStatement(String.format("SELECT value FROM %s WHERE option = ?", table))) {
+ stmt.setString(1, setting.name());
+ try (ResultSet result = stmt.executeQuery()) {
+ if (result.next()) {
+ // found an override for this config level
+ return result.getInt(1);
+ } else if (parent != null) {
+ // look for it in zone/global configs; will be recursive upwards
+ return parent.getInt(setting);
+ } else {
+ // the hard-coded value for fallback
+ return (Integer) setting.getDefaultValue();
+ }
+ }
+ }
+ }
+
+}
diff --git a/sponge/src/main/java/com/tommytony/war/zone/ZoneSetting.java b/sponge/src/main/java/com/tommytony/war/zone/ZoneSetting.java
new file mode 100644
index 0000000..9c90f16
--- /dev/null
+++ b/sponge/src/main/java/com/tommytony/war/zone/ZoneSetting.java
@@ -0,0 +1,25 @@
+package com.tommytony.war.zone;
+
+/**
+ * Settings that can be changed on a per-zone basis.
+ * Some can be overridden per-team.
+ */
+public enum ZoneSetting {
+ /**
+ * Maximum players in a zone or team. Zone max is still observed even if team settings allow for more.
+ */
+ MAXPLAYERS(Integer.class, 10, true);
+ private final Class> dataType;
+ private final Object defaultValue;
+ private final boolean perTeam;
+
+ private ZoneSetting(Class> dataType, Object defaultValue, boolean perTeam) {
+ this.dataType = dataType;
+ this.defaultValue = defaultValue;
+ this.perTeam = perTeam;
+ }
+
+ public Object getDefaultValue() {
+ return defaultValue;
+ }
+}
diff --git a/sponge/src/main/java/com/tommytony/war/zone/ZoneStorage.java b/sponge/src/main/java/com/tommytony/war/zone/ZoneStorage.java
new file mode 100644
index 0000000..c6e1836
--- /dev/null
+++ b/sponge/src/main/java/com/tommytony/war/zone/ZoneStorage.java
@@ -0,0 +1,76 @@
+package com.tommytony.war.zone;
+
+import com.tommytony.war.WarPlugin;
+
+import java.io.File;
+import java.sql.*;
+
+/**
+ * Manages the war zone database file, which contains all the data for the war zone.
+ */
+public class ZoneStorage implements AutoCloseable {
+ private static int DATABASE_VERSION = 1;
+ private final Warzone zone;
+ private final WarPlugin plugin;
+ private final Connection connection;
+ private final File dataStore;
+
+ /**
+ * Initiates a database for a new or existing database.
+ *
+ * @param zone The server war zone object for this database.
+ * @param plugin The war plugin, for storage information and configuration.
+ * @throws SQLException
+ */
+ ZoneStorage(Warzone zone, WarPlugin plugin) throws SQLException {
+ this.zone = zone;
+ this.plugin = plugin;
+ dataStore = new File(plugin.getDataDir(), String.format("%s.warzone", zone.getName()));
+ connection = DriverManager.getConnection("jdbc:sqlite:" + dataStore.getPath());
+ this.upgradeDatabase();
+ }
+
+ Connection getConnection() {
+ return connection;
+ }
+
+ /**
+ * Check the database stored version information and perform upgrade tasks if necessary.
+ *
+ * @throws SQLException
+ */
+ private void upgradeDatabase() throws SQLException {
+ int version;
+ try (
+ Statement stmt = connection.createStatement();
+ ResultSet resultSet = stmt.executeQuery("PRAGMA user_version");
+ ) {
+ version = resultSet.getInt("user_version");
+ }
+ if (version > DATABASE_VERSION) {
+ // version is from a future version
+ throw new IllegalStateException(String.format("Unsupported zone version: %d. War current version: %d",
+ version, DATABASE_VERSION));
+ } else if (version == 0) {
+ // brand new database file
+ } else if (version < DATABASE_VERSION) {
+ // upgrade
+ switch (version) {
+ // none yet
+ default:
+ // some odd bug or people messing with their database
+ throw new IllegalStateException(String.format("Unsupported zone version: %d.", version));
+ }
+ }
+ }
+
+ /**
+ * Closes this resource, relinquishing any underlying resources.
+ *
+ * @throws Exception if this resource cannot be closed
+ */
+ @Override
+ public void close() throws Exception {
+ connection.close();
+ }
+}