diff --git a/serverdata/static/spawns.db b/serverdata/static/spawns.db new file mode 100644 index 000000000..b6deebec9 Binary files /dev/null and b/serverdata/static/spawns.db differ diff --git a/serverdata/static/spawns.sdb b/serverdata/static/spawns.sdb new file mode 100644 index 000000000..228bbc736 --- /dev/null +++ b/serverdata/static/spawns.sdb @@ -0,0 +1,31 @@ +iff_type child_iff cell x y z heading +INTEGER TEXT NOT NULL TEXT NOT NULL REAL REAL REAL REAL +1 object/tangible/terminal/shared_terminal_travel.iff foyer4 11.255 0.64 51.64 -35 +1 object/tangible/terminal/shared_terminal_travel.iff foyer4 2.52 0.64 50.02 0 +1 object/tangible/terminal/shared_terminal_travel.iff foyer4 -3.23 0.64 50.05 0 +1 object/tangible/terminal/shared_terminal_travel.iff foyer4 -12.71 0.64 52.04 35 +1 object/creature/npc/theme_park/shared_player_transport.iff 1.72 0 -5.06 180 +1 object/tangible/travel/ticket_collector/shared_ticket_collector.iff 10 0 -10 90 +2 object/tangible/terminal/shared_terminal_travel.iff -13.3 0.6 -9.4 270 +2 object/creature/npc/theme_park/shared_player_shuttle.iff 0 1.6 -5 180 +2 object/tangible/travel/ticket_collector/shared_ticket_collector.iff -13.3 0.6 9.4 270 +3 object/tangible/terminal/shared_terminal_travel.iff -6 -1 -14 0 +3 object/creature/npc/theme_park/shared_player_shuttle.iff 0 -0.6 -5 180 +3 object/tangible/travel/ticket_collector/shared_ticket_collector.iff 6 -1 -14 0 +4 object/tangible/terminal/shared_terminal_travel.iff r1 -3.12 0 -17.57 -90 +4 object/creature/npc/theme_park/shared_player_transport.iff 0 9 0 -90 +4 object/tangible/travel/ticket_collector/shared_ticket_collector.iff 1 0 -10 315 +4 object/tangible/terminal/shared_terminal_space.iff r1 -7.6 0 -19.5 0 +5 object/tangible/terminal/shared_terminal_travel.iff hall1 -16.5 0.75 -68 180 +5 object/tangible/terminal/shared_terminal_travel.iff hall1 -27 0.75 -68 180 +5 object/tangible/terminal/shared_terminal_travel.iff hall2 16.5 0.75 -68 180 +5 object/tangible/terminal/shared_terminal_travel.iff hall2 27 0.75 -68 180 +5 object/creature/npc/theme_park/shared_player_transport_theed_hangar.iff hangar -0.51 7.94 21 0 +5 object/tangible/travel/ticket_collector/shared_ticket_collector.iff hangar -10 7.94 10 90 +6 object/tangible/terminal/shared_terminal_travel.iff 60 -30.7 130 180 +6 object/tangible/travel/ticket_collector/shared_ticket_collector.iff 45 -30.7 152 -90 +6 object/creature/npc/theme_park/shared_player_transport.iff 55 -32 160 90 +7 object/tangible/terminal/shared_terminal_bank.iff 0 0.52 4.3 0 +7 object/tangible/terminal/shared_terminal_bank.iff 0 0.52 -4.3 180 +7 object/tangible/terminal/shared_terminal_bank.iff 4.3 0.52 0 90 +7 object/tangible/terminal/shared_terminal_bank.iff -4.3 0.52 0 -90 diff --git a/serverdata/static/types.sdb b/serverdata/static/types.sdb new file mode 100644 index 000000000..9b6beb184 --- /dev/null +++ b/serverdata/static/types.sdb @@ -0,0 +1,23 @@ +iff iff_type +TEXT PRIMARY KEY INTEGER +object/building/tatooine/shared_starport_tatooine.iff 1 +object/building/tatooine/shared_shuttleport_tatooine.iff 2 +object/building/naboo/shared_starport_naboo.iff 1 +object/building/naboo/shared_hangar_naboo_theed.iff 5 +object/building/corellia/shared_starport_corellia.iff 1 +object/building/naboo/shared_shuttleport_naboo.iff 2 +object/building/corellia/shared_shuttleport_corellia.iff 2 +object/building/military/shared_outpost_starport.iff 4 +object/building/player/city/shared_shuttleport_tatooine.iff 3 +object/building/player/city/shared_shuttleport_naboo.iff 3 +object/building/player/city/shared_shuttleport_corellia.iff 3 +object/building/kashyyyk/shared_mun_kachirho_starport.iff 4 +object/building/mustafar/structures/shared_must_new_mining_facility.iff 6 +object/building/naboo/shared_rori_restuss_starport.iff 4 +object/building/naboo/shared_rori_restuss_shuttleport.iff 2 +object/tangible/gcw/static_base/shared_invisible_beacon.iff 4 +object/tangible/camp/shared_camp_shuttle_beacon.iff 4 +object/building/dathomir/shared_dathomir_quarantine_zone_starport.iff 4 +object/building/tatooine/shared_bank_tatooine.iff 7 +object/building/naboo/shared_bank_naboo.iff 7 +object/building/corellia/shared_bank_corellia.iff 7 diff --git a/serverdata/static_spawns.db b/serverdata/static_spawns.db deleted file mode 100644 index c04941de6..000000000 Binary files a/serverdata/static_spawns.db and /dev/null differ diff --git a/src/main/ProjectSWG.java b/src/main/ProjectSWG.java index 8b2c2b484..2510a3b07 100644 --- a/src/main/ProjectSWG.java +++ b/src/main/ProjectSWG.java @@ -172,7 +172,7 @@ public class ProjectSWG { System.out.println("ProjectSWG: Terminated. Time: " + manager.getCoreTime() + "ms"); } - private static class CoreException extends RuntimeException { + public static class CoreException extends RuntimeException { private static final long serialVersionUID = 455306876887818064L; diff --git a/src/resources/server_info/RelationalServerData.java b/src/resources/server_info/RelationalServerData.java index c4435cad2..260feab6a 100644 --- a/src/resources/server_info/RelationalServerData.java +++ b/src/resources/server_info/RelationalServerData.java @@ -27,10 +27,177 @@ ***********************************************************************************/ package resources.server_info; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + public class RelationalServerData extends RelationalDatabase { + private static final String META_TABLE = "__server_table_metadata__"; + private final PreparedStatement getTableMetadata; + private final PreparedStatement updateTableMetadata; + private final PreparedStatement insertTableMetadata; + public RelationalServerData(String file) { super("org.sqlite.JDBC", "jdbc:sqlite:" + file); + /* + * GREATLY speeds up INSERT speeds. This makes it so SQLite will not + * wait until the previous write finishes before starting to write the + * next INSERT. All imports will only take milliseconds. A caveat of + * this is that if the power ever cuts or the application is force + * killed it may result in database corruption. + */ + updateQuery("PRAGMA synchronous=OFF"); + updateQuery("CREATE TABLE IF NOT EXISTS "+META_TABLE+" (table_name TEXT, last_imported INTEGER)"); + getTableMetadata = prepareStatement("SELECT * FROM "+META_TABLE+" WHERE table_name=?"); + updateTableMetadata = prepareStatement("UPDATE "+META_TABLE+" SET last_imported=? WHERE table_name=?"); + insertTableMetadata = prepareStatement("INSERT INTO "+META_TABLE+" (table_name, last_imported) VALUES (?, ?)"); + } + + public void linkTableWithSdb(String table, String sdbPath) throws FileNotFoundException { + File sdb = new File(sdbPath); + if (!sdb.isFile()) + throw new FileNotFoundException("SDB does not exist at path " + sdb); + long sdbModified = sdb.lastModified(); + long imported = getLastImported(table); + if (sdbModified > imported) { + updateQuery("DROP TABLE " + table); + importFromSdb(table, sdb); + updateLastImported(table, System.currentTimeMillis()); + } + } + + private long getLastImported(String table) { + synchronized (getTableMetadata) { + ResultSet set = null; + try { + getTableMetadata.setString(1, table); + set = getTableMetadata.executeQuery(); + if (set.next()) + return set.getLong("last_imported"); + } catch (SQLException e) { + e.printStackTrace(); + } finally { + if (set != null) { + try { + set.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + } + return -1; + } + } + + private void updateLastImported(String table, long lastImported) { + synchronized (updateTableMetadata) { + try { + updateTableMetadata.setLong(1, lastImported); + updateTableMetadata.setString(2, table); + if (updateTableMetadata.executeUpdate() > 0) + return; + } catch (SQLException e) { + e.printStackTrace(); + return; + } + } + synchronized (insertTableMetadata) { + try { + insertTableMetadata.setString(1, table); + insertTableMetadata.setLong(2, lastImported); + insertTableMetadata.executeUpdate(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + } + + private boolean importFromSdb(String table, File sdb) { + try { + processSdb(table, sdb); + System.out.println("Imported from sdb: " + sdb); + return true; + } catch (IOException e) { + e.printStackTrace(); + } catch (SQLException e) { + e.printStackTrace(); + } catch (NumberFormatException e) { + System.err.println("Invalid record in sdb. Aborting read of " + sdb + "!"); + } catch (IllegalArgumentException e) { + System.err.println("Invalid file format. Aborting read of " + sdb + "!"); + } + return false; + } + + private void processSdb(String table, File file) throws IOException, SQLException { + BufferedReader reader = new BufferedReader(new FileReader(file)); + String line = null; + String [] columnNames = null; + String [] columnTypes = null; + PreparedStatement insert = null; + try { + while ((line = reader.readLine()) != null) { + String [] parts = line.split("\t"); + if (columnNames == null) + columnNames = parts; + else if (columnTypes == null) { + columnTypes = parts; + createTable(table, columnNames, columnTypes); + insert = prepareStatement(createPreparedStatement(table, columnNames.length)); + } else + generateInsert(insert, columnTypes, parts); + } + if (insert != null) + insert.executeBatch(); + } finally { + reader.close(); + } + } + + private void createTable(String table, String [] names, String [] types) { + if (names.length != types.length) + throw new IllegalArgumentException("Names length and Types length mismatch"); + String sql = "CREATE TABLE "+table+" ("; + for (int i = 0; i < names.length; i++) { + if (i > 0) + sql += ", "; + sql += names[i] + " " + types[i]; + } + sql += ")"; + updateQuery(sql); + } + + private String createPreparedStatement(String table, int valueSize) { + String sql = "INSERT INTO " + table + " VALUES ("; + for (int i = 0; i < valueSize; i++) { + if (i > 0) + sql += ", "; + sql += "?"; + } + sql += ")"; + return sql; + } + + private void generateInsert(PreparedStatement insert, String [] types, String [] data) throws SQLException { + if (types.length != data.length) + throw new IllegalArgumentException("Types length and Data length mismatch"); + for (int i = 0; i < data.length; i++) { + if (types[i].startsWith("TEXT")) + insert.setString(i+1, data[i]); + else if (types[i].startsWith("REAL")) + insert.setDouble(i+1, Double.valueOf(data[i])); + else if (types[i].startsWith("INTEGER")) + insert.setLong(i+1, Long.valueOf(data[i])); + else + throw new SQLException("Data type unsupported by sdb/sqlite! Type: " + types[i]); + } + insert.addBatch(); } } diff --git a/src/services/spawn/StaticService.java b/src/services/spawn/StaticService.java index 2d4370d16..059cb35cf 100644 --- a/src/services/spawn/StaticService.java +++ b/src/services/spawn/StaticService.java @@ -27,6 +27,7 @@ ***********************************************************************************/ package services.spawn; +import java.io.FileNotFoundException; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; @@ -51,7 +52,14 @@ public class StaticService extends Service { this.databaseMutex = new Object(); this.objectManager = objectManager; - spawnDatabase = new RelationalServerData("serverdata/static_spawns.db"); + spawnDatabase = new RelationalServerData("serverdata/static/spawns.db"); + try { + spawnDatabase.linkTableWithSdb("static_spawns", "serverdata/static/spawns.sdb"); + spawnDatabase.linkTableWithSdb("static_types", "serverdata/static/types.sdb"); + } catch (FileNotFoundException e) { + e.printStackTrace(); + throw new main.ProjectSWG.CoreException("Unable to load sdb files for StaticService"); + } getSupportingStatement = spawnDatabase.prepareStatement(GET_SUPPORTING_SQL); }