diff --git a/src/main/java/net/licks92/wirelessredstone/storage/DatabaseClient.java b/src/main/java/net/licks92/wirelessredstone/storage/DatabaseClient.java new file mode 100644 index 0000000..c1ac8f3 --- /dev/null +++ b/src/main/java/net/licks92/wirelessredstone/storage/DatabaseClient.java @@ -0,0 +1,770 @@ +package net.licks92.wirelessredstone.storage; + +import com.tylersuehr.sql.ContentValues; +import com.tylersuehr.sql.SQLiteDatabase; +import com.tylersuehr.sql.SQLiteOpenHelper; +import net.licks92.wirelessredstone.ConfigManager; +import net.licks92.wirelessredstone.Utils; +import net.licks92.wirelessredstone.WirelessRedstone; +import net.licks92.wirelessredstone.signs.SignType; +import net.licks92.wirelessredstone.signs.WirelessChannel; +import net.licks92.wirelessredstone.signs.WirelessPoint; +import net.licks92.wirelessredstone.signs.WirelessReceiver; +import net.licks92.wirelessredstone.signs.WirelessReceiverClock; +import net.licks92.wirelessredstone.signs.WirelessReceiverDelayer; +import net.licks92.wirelessredstone.signs.WirelessReceiverInverter; +import net.licks92.wirelessredstone.signs.WirelessReceiverSwitch; +import net.licks92.wirelessredstone.signs.WirelessScreen; +import net.licks92.wirelessredstone.signs.WirelessTransmitter; +import org.bukkit.block.BlockFace; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.Objects; +import java.util.stream.Collectors; + +public class DatabaseClient extends SQLiteOpenHelper { + private static final String DB_NAME = "WirelessRedstoneDatabase"; + private static final int DB_VERSION = 1; + + private static final String TB_CHANNELS = "channel"; + private static final String TB_OWNERS = "owner"; + private static final String TB_TRANSMITTERS = "transmitter"; + private static final String TB_RECEIVERS = "receiver"; + private static final String TB_SCREENS = "screen"; + private static final String TB_INVERTERS = "inverter"; + private static final String TB_DELAYERS = "delayer"; + private static final String TB_SWITCH = "switch"; + private static final String TB_CLOCKS = "clock"; + + private static volatile DatabaseClient instance; + private final SQLiteDatabase db; + + + private DatabaseClient(String channelFolder) { + super(channelFolder + File.separator + DB_NAME, DB_VERSION); + this.db = getWritableInstance(); + } + + protected static synchronized DatabaseClient getInstance() { + if (instance == null) { + throw new IllegalStateException("DatabaseClient hasn't been initialized"); + } + + return instance; + } + + protected static synchronized DatabaseClient init(String channelFolder) { + if (instance == null) { + Objects.requireNonNull(channelFolder, "Channel folder can't be null"); + + instance = new DatabaseClient(channelFolder); + } + return instance; + } + + @Override + protected void onCreate(SQLiteDatabase db) { + try (BufferedReader br = new BufferedReader(new InputStreamReader( + Objects.requireNonNull(WirelessRedstone.getInstance().getResource("database/Database_1.sql")), + StandardCharsets.UTF_8))) { + String sql = br.lines().collect(Collectors.joining(System.lineSeparator()));; + db.execSql(sql); + } catch (IOException ex) { + WirelessRedstone.getWRLogger().info("There was an error while initializing the database."); + + ex.printStackTrace(); + } + } + + @Override + protected void onUpdate(SQLiteDatabase db, int oldVersion, int newVersion) { + WirelessRedstone.getWRLogger().info("Updating SQLite database. This could take a while. As a precaution, a backup will be created."); + if (WirelessRedstone.getStorage().backupData()) { + if (oldVersion == 0) { + try { + performUpdate1(db); + } catch (SQLException | IOException ex) { + ex.printStackTrace(); + throw new RuntimeException("There was an error while performing database update 1."); + } + } + } else { + throw new RuntimeException("There was an error while backing up the database. The channels folder couldn't be accessed."); + } + WirelessRedstone.getWRLogger().info("Updating SQLite database done."); + } + + /** + * Expose the SQLiteDatabase to anything that wants to use it. + */ + protected SQLiteDatabase getDatabase() { + return db; + } + + protected Collection getAllChannels() { + Collection channels = new ArrayList<>(); + + try { + ResultSet resultSet = getDatabase().query(TB_CHANNELS, null, null, null); + while (resultSet.next()) { + channels.add(new WirelessChannel(resultSet.getString("name"), resultSet.getBoolean("locked"))); + } + + resultSet.close(); + + Iterator iterator = channels.iterator(); + while (iterator.hasNext()) { + WirelessChannel channel = iterator.next(); + + resultSet = getDatabase().query(TB_OWNERS, new String[]{"user"}, "[channel_name]='" + channel.getName() + "'", null, null); + while (resultSet.next()) { + channel.addOwner(resultSet.getString("user")); + } + + resultSet.close(); + + resultSet = getDatabase().query(TB_TRANSMITTERS, "[channel_name]='" + channel.getName() + "'", null, null); + while (resultSet.next()) { + WirelessPoint point = new WirelessTransmitter( + resultSet.getInt("x"), + resultSet.getInt("y"), + resultSet.getInt("z"), + resultSet.getString("world"), + resultSet.getInt("is_wallsign") != 0, + BlockFace.valueOf(resultSet.getString("direction")), + resultSet.getString("owner") + ); + channel.addWirelessPoint(point); + WirelessRedstone.getWRLogger().debug("Transmitter found: " + point); + } + + resultSet.close(); + + resultSet = getDatabase().query(TB_RECEIVERS, "[channel_name]='" + channel.getName() + "'", null, null); + while (resultSet.next()) { + WirelessPoint point = new WirelessReceiver( + resultSet.getInt("x"), + resultSet.getInt("y"), + resultSet.getInt("z"), + resultSet.getString("world"), + resultSet.getInt("is_wallsign") != 0, + BlockFace.valueOf(resultSet.getString("direction")), + resultSet.getString("owner") + ); + channel.addWirelessPoint(point); + WirelessRedstone.getWRLogger().debug("Receiver found: " + point); + } + + resultSet.close(); + + resultSet = getDatabase().query(TB_SCREENS, "[channel_name]='" + channel.getName() + "'", null, null); + while (resultSet.next()) { + WirelessPoint point = new WirelessScreen( + resultSet.getInt("x"), + resultSet.getInt("y"), + resultSet.getInt("z"), + resultSet.getString("world"), + resultSet.getInt("is_wallsign") != 0, + BlockFace.valueOf(resultSet.getString("direction")), + resultSet.getString("owner") + ); + channel.addWirelessPoint(point); + WirelessRedstone.getWRLogger().debug("Screen found: " + point); + } + + resultSet.close(); + + resultSet = getDatabase().query(TB_INVERTERS, "[channel_name]='" + channel.getName() + "'", null, null); + while (resultSet.next()) { + WirelessPoint point = new WirelessReceiverInverter( + resultSet.getInt("x"), + resultSet.getInt("y"), + resultSet.getInt("z"), + resultSet.getString("world"), + resultSet.getInt("is_wallsign") != 0, + BlockFace.valueOf(resultSet.getString("direction")), + resultSet.getString("owner") + ); + channel.addWirelessPoint(point); + WirelessRedstone.getWRLogger().debug("Inverter found: " + point); + } + + resultSet.close(); + + resultSet = getDatabase().query(TB_DELAYERS, "[channel_name]='" + channel.getName() + "'", null, null); + while (resultSet.next()) { + WirelessPoint point = new WirelessReceiverDelayer( + resultSet.getInt("x"), + resultSet.getInt("y"), + resultSet.getInt("z"), + resultSet.getString("world"), + resultSet.getInt("is_wallsign") != 0, + BlockFace.valueOf(resultSet.getString("direction")), + resultSet.getString("owner"), + resultSet.getInt("delay") + ); + channel.addWirelessPoint(point); + WirelessRedstone.getWRLogger().debug("Delayer found: " + point); + } + + resultSet.close(); + + resultSet = getDatabase().query(TB_SWITCH, "[channel_name]='" + channel.getName() + "'", null, null); + while (resultSet.next()) { + WirelessPoint point = new WirelessReceiverSwitch( + resultSet.getInt("x"), + resultSet.getInt("y"), + resultSet.getInt("z"), + resultSet.getString("world"), + resultSet.getInt("is_wallsign") != 0, + BlockFace.valueOf(resultSet.getString("direction")), + resultSet.getString("owner"), + resultSet.getBoolean("powered") + ); + channel.addWirelessPoint(point); + WirelessRedstone.getWRLogger().debug("Switch found: " + point); + } + + resultSet.close(); + + resultSet = getDatabase().query(TB_CLOCKS, "[channel_name]='" + channel.getName() + "'", null, null); + while (resultSet.next()) { + WirelessPoint point = new WirelessReceiverClock( + resultSet.getInt("x"), + resultSet.getInt("y"), + resultSet.getInt("z"), + resultSet.getString("world"), + resultSet.getInt("is_wallsign") != 0, + BlockFace.valueOf(resultSet.getString("direction")), + resultSet.getString("owner"), + resultSet.getInt("delay") + ); + channel.addWirelessPoint(point); + WirelessRedstone.getWRLogger().debug("Clock found: " + point); + } + + resultSet.close(); + } + } catch (SQLException ex) { + WirelessRedstone.getWRLogger().severe("Couldn't retrieve channels from the database!"); + + ex.printStackTrace(); + } + return channels; + } + + protected void recreateDatabase() { + onCreate(getDatabase()); + } + + protected boolean insertWirelessPoint(WirelessChannel channel, WirelessPoint point) { + if (point == null) { + throw new IllegalArgumentException("WirelessPoint can not be null."); + } + + try { + if (isWirelessPointInDb(point)) { + WirelessRedstone.getWRLogger().warning("WirelesPoint " + point + " is a duplicate in the storage. Skipping saving the WirelessPoint"); + return false; + } + } catch (SQLException ex) { + WirelessRedstone.getWRLogger().warning("Database exception, enable debug mode to see the full stacktrace."); + + if (ConfigManager.getConfig().getDebugMode()) { + ex.printStackTrace(); + } + } + + ContentValues values = new ContentValues(); + try { + if (!isChannelInDb(channel.getName())) { + values.put("name", escape(channel.getName())); + values.put("locked", channel.isLocked()); + getDatabase().insert(TB_CHANNELS, values); + WirelessRedstone.getWRLogger().debug("Channel created in database. " + channel.getName()); + } + } catch (SQLException ex) { + WirelessRedstone.getWRLogger().warning("Database exception, enable debug mode to see the full stacktrace."); + + if (ConfigManager.getConfig().getDebugMode()) { + ex.printStackTrace(); + } + } + + String table; + values = new ContentValues(); + values.put("x", point.getX()); + values.put("y", point.getY()); + values.put("z", point.getZ()); + values.put("world", point.getWorld()); + values.put("channel_name", channel.getName()); + values.put("direction", point.getDirection().toString()); + values.put("owner", point.getOwner()); + values.put("is_wallsign", point.isWallSign()); + + if (point instanceof WirelessTransmitter) { + table = TB_TRANSMITTERS; + } else if (point instanceof WirelessScreen) { + table = TB_SCREENS; + } else if (point instanceof WirelessReceiver) { + if (point instanceof WirelessReceiverInverter) { + table = TB_INVERTERS; + } else if (point instanceof WirelessReceiverDelayer) { + table = TB_DELAYERS; + values.put("delay", ((WirelessReceiverDelayer) point).getDelay()); + } else if (point instanceof WirelessReceiverSwitch) { + table = TB_SWITCH; + values.put("powered", ((WirelessReceiverSwitch) point).isActive()); + } else if (point instanceof WirelessReceiverClock) { + table = TB_CLOCKS; + values.put("delay", ((WirelessReceiverClock) point).getDelay()); + } else { + table = TB_RECEIVERS; + } + } else { + WirelessRedstone.getWRLogger().debug("Can't add wirelesspoint to database. Couldn't find what type the wirelesspoint is."); + WirelessRedstone.getWRLogger().debug(point.toString()); + return false; + } + + getDatabase().insert(table, values); + WirelessRedstone.getWRLogger().debug("Placed new WirelessPoint in the database"); + + return true; + } + + protected void updateSwitch(WirelessReceiverSwitch receiver) { + ContentValues values = new ContentValues(); + values.put("powered", receiver.isActive()); + getDatabase().update(TB_SWITCH, values, + "[x]=" + receiver.getX() + " AND [y]=" + receiver.getY() + " AND [z]=" + receiver.getZ() + " AND [world]='" + receiver.getWorld() + "'"); + } + + protected boolean isChannelInDb(String channelName) throws SQLException { + boolean exists = false; + + ResultSet resultSet = getDatabase().query(TB_CHANNELS, "[name]='" + escape(channelName) + "'", null, null); + while (resultSet.next() && !exists) { + exists = true; + } + + resultSet.close(); + + return exists; + } + + protected boolean isWirelessPointInDb(WirelessPoint point) throws SQLException { + boolean exists = false; + String table; + + if (point instanceof WirelessTransmitter) { + table = TB_TRANSMITTERS; + } else if (point instanceof WirelessScreen) { + table = TB_SCREENS; + } else if (point instanceof WirelessReceiver) { + if (point instanceof WirelessReceiverInverter) { + table = TB_INVERTERS; + } else if (point instanceof WirelessReceiverDelayer) { + table = TB_DELAYERS; + } else if (point instanceof WirelessReceiverSwitch) { + table = TB_SWITCH; + } else if (point instanceof WirelessReceiverClock) { + table = TB_CLOCKS; + } else { + table = TB_RECEIVERS; + } + } else { + WirelessRedstone.getWRLogger().debug("Can't find wirelesspoint in database. Couldn't find what type the wirelesspoint is."); + WirelessRedstone.getWRLogger().debug(point.toString()); + return false; + } + + ResultSet resultSet = getDatabase().query(table, + "[x]=" + point.getX() + " AND [y]=" + point.getY() + " AND [z]=" + point.getZ() + " AND [world]='" + point.getWorld() + "'", + null, null); + while (resultSet.next() && !exists) { + exists = true; + } + + resultSet.close(); + + return exists; + } + + private void performUpdate1(SQLiteDatabase db) throws SQLException, IOException { + Collection channels = new ArrayList<>(); + Collection channelNames = new ArrayList<>(); + int channelIteration = 0; + int progress = 0; + + ResultSet resultSet = db.rawQuery("SELECT [name] FROM [sqlite_master] WHERE [type] = 'table'"); + + while (resultSet.next()) { + WirelessRedstone.getWRLogger().debug("Found channel: " + resultSet.getString(1)); + channelNames.add(resultSet.getString(1)); + } + + resultSet.close(); + + for (String channelName : channelNames) { + if ((int) Math.floor((float) channelIteration / (float) channelNames.size() * 100) % 5 == 0 + && (int) Math.floor((float) channelIteration / (float) channelNames.size() * 100) != progress) { + progress = (int) Math.floor((float) channelIteration / (float) channelNames.size() * 100); + WirelessRedstone.getWRLogger().info("Database upgrade stage 1/2; Progress: " + progress + "%"); + } + + WirelessChannel channel = null; + int channelInfoIteration = 0; + resultSet = db.query(channelName, null, null, null); + + while (resultSet.next()) { + if (channelInfoIteration == 0) { + if (resultSet.getString("name") != null) { + WirelessRedstone.getWRLogger().debug("---------------"); + WirelessRedstone.getWRLogger().debug("Created channel: " + resultSet.getString("name") + " | " + + Collections.singletonList(resultSet.getString("owners")) + " | " + + resultSet.getBoolean("locked") + ); + + channel = new WirelessChannel( + resultSet.getString("name"), + Collections.singletonList(resultSet.getString("owners")), + resultSet.getBoolean("locked") + ); + } + } + + if (channelInfoIteration > 0 && channel == null) { + continue; + } + + WirelessRedstone.getWRLogger().debug("---------------"); + + if (channelInfoIteration > 0) { + if (resultSet.getString("signType") != null) { + String signTypeSerialized = resultSet.getString("signType"); + SignType signType = getSignType(signTypeSerialized); + WirelessRedstone.getWRLogger().debug("SignType " + signType); + + if (signType == null) { + continue; + } + + switch (signType) { + case TRANSMITTER: + WirelessPoint point = new WirelessTransmitter( + resultSet.getInt("x"), + resultSet.getInt("y"), + resultSet.getInt("z"), + resultSet.getString("world"), + resultSet.getInt("isWallSign") != 0, + getBlockFaceOldDatabase(resultSet), + resultSet.getString("signOwner") + ); + channel.addWirelessPoint(point); + + WirelessRedstone.getWRLogger().debug(point.toString()); + break; + case RECEIVER: + point = new WirelessReceiver( + resultSet.getInt("x"), + resultSet.getInt("y"), + resultSet.getInt("z"), + resultSet.getString("world"), + resultSet.getInt("isWallSign") != 0, + getBlockFaceOldDatabase(resultSet), + resultSet.getString("signOwner") + ); + channel.addWirelessPoint(point); + + WirelessRedstone.getWRLogger().debug(point.toString()); + break; + case SCREEN: + point = new WirelessScreen( + resultSet.getInt("x"), + resultSet.getInt("y"), + resultSet.getInt("z"), + resultSet.getString("world"), + resultSet.getInt("isWallSign") != 0, + getBlockFaceOldDatabase(resultSet), + resultSet.getString("signOwner") + ); + channel.addWirelessPoint(point); + + WirelessRedstone.getWRLogger().debug(point.toString()); + break; + case RECEIVER_INVERTER: + point = new WirelessReceiverInverter( + resultSet.getInt("x"), + resultSet.getInt("y"), + resultSet.getInt("z"), + resultSet.getString("world"), + resultSet.getInt("isWallSign") != 0, + getBlockFaceOldDatabase(resultSet), + resultSet.getString("signOwner") + ); + channel.addWirelessPoint(point); + + WirelessRedstone.getWRLogger().debug(point.toString()); + break; + case RECEIVER_DELAYER: + int delay; + + try { + delay = Integer.parseInt(signTypeSerialized.split("_")[2]); + } catch (NumberFormatException e) { + continue; + } + + point = new WirelessReceiverDelayer( + resultSet.getInt("x"), + resultSet.getInt("y"), + resultSet.getInt("z"), + resultSet.getString("world"), + resultSet.getInt("isWallSign") != 0, + getBlockFaceOldDatabase(resultSet), + resultSet.getString("signOwner"), + delay + ); + channel.addWirelessPoint(point); + + WirelessRedstone.getWRLogger().debug(point.toString()); + break; + case RECEIVER_SWITCH: + boolean state; + + state = Boolean.parseBoolean(signTypeSerialized.split("_")[2]); + + point = new WirelessReceiverSwitch( + resultSet.getInt("x"), + resultSet.getInt("y"), + resultSet.getInt("z"), + resultSet.getString("world"), + resultSet.getInt("isWallSign") != 0, + getBlockFaceOldDatabase(resultSet), + resultSet.getString("signOwner"), + state + ); + channel.addWirelessPoint(point); + + WirelessRedstone.getWRLogger().debug(point.toString()); + break; + case RECEIVER_CLOCK: + try { + delay = Integer.parseInt(signTypeSerialized.split("_")[2]); + } catch (NumberFormatException e) { + continue; + } + + point = new WirelessReceiverClock( + resultSet.getInt("x"), + resultSet.getInt("y"), + resultSet.getInt("z"), + resultSet.getString("world"), + resultSet.getInt("isWallSign") != 0, + getBlockFaceOldDatabase(resultSet), + resultSet.getString("signOwner"), + delay + ); + channel.addWirelessPoint(point); + + WirelessRedstone.getWRLogger().debug(point.toString()); + break; + } + } + } + channelInfoIteration++; + } + resultSet.close(); + channels.add(channel); + channelIteration++; + + db.execSql("DROP TABLE IF EXISTS [" + channelName + "];"); + } + + WirelessRedstone.getWRLogger().debug("---------------"); + + onCreate(db); + + progress = 0; + channelIteration = 0; + for (WirelessChannel channel : channels) { + if ((int) Math.floor((float) channelIteration / (float) channels.size() * 100) % 5 == 0 + && (int) Math.floor((float) channelIteration / (float) channels.size() * 100) != progress) { + progress = (int) Math.floor((float) channelIteration / (float) channels.size() * 100); + WirelessRedstone.getWRLogger().info("Database upgrade stage 2/2; Progress: " + progress + "%"); + } + + ContentValues values = new ContentValues(); + values.put("name", channel.getName()); + values.put("locked", channel.isLocked()); + db.insert(TB_CHANNELS, values); + WirelessRedstone.getWRLogger().debug("Inserted channel " + channel.getName()); + + for (String owner : channel.getOwners()) { + values = new ContentValues(); + values.put("channel_name", channel.getName()); + values.put("user", owner); + db.insert(TB_OWNERS, values); + WirelessRedstone.getWRLogger().debug("Inserted owner " + owner + "|" + channel.getName()); + } + + for (WirelessTransmitter point : channel.getTransmitters()) { + values = new ContentValues(); + values.put("x", point.getX()); + values.put("y", point.getY()); + values.put("z", point.getZ()); + values.put("world", point.getWorld()); + values.put("channel_name", channel.getName()); + values.put("direction", point.getDirection().toString()); + values.put("owner", point.getOwner()); + values.put("is_wallsign", point.isWallSign()); + db.insert(TB_TRANSMITTERS, values); + WirelessRedstone.getWRLogger().debug("Inserted transmitter " + point.toString() + "|" + channel.getName()); + } + + for (WirelessScreen point : channel.getScreens()) { + values = new ContentValues(); + values.put("x", point.getX()); + values.put("y", point.getY()); + values.put("z", point.getZ()); + values.put("world", point.getWorld()); + values.put("channel_name", channel.getName()); + values.put("direction", point.getDirection().toString()); + values.put("owner", point.getOwner()); + values.put("is_wallsign", point.isWallSign()); + db.insert(TB_SCREENS, values); + WirelessRedstone.getWRLogger().debug("Inserted screen " + point.toString() + "|" + channel.getName()); + } + + for (WirelessReceiver point : channel.getReceivers()) { + if (point instanceof WirelessReceiverInverter) { + values = new ContentValues(); + values.put("x", point.getX()); + values.put("y", point.getY()); + values.put("z", point.getZ()); + values.put("world", point.getWorld()); + values.put("channel_name", channel.getName()); + values.put("direction", point.getDirection().toString()); + values.put("owner", point.getOwner()); + values.put("is_wallsign", point.isWallSign()); + db.insert(TB_INVERTERS, values); + WirelessRedstone.getWRLogger().debug("Inserted inverter " + point.toString() + "|" + channel.getName()); + } else if (point instanceof WirelessReceiverDelayer) { + values = new ContentValues(); + values.put("x", point.getX()); + values.put("y", point.getY()); + values.put("z", point.getZ()); + values.put("world", point.getWorld()); + values.put("channel_name", channel.getName()); + values.put("direction", point.getDirection().toString()); + values.put("owner", point.getOwner()); + values.put("is_wallsign", point.isWallSign()); + values.put("delay", ((WirelessReceiverDelayer) point).getDelay()); + db.insert(TB_DELAYERS, values); + WirelessRedstone.getWRLogger().debug("Inserted delayer " + point.toString() + "|" + channel.getName()); + } else if (point instanceof WirelessReceiverSwitch) { + values = new ContentValues(); + values.put("x", point.getX()); + values.put("y", point.getY()); + values.put("z", point.getZ()); + values.put("world", point.getWorld()); + values.put("channel_name", channel.getName()); + values.put("direction", point.getDirection().toString()); + values.put("owner", point.getOwner()); + values.put("is_wallsign", point.isWallSign()); + values.put("powered", ((WirelessReceiverSwitch) point).isActive()); + db.insert(TB_SWITCH, values); + WirelessRedstone.getWRLogger().debug("Inserted switch " + point.toString() + "|" + channel.getName()); + } else if (point instanceof WirelessReceiverClock) { + values = new ContentValues(); + values.put("x", point.getX()); + values.put("y", point.getY()); + values.put("z", point.getZ()); + values.put("world", point.getWorld()); + values.put("channel_name", channel.getName()); + values.put("direction", point.getDirection().toString()); + values.put("owner", point.getOwner()); + values.put("is_wallsign", point.isWallSign()); + values.put("delay", ((WirelessReceiverClock) point).getDelay()); + db.insert(TB_CLOCKS, values); + WirelessRedstone.getWRLogger().debug("Inserted clock " + point.toString() + "|" + channel.getName()); + } else { + values = new ContentValues(); + values.put("x", point.getX()); + values.put("y", point.getY()); + values.put("z", point.getZ()); + values.put("world", point.getWorld()); + values.put("channel_name", channel.getName()); + values.put("direction", point.getDirection().toString()); + values.put("owner", point.getOwner()); + values.put("is_wallsign", point.isWallSign()); + db.insert(TB_RECEIVERS, values); + WirelessRedstone.getWRLogger().debug("Inserted receiver " + point.toString() + "|" + channel.getName()); + } + } + channelIteration++; + } + } + + private SignType getSignType(String signTypeSerialized) { + if (signTypeSerialized.equalsIgnoreCase("transmitter")) { + return SignType.TRANSMITTER; + } else if (signTypeSerialized.equalsIgnoreCase("receiver")) { + return SignType.RECEIVER; + } else if (signTypeSerialized.equalsIgnoreCase("screen")) { + return SignType.SCREEN; + } else if (signTypeSerialized.contains("receiver")) { + String[] receiver = signTypeSerialized.split("_"); + + if (receiver[1].equalsIgnoreCase("inverter")) { + return SignType.RECEIVER_INVERTER; + } else if (receiver[1].equalsIgnoreCase("delayer")) { + return SignType.RECEIVER_DELAYER; + } else if (receiver[1].equalsIgnoreCase("switch")) { + return SignType.RECEIVER_SWITCH; + } else if (receiver[1].equalsIgnoreCase("clock")) { + return SignType.RECEIVER_CLOCK; + } + } + + return null; + } + + private BlockFace getBlockFaceOldDatabase(ResultSet resultSet) throws SQLException { + Object directionObject = resultSet.getObject("direction"); + + if (directionObject instanceof Integer) { + return Utils.getBlockFace(false, (int) directionObject); + } else if (directionObject instanceof String) { + return BlockFace.valueOf(directionObject.toString().toUpperCase()); + } else { + throw new IllegalArgumentException("Direction (" + directionObject + ") row inside database isn't parsable."); + } + } + + private String escape(String str) { + if (str == null || str.length() == 0) { + throw new IllegalArgumentException(); + } + + str = str.replace("\\", "\\\\"); + str = str.replace("'", "\\'"); + str = str.replace("\0", "\\0"); + str = str.replace("\n", "\\n"); + str = str.replace("\r", "\\r"); + str = str.replace("\"", "\\\""); + str = str.replace("\\x1a", "\\Z"); + return str; + } +} \ No newline at end of file diff --git a/src/main/java/net/licks92/wirelessredstone/storage/SQLiteStorage.java b/src/main/java/net/licks92/wirelessredstone/storage/SQLiteStorage.java new file mode 100644 index 0000000..e57a8a2 --- /dev/null +++ b/src/main/java/net/licks92/wirelessredstone/storage/SQLiteStorage.java @@ -0,0 +1,113 @@ +package net.licks92.wirelessredstone.storage; + +import net.licks92.wirelessredstone.signs.WirelessChannel; +import net.licks92.wirelessredstone.signs.WirelessPoint; +import net.licks92.wirelessredstone.signs.WirelessReceiver; +import net.licks92.wirelessredstone.signs.WirelessReceiverSwitch; +import net.licks92.wirelessredstone.WirelessRedstone; + +import java.io.File; +import java.util.Collection; +import java.util.Objects; + +public class SQLiteStorage extends StorageConfiguration { + + private final File channelFolder; + + public SQLiteStorage(String channelFolder) { + this.channelFolder = new File(WirelessRedstone.getInstance().getDataFolder(), channelFolder); + } + + @Override + public boolean initStorage() { + try { + DatabaseClient.init(channelFolder.toString()); + WirelessRedstone.getStorageManager().updateChannels(false); + + StorageType oldStorageType = canConvertFromType(); + if (oldStorageType != null) { + return WirelessRedstone.getStorageManager().moveStorageFromType(oldStorageType); + } + + return true; + } catch (RuntimeException ex) { + WirelessRedstone.getWRLogger().severe("There was an error accessing the database!"); + ex.printStackTrace(); + return false; + } + } + + @Override + public boolean close() { + DatabaseClient.getInstance().getDatabase().close(); + return true; + } + + @Override + protected Collection getAllChannels() { + return DatabaseClient.getInstance().getAllChannels(); + } + + @Override + public boolean createChannel(WirelessChannel channel) { + channel.getSigns() + .forEach(wirelessPoint -> DatabaseClient.getInstance().insertWirelessPoint(channel, wirelessPoint)); + + return super.createChannel(channel); + } + + @Override + public boolean createWirelessPoint(String channelName, WirelessPoint wirelessPoint) { + WirelessChannel channel = WirelessRedstone.getStorageManager().getChannel(channelName); + channel.addWirelessPoint(wirelessPoint); + + DatabaseClient.getInstance().insertWirelessPoint(channel, wirelessPoint); + + return super.createWirelessPoint(channelName, wirelessPoint); + } + + @Override + public boolean removeWirelessPoint(String channelName, WirelessPoint wirelessPoint) { + WirelessChannel channel = WirelessRedstone.getStorageManager().getChannel(channelName); + channel.removeWirelessPoint(wirelessPoint); + + return super.removeWirelessPoint(channelName, wirelessPoint); + } + + @Override + public boolean updateChannel(String channelName, WirelessChannel channel) { + return super.updateChannel(channelName, channel); + } + + @Override + public boolean removeChannel(String channelName, boolean removeSigns) { + return super.removeChannel(channelName, removeSigns); + } + + @Override + public boolean wipeData() { + DatabaseClient.getInstance().recreateDatabase(); + + return super.wipeData(); + } + + @Override + protected StorageType canConvertFromType() { + for (File file : Objects.requireNonNull(channelFolder.listFiles())) { + if (file.getName().contains(".yml")) { + return StorageType.YAML; + } + } + + return null; + } + + @Override + public void updateSwitchState(WirelessChannel channel) { + for (WirelessReceiver receiver : channel.getReceivers()) { + if (receiver instanceof WirelessReceiverSwitch) { + DatabaseClient.getInstance().updateSwitch((WirelessReceiverSwitch) receiver); + } + } + } +} diff --git a/src/main/java/net/licks92/wirelessredstone/storage/StorageConfiguration.java b/src/main/java/net/licks92/wirelessredstone/storage/StorageConfiguration.java new file mode 100644 index 0000000..f10961d --- /dev/null +++ b/src/main/java/net/licks92/wirelessredstone/storage/StorageConfiguration.java @@ -0,0 +1,164 @@ +package net.licks92.wirelessredstone.storage; + +import net.licks92.wirelessredstone.WirelessRedstone; +import net.licks92.wirelessredstone.signs.WirelessChannel; +import net.licks92.wirelessredstone.signs.WirelessPoint; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +public abstract class StorageConfiguration { + + public abstract boolean initStorage(); + + public abstract boolean close(); + + protected abstract Collection getAllChannels(); + + public abstract void updateSwitchState(WirelessChannel channel); + + protected abstract StorageType canConvertFromType(); + + public boolean createChannel(WirelessChannel channel) { + WirelessRedstone.getStorageManager().updateList(channel.getName(), channel); + return true; + } + + public boolean createWirelessPoint(String channelName, WirelessPoint wirelessPoint) { + WirelessChannel channel = WirelessRedstone.getStorageManager().getChannel(channelName); + //TODO: Investigate if this duplicates the wirelesspoint into the channel + channel.addWirelessPoint(wirelessPoint); + WirelessRedstone.getStorageManager().updateList(channelName, channel); + return true; + } + + public boolean removeWirelessPoint(String channelName, WirelessPoint wirelessPoint) { + WirelessChannel channel = WirelessRedstone.getStorageManager().getChannel(channelName); + //TODO: Investigate if this duplicates the wirelesspoint into the channel + channel.removeWirelessPoint(wirelessPoint); + + if (channel.isEmpty()) { + WirelessRedstone.getStorage().removeChannel(channelName, false); + } else { + WirelessRedstone.getStorageManager().updateList(channelName, channel); + } + return true; + } + + public boolean updateChannel(String channelName, WirelessChannel channel) { + WirelessRedstone.getStorageManager().updateList(channel.getName(), channel); + return true; + } + + public boolean removeChannel(String channelName, boolean removeSigns) { + if (removeSigns) { + WirelessChannel channel = WirelessRedstone.getStorageManager().getChannel(channelName); + + for (WirelessPoint point : channel.getSigns()) { + World world = Bukkit.getWorld(point.getWorld()); + + if (world == null) + continue; + + Location loc = new Location(world, point.getX(), point.getY(), point.getZ()); + loc.getBlock().setType(Material.AIR); + } + } + WirelessRedstone.getStorageManager().updateList(channelName, null); + + return true; + } + + public int purgeData() { + int response = 0; + + for (Map.Entry> entry : WirelessRedstone.getSignManager().getAllInvalidPoints().entrySet()) { + for (WirelessPoint point : entry.getValue()) { + if (!WirelessRedstone.getStorage().removeWirelessPoint(entry.getKey().getName(), point)) { + response = -1; + break; + } + + WirelessRedstone.getWRLogger().debug("Purged WirelessPoint " + point.getLocation().toString() + " because the location is invalid."); + + response++; + } + } + + List emptyChannels = new ArrayList<>(); + for (WirelessChannel channel : WirelessRedstone.getStorageManager().getChannels()) { + if (channel.isEmpty()) { + emptyChannels.add(channel); + } + } + + for (WirelessChannel channel : emptyChannels) { + WirelessRedstone.getStorage().removeChannel(channel.getName(), false); + response++; + } + + return response; + } + + public boolean backupData() { + byte[] buffer = new byte[1024]; + + if (!(new File(WirelessRedstone.getInstance().getDataFolder(), WirelessRedstone.CHANNEL_FOLDER).exists())) { + return false; + } + + try { + String zipName = "WRBackup " + + Calendar.getInstance().get(Calendar.DAY_OF_MONTH) + "-" + + Calendar.getInstance().get(Calendar.MONTH) + "-" + + Calendar.getInstance().get(Calendar.YEAR) + "_" + + Calendar.getInstance().get(Calendar.HOUR_OF_DAY) + "." + + Calendar.getInstance().get(Calendar.MINUTE) + "." + + Calendar.getInstance().get(Calendar.SECOND); + FileOutputStream fos = new FileOutputStream(WirelessRedstone.getInstance().getDataFolder() + File.separator + zipName + ".zip"); + ZipOutputStream zos = new ZipOutputStream(fos); + + for (File file : (new File(WirelessRedstone.getInstance().getDataFolder(), WirelessRedstone.CHANNEL_FOLDER)).listFiles()) { + + ZipEntry ze = new ZipEntry(file.getName()); + zos.putNextEntry(ze); + + FileInputStream in = new FileInputStream(file); + + int len; + while ((len = in.read(buffer)) > 0) { + zos.write(buffer, 0, len); + } + + in.close(); + } + + zos.closeEntry(); + zos.close(); + } catch (IOException ex) { + ex.printStackTrace(); + return false; + } + + return true; + } + + public boolean wipeData() { + WirelessRedstone.getStorageManager().wipeList(); + return true; + } + +} diff --git a/src/main/java/net/licks92/wirelessredstone/storage/StorageManager.java b/src/main/java/net/licks92/wirelessredstone/storage/StorageManager.java new file mode 100644 index 0000000..484fb85 --- /dev/null +++ b/src/main/java/net/licks92/wirelessredstone/storage/StorageManager.java @@ -0,0 +1,150 @@ +package net.licks92.wirelessredstone.storage; + +import net.licks92.wirelessredstone.ConfigManager; +import net.licks92.wirelessredstone.signs.WirelessChannel; +import net.licks92.wirelessredstone.signs.WirelessPoint; +import net.licks92.wirelessredstone.WirelessRedstone; +import org.bukkit.Bukkit; +import org.bukkit.scheduler.BukkitTask; + +import java.io.File; +import java.io.FilenameFilter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; + +public class StorageManager { + + private final ConcurrentHashMap allChannels = new ConcurrentHashMap<>(); + private final BukkitTask refreshingTask; + private final StorageType storageType; + private final StorageConfiguration storage; + private final String channelFolder; + private final File channelFolderFile; + + public StorageManager(StorageType type, String channelFolder) { + this.storageType = type; + this.channelFolder = channelFolder; + + this.channelFolderFile = new File(WirelessRedstone.getInstance().getDataFolder(), channelFolder); + this.channelFolderFile.mkdir(); + switch (type) { + case SQLITE: + storage = new SQLiteStorage(channelFolder); + break; + case YAML: + storage = new YamlStorage(channelFolder); + break; + default: + storage = new YamlStorage(channelFolder); + break; + } + + int refreshRate = ConfigManager.getConfig().getCacheRefreshRate(); + if (refreshRate < 60) { + refreshRate = 60; + ConfigManager.getConfig().setValue(ConfigManager.ConfigPaths.CACHEREFRESHRATE, 60); + } + if (refreshRate > 480) { + refreshRate = 480; + ConfigManager.getConfig().setValue(ConfigManager.ConfigPaths.CACHEREFRESHRATE, 480); + } + + int timeInTicks = refreshRate * 20; + refreshingTask = Bukkit.getScheduler().runTaskTimerAsynchronously(WirelessRedstone.getInstance(), new Runnable() { + @Override + public void run() { + //TODO: Check if this is necessary +// updateList(); + } + }, timeInTicks, timeInTicks); + } + + public void updateChannels(boolean async) { + if (async) { + Bukkit.getServer().getScheduler().runTaskAsynchronously(WirelessRedstone.getInstance(), this::updateList); + } else { + updateList(); + } + } + + protected void updateList() { + allChannels.clear(); + Collection channels = getStorage().getAllChannels(); + + channels.forEach(channel -> allChannels.put(channel.getName(), channel)); + } + + protected void updateList(String channelName, WirelessChannel channel) { + if (channel == null) { + allChannels.remove(channelName); + } else { + allChannels.put(channelName, channel); + } + } + + protected void wipeList() { + allChannels.clear(); + } + + public StorageConfiguration getStorage() { + return storage; + } + + public Collection getChannels() { + return allChannels.values(); + } + + public WirelessChannel getChannel(String channelName) { + return allChannels.get(channelName); + } + + public Collection getAllSigns() { + List collection = new ArrayList<>(); + getChannels().stream() + .map(WirelessChannel::getSigns) + .forEach(collection::addAll); + return collection; + } + + protected boolean moveStorageFromType(StorageType storageType) { + if (!getStorage().backupData()) { + WirelessRedstone.getWRLogger().severe("Porting data to other storage type failed due to a backup problem!"); + return false; + } + + StorageConfiguration storage; + if (storageType == StorageType.YAML) { + storage = new YamlStorage(channelFolder); + } else if (storageType == StorageType.SQLITE) { + storage = new SQLiteStorage(channelFolder); + DatabaseClient.init(new File(WirelessRedstone.getInstance().getDataFolder(), channelFolder).toString()); + } else { + return false; + } + + Collection channels = storage.getAllChannels(); + channels.forEach(getStorage()::createChannel); + storage.close(); + + if (storageType == StorageType.YAML) { + final FilenameFilter filter = (dir, name) -> name.toLowerCase().endsWith(".yml"); + + Arrays.stream(Objects.requireNonNull(channelFolderFile.listFiles(filter))) + .forEach(File::delete); + } else { + final FilenameFilter filter = (dir, name) -> name.toLowerCase().endsWith(".db"); + + Arrays.stream(Objects.requireNonNull(channelFolderFile.listFiles(filter))) + .forEach(File::delete); + } + + WirelessRedstone.getStorageManager().updateChannels(false); + + return true; + } + +} diff --git a/src/main/java/net/licks92/wirelessredstone/storage/StorageType.java b/src/main/java/net/licks92/wirelessredstone/storage/StorageType.java new file mode 100644 index 0000000..ed7bc62 --- /dev/null +++ b/src/main/java/net/licks92/wirelessredstone/storage/StorageType.java @@ -0,0 +1,5 @@ +package net.licks92.wirelessredstone.storage; + +public enum StorageType { + SQLITE, YAML +} diff --git a/src/main/java/net/licks92/wirelessredstone/storage/YamlStorage.java b/src/main/java/net/licks92/wirelessredstone/storage/YamlStorage.java new file mode 100644 index 0000000..20f3abe --- /dev/null +++ b/src/main/java/net/licks92/wirelessredstone/storage/YamlStorage.java @@ -0,0 +1,208 @@ +package net.licks92.wirelessredstone.storage; + +import net.licks92.wirelessredstone.signs.WirelessChannel; +import net.licks92.wirelessredstone.signs.WirelessPoint; +import net.licks92.wirelessredstone.signs.WirelessReceiver; +import net.licks92.wirelessredstone.signs.WirelessReceiverClock; +import net.licks92.wirelessredstone.signs.WirelessReceiverDelayer; +import net.licks92.wirelessredstone.signs.WirelessReceiverInverter; +import net.licks92.wirelessredstone.signs.WirelessReceiverSwitch; +import net.licks92.wirelessredstone.signs.WirelessScreen; +import net.licks92.wirelessredstone.signs.WirelessTransmitter; +import net.licks92.wirelessredstone.WirelessRedstone; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.configuration.serialization.ConfigurationSerialization; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FilenameFilter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Objects; + +@SuppressWarnings("ResultOfMethodCallIgnored") +public class YamlStorage extends StorageConfiguration { + + private final File channelFolder; + private final FilenameFilter yamlFilter = (dir, name) -> name.toLowerCase().endsWith(".yml"); + + public YamlStorage(String channelFolder) { + this.channelFolder = new File(WirelessRedstone.getInstance().getDataFolder(), channelFolder); + + //Initialize the serialization + ConfigurationSerialization.registerClass(WirelessChannel.class, "WirelessChannel"); + ConfigurationSerialization.registerClass(WirelessTransmitter.class, "WirelessTransmitter"); + ConfigurationSerialization.registerClass(WirelessScreen.class, "WirelessScreen"); + ConfigurationSerialization.registerClass(WirelessReceiver.class, "WirelessReceiver"); + ConfigurationSerialization.registerClass(WirelessReceiverInverter.class, "WirelessReceiverInverter"); + ConfigurationSerialization.registerClass(WirelessReceiverDelayer.class, "WirelessReceiverDelayer"); + ConfigurationSerialization.registerClass(WirelessReceiverClock.class, "WirelessReceiverClock"); + ConfigurationSerialization.registerClass(WirelessReceiverSwitch.class, "WirelessReceiverSwitch"); + } + + + @Override + public boolean initStorage() { + //TODO: Initstorage + + WirelessRedstone.getStorageManager().updateChannels(false); + + StorageType oldStorageType = canConvertFromType(); + if (oldStorageType != null) { + return WirelessRedstone.getStorageManager().moveStorageFromType(oldStorageType); + } + + return true; + } + + @Override + public boolean close() { + //TODO: See if there is a better way to save active state + for (WirelessChannel channel : WirelessRedstone.getStorageManager().getChannels()) { + setChannel(channel.getName(), channel); + } + + return true; + } + + @Override + protected Collection getAllChannels() { + Collection channels = new ArrayList<>(); + + for (File f : Objects.requireNonNull(channelFolder.listFiles(yamlFilter))) { + FileConfiguration channelConfig = new YamlConfiguration(); + try { + channelConfig.load(f); + } catch (InvalidConfigurationException | IOException ex) { + ex.printStackTrace(); + } + + String channelName; + try { + channelName = f.getName().split(".yml")[0]; + } catch (ArrayIndexOutOfBoundsException ex) { + continue; + } + + Object channel = channelConfig.get(channelName); + if (channel instanceof WirelessChannel) { + channels.add((WirelessChannel) channel); + WirelessRedstone.getWRLogger().debug("Found channel: " + ((WirelessChannel) channel).getName()); + } else if (channel == null) { + WirelessRedstone.getWRLogger().debug("File " + f.getName() + " does not contain a Wireless Channel. Removing it."); + f.delete(); + } else + WirelessRedstone.getWRLogger().warning("Channel " + channel + " is not of type WirelessChannel."); + } + + return channels; + } + + @Override + public boolean createChannel(WirelessChannel channel) { + if (!setChannel(channel.getName(), channel)) + return false; + + return super.createChannel(channel); + } + + @Override + public boolean createWirelessPoint(String channelName, WirelessPoint wirelessPoint) { + WirelessChannel channel = WirelessRedstone.getStorageManager().getChannel(channelName); + channel.addWirelessPoint(wirelessPoint); + + if (!setChannel(channelName, channel)) + return false; + + return super.createWirelessPoint(channelName, wirelessPoint); + } + + @Override + public boolean removeWirelessPoint(String channelName, WirelessPoint wirelessPoint) { + WirelessChannel channel = WirelessRedstone.getStorageManager().getChannel(channelName); + channel.removeWirelessPoint(wirelessPoint); + + if (!setChannel(channelName, channel)) + return false; + + return super.removeWirelessPoint(channelName, wirelessPoint); + } + + @Override + public boolean updateChannel(String channelName, WirelessChannel channel) { + if (!setChannel(channelName, channel)) + return false; + + return super.updateChannel(channelName, channel); + } + + @Override + public boolean removeChannel(String channelName, boolean removeSigns) { + File file = new File(channelFolder, channelName + ".yml"); + + if (file.exists()) + file.delete(); + + return super.removeChannel(channelName, removeSigns); + } + + @Override + public boolean wipeData() { + for (File f : Objects.requireNonNull(channelFolder.listFiles(yamlFilter))) { + f.delete(); + } + + return super.wipeData(); + } + + @Override + public void updateSwitchState(WirelessChannel channel) { + for (WirelessReceiver receiver : channel.getReceivers()) { + if (receiver instanceof WirelessReceiverSwitch) { + setChannel(channel.getName(), channel); + break; + } + } + } + + @Override + protected StorageType canConvertFromType() { + for (File file : Objects.requireNonNull(channelFolder.listFiles())) { + if (file.getName().contains(".db")) { + return StorageType.SQLITE; + } + } + + return null; + } + + private boolean setChannel(String channelName, WirelessChannel channel) { + FileConfiguration channelConfig = new YamlConfiguration(); + try { + File channelFile = new File(channelFolder, channelName + ".yml"); + + if (channel != null) + channelFile.createNewFile(); + + channelConfig.load(channelFile); + } catch (FileNotFoundException e) { + return false; + } catch (IOException | InvalidConfigurationException ex) { + ex.printStackTrace(); + } + + channelConfig.set(channelName, channel); + + try { + channelConfig.save(new File(channelFolder, channelName + ".yml")); + } catch (IOException ex) { + ex.printStackTrace(); + return false; + } + + return true; + } +} \ No newline at end of file