Skip to content
Binary file added FSG_FILE.fsg
Binary file not shown.
Empty file added FSG_FILE.xml
Empty file.
Empty file added XML_FILE.xml
Empty file.
82 changes: 82 additions & 0 deletions src/net/sf/freecol/common/io/FreeColSQLReader.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/**
* Copyright (C) 2002-2022 The FreeCol Team
*
* This file is part of FreeCol.
*
* FreeCol is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* FreeCol is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with FreeCol. If not, see <http://www.gnu.org/licenses/>.
*/

package net.sf.freecol.common.io;
import java.io.Closeable;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.logging.Level;
import java.util.logging.Logger;


/**
* This code creates a `FreeColSQLReader` class that connects to an
* SQLite database and is capable of executing queries.
* It maintains a connection to the SQLite file and a statement object used to
* execute the queries. The `ReadScopeType` is also included in the implementation,
* similar to the XML implementation.
*/
public class FreeColSQLReader implements Closeable {

private static final Logger logger = Logger.getLogger(FreeColSQLReader.class.getName());

/**
* Enum representing the scope of the data reading.
*/
public enum ReadScopeType {
CLIENT, // Only the client-visible information
SERVER, // Full server-visible information
LOAD // Absolutely everything needed to load the game state
}

private Connection connection;
private Statement statement;
private final ReadScopeType readScope;

public FreeColSQLReader(String dbPath, ReadScopeType scope) throws SQLException {
this.connection = DriverManager.getConnection("jdbc:sqlite:" + dbPath);
this.statement = connection.createStatement();
this.readScope = (scope == null) ? ReadScopeType.LOAD : scope;
}

/**
* Executes a query and returns the result set.
*
* @param query SQL query to execute.
* @return ResultSet containing the result of the query.
* @throws SQLException If the query execution fails.
*/
public ResultSet executeQuery(String query) throws SQLException {
return statement.executeQuery(query);
}

@Override
public void close() {
try {
if (statement != null) statement.close();
if (connection != null) connection.close();
} catch (SQLException e) {
logger.log(Level.WARNING, "Failed to close resources", e);
}
}
}
133 changes: 133 additions & 0 deletions src/net/sf/freecol/common/io/FreeColSQLWriter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
/**
* Copyright (C) 2002-2022 The FreeCol Team
*
* This file is part of FreeCol.
*
* FreeCol is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* FreeCol is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with FreeCol. If not, see <http://www.gnu.org/licenses/>.
*/

package net.sf.freecol.common.io;

import java.io.Closeable;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.Logger;


/**
* `FreeColSQLWriter` class to save game data into an SQLite database.
* It consists of the `saveGameData` method that saves general game data,
* player data, turn data, and tile data. Additional game components can be saved
* in a similar manner using prepared statements. The `close()`
* method ensures that the database connection is closed when done.
*/
public class FreeColSQLWriter implements Closeable {

private static final Logger logger = Logger.getLogger(FreeColSQLWriter.class.getName());

/**
* Enum representing the scope of the data writing.
*/
public enum WriteScopeType {
CLIENT, // Only the client-visible information
SERVER, // Full server-visible information
SAVE // Absolutely everything needed to save the game state
};

private Connection connection;
private WriteScopeType writeScope;

public FreeColSQLWriter(String dbPath, WriteScopeType scope) throws SQLException {
this.connection = DriverManager.getConnection("jdbc:sqlite:" + dbPath);
this.writeScope = (scope == null) ? WriteScopeType.SAVE : scope;
}

/**
* Executes an SQL update statement.
*
* @param update SQL update statement to execute.
* @throws SQLException If the statement execution fails.
*/
public void executeUpdate(String update) throws SQLException {
try (PreparedStatement pstmt = connection.prepareStatement(update)) {
pstmt.executeUpdate();
}
}

/**
* Save game data to the SQLite database.
*
* @param game The Game object to save.
* @throws SQLException If any error occurs during saving.
*/
public void saveGameData(Game game) throws SQLException {
// Save general game data
String sqlGame = "INSERT OR REPLACE INTO game (id, options) VALUES(?, ?)";
try (PreparedStatement pstmt = connection.prepareStatement(sqlGame)) {
pstmt.setInt(1, game.getId());
pstmt.setString(2, game.getGameOptions().toString());
pstmt.executeUpdate();
}

// Save player data
String sqlPlayer = "INSERT OR REPLACE INTO player (id, name, nation) VALUES(?, ?, ?)";
for (Player player : game.getPlayers()) {
try (PreparedStatement pstmt = connection.prepareStatement(sqlPlayer)) {
pstmt.setInt(1, player.getId());
pstmt.setString(2, player.getName());
pstmt.setString(3, player.getNation().getName());
pstmt.executeUpdate();
}
}

// Save turn data
String sqlTurn = "UPDATE game SET turn = ? WHERE id
// Save turn data
String sqlTurn = "UPDATE game SET turn = ? WHERE id = ?";
try (PreparedStatement pstmt = connection.prepareStatement(sqlTurn)) {
pstmt.setInt(1, game.getTurn());
pstmt.setInt(2, game.getId());
pstmt.executeUpdate();
}

// Save tile data
String sqlTile = "INSERT OR REPLACE INTO tile (x, y, terrain_type, owner, game_id) VALUES(?, ?, ?, ?, ?)";
for (Tile tile : game.getMap().getAllTiles()) {
try (PreparedStatement pstmt = connection.prepareStatement(sqlTile)) {
pstmt.setInt(1, tile.getX());
pstmt.setInt(2, tile.getY());
pstmt.setString(3, tile.getTerrainType().getName());
pstmt.setString(4, tile.getOwner().getName());
pstmt.setInt(5, game.getId());
pstmt.executeUpdate();
}
}

// Additional game components can be saved similarly with prepared statements
}

@Override
public void close() {
try {
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
logger.log(Level.WARNING, "Failed to close resources", e);
}
}
}
1 change: 1 addition & 0 deletions src/net/sf/freecol/common/io/FreeColXMLWriter.java
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,7 @@ public void writeAttribute(String attributeName, Enum<?> value)
*/
public void writeAttribute(String attributeName, Object value)
throws XMLStreamException {
logger.log(Level.WARNING, "Writing attribute:" + attributeName + ", with value of: " + String.valueOf(value));
xmlStreamWriter.writeAttribute(attributeName, String.valueOf(value));
}

Expand Down
84 changes: 84 additions & 0 deletions src/net/sf/freecol/common/model/sql/generate_sql_tables.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
-- Create game table
CREATE TABLE IF NOT EXISTS game (
id INTEGER PRIMARY KEY,
difficulty TEXT,
mapFile TEXT,
gameOptions TEXT,
language TEXT,
turns INTEGER,
graphicalOptions TEXT,
soundOptions TEXT,
turn INTEGER
);

-- Create player table
CREATE TABLE IF NOT EXISTS player (
id INTEGER PRIMARY KEY,
game_id INTEGER,
name TEXT,
color TEXT,
flag TEXT,
playerPreferences TEXT,
FOREIGN KEY (game_id) REFERENCES game (id) ON DELETE CASCADE
);

-- Create native table
CREATE TABLE IF NOT EXISTS natives (
id INTEGER PRIMARY KEY,
game_id INTEGER,
name TEXT,
color TEXT,
type TEXT,
FOREIGN KEY (game_id) REFERENCES game (id) ON DELETE CASCADE
);

-- Create faction table
CREATE TABLE IF NOT EXISTS factions (
id INTEGER PRIMARY KEY,
game_id INTEGER,
name TEXT,
FOREIGN KEY (game_id) REFERENCES game (id) ON DELETE CASCADE
);

-- ... The existing unit, city, and tile table creation statements remain unchanged

-- Create building table
CREATE TABLE IF NOT EXISTS building (
id INTEGER PRIMARY KEY,
game_id INTEGER,
city_id INTEGER,
type TEXT,
x INTEGER,
y INTEGER,
FOREIGN KEY (game_id) REFERENCES game (id) ON DELETE CASCADE,
FOREIGN KEY (city_id) REFERENCES city (id) ON DELETE CASCADE
);

-- Create resource (goods) table
CREATE TABLE IF NOT EXISTS goods (
id INTEGER PRIMARY KEY,
game_id INTEGER,
type TEXT,
available INTEGER,
FOREIGN KEY (game_id) REFERENCES game (id) ON DELETE CASCADE
);

-- Create highscore table
CREATE TABLE IF NOT EXISTS highScores (
id INTEGER PRIMARY KEY,
game_id INTEGER,
player_id INTEGER,
score INTEGER,
FOREIGN KEY (game_id) REFERENCES game (id) ON DELETE CASCADE,
FOREIGN KEY (player_id) REFERENCES player (id) ON DELETE CASCADE
);

-- Create foundingFathers table
CREATE TABLE IF NOT EXISTS foundingFathers (
id INTEGER PRIMARY KEY,
game_id INTEGER,
name TEXT,
bonus TEXT,
improvement TEXT,
FOREIGN KEY (game_id) REFERENCES game (id) ON DELETE CASCADE
);
15 changes: 8 additions & 7 deletions src/net/sf/freecol/tools/FSGConverter.java
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,9 @@ private void convertToXML(InputStream ins, OutputStream outs)
return;
}
in.reset();
if (!"<?xml".equals(new String(buf, StandardCharsets.UTF_8))) {
in = new BufferedInputStream(new GZIPInputStream(ins));
}
// if (!"<?xml".equals(new String(buf, StandardCharsets.UTF_8))) {
// in = new BufferedInputStream(new GZIPInputStream(ins));
// }

// Support for XML comments has not been added:
int indent = 0;
Expand Down Expand Up @@ -183,15 +183,16 @@ private static void printUsage() {
* @param args The command-line parameters.
*/
public static void main(String[] args) {
if (args.length >= 2 && args[0].endsWith("output:xml")) {
File in = new File(args[1]);
String[] actual_args = {"FSG_FILE:xml", "5c812fa4_Голландцы_1494.fsg"};
if (actual_args.length >= 2 && actual_args[0].endsWith("output:xml")) {
File in = new File(actual_args[1]);
if (!in.exists()) {
printUsage();
System.exit(1);
}
File out;
if (args.length >= 3) {
out = new File(args[2]);
if (actual_args.length >= 3) {
out = new File(actual_args[2]);
} else {
String name = in.getName();
String filename = name.replaceAll("." + FreeCol.FREECOL_SAVE_EXTENSION, ".xml");
Expand Down
Loading