Implement sql properly

This commit is contained in:
Paul Reilly 2023-03-31 17:21:19 -05:00
parent 997210a16f
commit 0d0fbf2c04
6 changed files with 655 additions and 347 deletions

View File

@ -13,14 +13,15 @@ import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import java.sql.SQLException;
import java.util.*; import java.util.*;
import java.util.concurrent.CompletionException;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
public class AdminList extends FreedomService public class AdminList extends FreedomService
{ {
public static final List<UUID> vanished = new ArrayList<>(); public static final List<UUID> vanished = new ArrayList<>();
public static final String SQL_SAVE_FAILED = "Failed to save admin: ";
private final Set<Admin> allAdmins = Sets.newHashSet(); // Includes disabled admins private final Set<Admin> allAdmins = Sets.newHashSet(); // Includes disabled admins
// Only active admins below // Only active admins below
private final Set<Admin> activeAdmins = Sets.newHashSet(); private final Set<Admin> activeAdmins = Sets.newHashSet();
@ -44,11 +45,11 @@ public class AdminList extends FreedomService
public void load() public void load()
{ {
allAdmins.clear(); allAdmins.clear();
try plugin.sql.getAdminList().thenAcceptAsync(provider ->
{ {
ResultSetProvider adminList = plugin.sql.getAdminList();
AtomicInteger row = new AtomicInteger(1); AtomicInteger row = new AtomicInteger(1);
adminList.getAllRowsResultSet().forEach(adminSet -> { provider.getAllRowsResultSet().forEach(adminSet ->
{
try try
{ {
tryAddAdmin(ResultSetProvider.fromRow(adminSet)); tryAddAdmin(ResultSetProvider.fromRow(adminSet));
@ -56,22 +57,25 @@ public class AdminList extends FreedomService
} catch (Throwable e) } catch (Throwable e)
{ {
FLog.warning("An error occurred whilst reading the admin entry at row #" + row.get()); FLog.warning("An error occurred whilst reading the admin entry at row #" + row.get());
FLog.warning(e); throw new CompletionException(e); // handle downstream.
} }
}); });
} catch (SQLException e) }).whenComplete((result, throwable) ->
{ {
FLog.severe("Failed to load admin list: " + e.getMessage()); if (throwable != null)
} {
FLog.severe(throwable.getMessage());
updateTables(); return;
FLog.info("Loaded " + allAdmins.size() + " admins (" + uuidTable.size() + " active, " + ipTable.size() + " IPs)"); }
updateTables();
FLog.info("Loaded " + allAdmins.size() + " admins (" + uuidTable.size() + " active, " + ipTable.size() + " IPs)");
});
} }
private void tryAddAdmin(ResultSetProvider adminSet) private void tryAddAdmin(ResultSetProvider adminSet)
{ {
Admin admin = new Admin(adminSet); Admin admin = new Admin(adminSet);
allAdmins.add(admin); allAdmins.add(admin);
} }
public void messageAllAdmins(Component message) public void messageAllAdmins(Component message)
@ -200,7 +204,13 @@ public class AdminList extends FreedomService
updateTables(); updateTables();
// Save admin // Save admin
plugin.sql.addAdmin(admin); plugin.sql.addAdmin(admin).whenComplete((result, exception) ->
{
if (exception != null)
{
FLog.warning(SQL_SAVE_FAILED + admin.getName() + " to the database.\n" + exception.getMessage());
}
});
} }
public boolean removeAdmin(Admin admin) public boolean removeAdmin(Admin admin)
@ -218,7 +228,13 @@ public class AdminList extends FreedomService
updateTables(); updateTables();
// Unsave admin // Unsave admin
plugin.sql.removeAdmin(admin); plugin.sql.removeAdmin(admin).whenComplete((result, exception) ->
{
if (exception != null)
{
FLog.warning("Failed to remove admin: " + admin.getName() + " from the database.\n" + exception.getMessage());
}
});
return true; return true;
} }
@ -263,21 +279,30 @@ public class AdminList extends FreedomService
public void save(Admin admin) public void save(Admin admin)
{ {
try plugin.sql.getAdminByUuid(admin.getUuid()).thenApplyAsync(result ->
{ {
ResultSetProvider currentSave = plugin.sql.getAdminByUuid(admin.getUuid());
for (Map.Entry<String, Object> entry : admin.toSQLStorable().entrySet()) for (Map.Entry<String, Object> entry : admin.toSQLStorable().entrySet())
{ {
Object storedValue = plugin.sql.getValue(currentSave, entry.getKey(), entry.getValue()); Object storedValue = plugin.sql.getValue(result, entry.getKey(), entry.getValue());
if (storedValue != null && !storedValue.equals(entry.getValue()) || storedValue == null && entry.getValue() != null || entry.getValue() == null) if (storedValue != null && !storedValue.equals(entry.getValue()) || storedValue == null && entry.getValue() != null || entry.getValue() == null)
{ {
plugin.sql.setAdminValue(admin, entry.getKey(), entry.getValue()); plugin.sql.setAdminValue(admin, entry.getKey(), entry.getValue()).whenComplete((result2, exception) ->
{
if (exception != null)
{
throw new CompletionException(exception); // We want to handle downstream in #whenComplete()
}
});
} }
} }
} catch (SQLException e) return null;
}).whenComplete((result, exception) ->
{ {
FLog.severe("Failed to save admin: " + e.getMessage()); if (exception != null)
} {
FLog.severe(SQL_SAVE_FAILED + exception.getMessage());
}
});
} }
public void deactivateOldEntries(boolean verbose) public void deactivateOldEntries(boolean verbose)

View File

@ -12,7 +12,6 @@ import org.bukkit.event.EventPriority;
import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerLoginEvent; import org.bukkit.event.player.PlayerLoginEvent;
import java.sql.SQLException;
import java.util.*; import java.util.*;
public class BanManager extends FreedomService public class BanManager extends FreedomService
@ -28,9 +27,17 @@ public class BanManager extends FreedomService
public void onStart() public void onStart()
{ {
bans.clear(); bans.clear();
try
plugin.sql.getBanList().whenComplete((result, throwable) ->
{ {
plugin.sql.getBanList().getAllRowsResultSet().forEach(row -> { if (throwable != null)
{
FLog.severe("Failed to load ban list: " + throwable.getMessage());
return;
}
result.getAllRowsResultSet().forEach(row ->
{
ResultSetProvider banSet = ResultSetProvider.fromRow(row); ResultSetProvider banSet = ResultSetProvider.fromRow(row);
String name = banSet.getString("name"); String name = banSet.getString("name");
UUID uuid = null; UUID uuid = null;
@ -47,10 +54,7 @@ public class BanManager extends FreedomService
Ban ban = new Ban(name, uuid, ips, by, at, expires, reason); Ban ban = new Ban(name, uuid, ips, by, at, expires, reason);
bans.add(ban); bans.add(ban);
}); });
} catch (SQLException e) });
{
FLog.severe("Failed to load ban list: " + e.getMessage());
}
// Remove expired bans, repopulate ipBans and nameBans, // Remove expired bans, repopulate ipBans and nameBans,
updateViews(); updateViews();
@ -191,8 +195,16 @@ public class BanManager extends FreedomService
if (bans.add(ban)) if (bans.add(ban))
{ {
plugin.sql.addBan(ban); plugin.sql.addBan(ban).whenComplete((result, ex) ->
updateViews(); {
if (ex != null)
{
FLog.severe(ex.getMessage());
return;
}
updateViews();
});
} }
} }
@ -201,8 +213,16 @@ public class BanManager extends FreedomService
{ {
if (bans.remove(ban)) if (bans.remove(ban))
{ {
plugin.sql.removeBan(ban); plugin.sql.removeBan(ban).whenComplete((result, ex) ->
updateViews(); {
if (ex != null)
{
FLog.severe(ex.getMessage());
return;
}
updateViews();
});
} }
} }
@ -212,7 +232,13 @@ public class BanManager extends FreedomService
int size = bans.size(); int size = bans.size();
bans.clear(); bans.clear();
updateViews(); updateViews();
plugin.sql.truncate("bans"); plugin.sql.truncate("bans").whenComplete((result, ex) ->
{
if (ex != null)
{
FLog.severe(ex.getMessage());
}
});
return size; return size;
} }
@ -274,7 +300,13 @@ public class BanManager extends FreedomService
if (ban.isExpired()) if (ban.isExpired())
{ {
bans.remove(ban); bans.remove(ban);
plugin.sql.removeBan(ban); plugin.sql.removeBan(ban).whenComplete((result, ex) ->
{
if (ex != null)
{
FLog.severe(ex.getMessage());
}
});
} }
} }

View File

@ -1,6 +1,7 @@
package me.totalfreedom.totalfreedommod.command; package me.totalfreedom.totalfreedommod.command;
import me.totalfreedom.totalfreedommod.player.PlayerData; import me.totalfreedom.totalfreedommod.player.PlayerData;
import me.totalfreedom.totalfreedommod.util.FLog;
import me.totalfreedom.totalfreedommod.util.FUtil; import me.totalfreedom.totalfreedommod.util.FUtil;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.bukkit.ChatColor; import org.bukkit.ChatColor;
@ -26,13 +27,12 @@ public class Command_mbconfig extends FreedomCommand
switch (args[0]) switch (args[0])
{ {
case "list": case "list" ->
{ {
msg("Master Builders: " + StringUtils.join(plugin.pl.getMasterBuilderNames(), ", "), ChatColor.GOLD); msg("Master Builders: " + StringUtils.join(plugin.pl.getMasterBuilderNames(), ", "), ChatColor.GOLD);
return true; return true;
} }
case "clearips" ->
case "clearips":
{ {
if (args.length > 1) if (args.length > 1)
{ {
@ -55,13 +55,23 @@ public class Command_mbconfig extends FreedomCommand
int counter = data.getIps().size() - 1; int counter = data.getIps().size() - 1;
data.clearIps(); data.clearIps();
data.addIp(FUtil.getIp(playerSender)); data.addIp(FUtil.getIp(playerSender));
plugin.sql.addPlayer(data); plugin.sql.addPlayer(data).whenComplete((result, ex) ->
msg(counter + " IPs removed."); {
msg(data.getIps().get(0) + " is now your only IP address"); if (ex != null)
FUtil.adminAction(sender.getName(), "Clearing my IPs", true); {
FLog.severe(ex.getMessage());
msgNew("<red>Failed to clear IPs!");
return;
}
msg(counter + " IPs removed.");
msg(data.getIps().get(0) + " is now your only IP address");
FUtil.adminAction(sender.getName(), "Clearing my IPs", true);
});
return true; return true;
} }
case "clearip": case "clearip" ->
{ {
if (args.length < 2) if (args.length < 2)
{ {
@ -89,12 +99,21 @@ public class Command_mbconfig extends FreedomCommand
return true; return true;
} }
data.removeIp(args[1]); data.removeIp(args[1]);
plugin.sql.addPlayer(data); plugin.sql.addPlayer(data).whenComplete((result, ex) ->
msg("Removed IP " + args[1]); {
msg("Current IPs: " + StringUtils.join(data.getIps(), ", ")); if (ex != null)
{
FLog.severe(ex.getMessage());
msgNew("<red>Failed to remove IP!");
return;
}
msg("Removed IP " + args[1]);
msg("Current IPs: " + StringUtils.join(data.getIps(), ", "));
});
return true; return true;
} }
case "add": case "add" ->
{ {
if (args.length < 2) if (args.length < 2)
{ {
@ -131,7 +150,7 @@ public class Command_mbconfig extends FreedomCommand
} }
return true; return true;
} }
case "remove": case "remove" ->
{ {
if (args.length < 2) if (args.length < 2)
{ {
@ -161,7 +180,7 @@ public class Command_mbconfig extends FreedomCommand
} }
return true; return true;
} }
default: default ->
{ {
return false; return false;
} }

View File

@ -8,12 +8,14 @@ import me.totalfreedom.totalfreedommod.rank.GroupProvider;
import me.totalfreedom.totalfreedommod.sql.ResultSetProvider; import me.totalfreedom.totalfreedommod.sql.ResultSetProvider;
import me.totalfreedom.totalfreedommod.util.FLog; import me.totalfreedom.totalfreedommod.util.FLog;
import me.totalfreedom.totalfreedommod.util.FUtil; import me.totalfreedom.totalfreedommod.util.FUtil;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer; import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.*; import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
public class PlayerList extends FreedomService public class PlayerList extends FreedomService
{ {
@ -43,16 +45,17 @@ public class PlayerList extends FreedomService
public void loadMasterBuilders() public void loadMasterBuilders()
{ {
ResultSetProvider resultSet = plugin.sql.getMasterBuilders(); plugin.sql.getMasterBuilders().whenComplete((result, throwable) -> {
if (throwable != null) {
FLog.severe("Failed to load master builders: " + throwable.getMessage());
return;
}
if (resultSet == null) result.getAllRowsResultSet().forEach(row ->
{ {
return; PlayerData playerData = load(ResultSetProvider.fromRow(row));
} dataMap.put(playerData.getUuid(), playerData);
});
resultSet.getAllRowsResultSet().forEach(row -> {
PlayerData playerData = load(ResultSetProvider.fromRow(row));
dataMap.put(playerData.getUuid(), playerData);
}); });
} }
@ -85,10 +88,7 @@ public class PlayerList extends FreedomService
{ {
PlayerData data = getData(name); PlayerData data = getData(name);
return (!ConfigEntry.HOST_SENDER_NAMES.getStringList().contains(name.toLowerCase()) && data != null && !ConfigEntry.SERVER_OWNERS.getStringList().contains(data.getName())) return (!ConfigEntry.HOST_SENDER_NAMES.getStringList().contains(name.toLowerCase()) && data != null && !ConfigEntry.SERVER_OWNERS.getStringList().contains(data.getName())) && !ConfigEntry.SERVER_EXECUTIVES.getStringList().contains(data.getName()) && !isTelnetMasterBuilder(data) && !ConfigEntry.HOST_SENDER_NAMES.getStringList().contains(name.toLowerCase());
&& !ConfigEntry.SERVER_EXECUTIVES.getStringList().contains(data.getName())
&& !isTelnetMasterBuilder(data)
&& !ConfigEntry.HOST_SENDER_NAMES.getStringList().contains(name.toLowerCase());
} }
public boolean isTelnetMasterBuilder(PlayerData playerData) public boolean isTelnetMasterBuilder(PlayerData playerData)
@ -114,12 +114,52 @@ public class PlayerList extends FreedomService
public PlayerData loadByUuid(UUID uuid) public PlayerData loadByUuid(UUID uuid)
{ {
return load(plugin.sql.getPlayerByUuid(uuid)); AtomicReference<PlayerData> futureData = new AtomicReference<>(null);
AtomicBoolean loaded = new AtomicBoolean(false);
plugin.sql.getPlayerByUuid(uuid).thenApplyAsync(result -> {
futureData.set(load(result));
return futureData;
}).whenComplete((result, throwable) -> {
if (throwable != null) {
FLog.severe("Failed to load player by UUID: " + throwable.getMessage());
return;
}
loaded.set(true);
});
while (!loaded.get()) // Wait for the future to complete.
{
if (futureData.get() != null) { // This will break the loop if the result is available before the boolean is updated.
break;
}
}
return futureData.get();
} }
public PlayerData loadByIp(String ip) public PlayerData loadByIp(String ip)
{ {
return load(plugin.sql.getPlayerByIp(ip)); AtomicReference<PlayerData> futureData = new AtomicReference<>(null);
AtomicBoolean loaded = new AtomicBoolean(false);
plugin.sql.getPlayerByIp(ip).thenApplyAsync(result -> {
futureData.set(load(result));
return futureData;
}).whenComplete((result, throwable) -> {
if (throwable != null) {
FLog.severe("Failed to load player by IP: " + throwable.getMessage());
return;
}
loaded.set(true);
});
while (!loaded.get())
{
if (futureData.get() != null) {
break;
}
} // Wait for the future to complete.
return futureData.get();
} }
public PlayerData load(ResultSetProvider resultSet) public PlayerData load(ResultSetProvider resultSet)
@ -142,21 +182,27 @@ public class PlayerList extends FreedomService
public void save(PlayerData player) public void save(PlayerData player)
{ {
try plugin.sql.getPlayerByUuid(player.getUuid()).whenComplete((result, throwable) -> {
{ if (throwable != null) {
ResultSetProvider currentSave = plugin.sql.getPlayerByUuid(player.getUuid()); FLog.severe("Failed to save player: " + throwable.getMessage());
return;
}
for (Map.Entry<String, Object> entry : player.toSQLStorable().entrySet()) for (Map.Entry<String, Object> entry : player.toSQLStorable().entrySet())
{ {
Object storedValue = plugin.sql.getValue(currentSave, entry.getKey(), entry.getValue()); Object storedValue = plugin.sql.getValue(result, entry.getKey(), entry.getValue());
if (storedValue != null && !storedValue.equals(entry.getValue()) || storedValue == null && entry.getValue() != null || entry.getValue() == null) if (storedValue != null && !storedValue.equals(entry.getValue()) || storedValue == null && entry.getValue() != null || entry.getValue() == null)
{ {
plugin.sql.setPlayerValue(player, entry.getKey(), entry.getValue()); plugin.sql.setPlayerValue(player, entry.getKey(), entry.getValue()).whenComplete((result1, ex) ->
{
if (ex != null)
{
FLog.severe("Failed to save player: " + ex.getMessage());
}
});
} }
} }
} catch (SQLException e) });
{
FLog.severe("Failed to save player: " + e.getMessage());
}
} }
public PlayerData getData(Player player) public PlayerData getData(Player player)
@ -184,7 +230,11 @@ public class PlayerList extends FreedomService
dataMap.put(player.getUniqueId(), playerData); dataMap.put(player.getUniqueId(), playerData);
// Send it to the SQL database. // Send it to the SQL database.
plugin.sql.addPlayer(playerData); plugin.sql.addPlayer(playerData).whenComplete((result, throwable) -> {
if (throwable != null) {
FLog.severe(throwable.getMessage());
}
});
// Returns it // Returns it
return playerData; return playerData;

View File

@ -2,15 +2,10 @@ package me.totalfreedom.totalfreedommod.sql;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Wrapper;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
public class ResultSetProvider public class ResultSetProvider
{ {
@ -40,34 +35,18 @@ public class ResultSetProvider
} }
} }
public static CompletableFuture<ResultSetProvider> provideAllRows(ResultSet resultSet) public static ResultSetProvider provideAllRows(ResultSet resultSet) throws SQLException
{ {
return CompletableFuture.supplyAsync(() -> return new ResultSetProvider(resultSet, true);
{
try
{
return new ResultSetProvider(resultSet, true);
} catch (SQLException e)
{
return null;
}
});
} }
public static CompletableFuture<ResultSetProvider> provideSpecificRow(ResultSet resultSet) throws SQLException public static ResultSetProvider provideSpecificRow(ResultSet resultSet) throws SQLException
{ {
return CompletableFuture.supplyAsync(() -> { return new ResultSetProvider(resultSet, false);
try
{
return new ResultSetProvider(resultSet, false);
} catch (SQLException e)
{
throw new RuntimeException(e); // We don't want to handle this exception, we want the delegate to handle it instead.
}
});
} }
public static ResultSetProvider fromRow(Map<String, Object> row) { public static ResultSetProvider fromRow(Map<String, Object> row)
{
return new ResultSetProvider(row); return new ResultSetProvider(row);
} }

View File

@ -10,9 +10,12 @@ import me.totalfreedom.totalfreedommod.util.FUtil;
import java.sql.*; import java.sql.*;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
public class SQLite extends FreedomService public class SQLite extends FreedomService
{ {
public static final String TABLE_CHECK_FAILURE = "Failed to check tables on database: ";
private final String FILE_NAME = "database.db"; private final String FILE_NAME = "database.db";
private Connection connection; private Connection connection;
@ -21,7 +24,13 @@ public class SQLite extends FreedomService
public void onStart() public void onStart()
{ {
connect(); connect();
checkTables(); checkTables().whenComplete((result, throwable) ->
{
if (throwable != null)
{
FLog.severe(TABLE_CHECK_FAILURE + throwable.getMessage());
}
});
} }
@Override @Override
@ -56,148 +65,293 @@ public class SQLite extends FreedomService
} }
} }
public void checkTables() public CompletableFuture<Void> checkTables()
{ {
try return CompletableFuture.supplyAsync(() ->
{ {
DatabaseMetaData meta = connection.getMetaData(); try
if (tableNotExists(meta, "bans"))
{ {
createBanTable(); return connection.getMetaData();
} catch (SQLException e)
{
throw new CompletionException(TABLE_CHECK_FAILURE, e);
}
}).thenAcceptAsync(meta ->
{
futureBansTable(meta);
futureAdminsTable(meta);
futurePlayersTable(meta);
});
}
private void futurePlayersTable(DatabaseMetaData meta)
{
tableNotExists(meta, "players").whenComplete((result, throwable) ->
{
if (throwable != null)
{
FLog.severe(throwable.getMessage());
return;
} }
if (tableNotExists(meta, "admins")) if (result)
{ {
createAdminsTable(); createPlayersTable().whenComplete((result2, throwable2) ->
{
if (throwable2 != null)
{
FLog.severe(throwable2.getMessage());
}
});
} }
if (tableNotExists(meta, "players")) });
}
private void futureAdminsTable(DatabaseMetaData meta)
{
tableNotExists(meta, "admins").whenComplete((result, throwable) ->
{
if (throwable != null)
{ {
createPlayersTable(); FLog.severe(throwable.getMessage());
return;
} }
} catch (SQLException e)
{ if (result)
FLog.severe("Failed to check tables on database: " + e.getMessage()); {
} createAdminsTable().whenComplete((result2, throwable2) ->
{
if (throwable2 != null)
{
FLog.severe(throwable2.getMessage());
}
});
}
});
} }
private void createPlayersTable() private void futureBansTable(DatabaseMetaData meta)
{ {
try (PreparedStatement statement = connection.prepareStatement("CREATE TABLE `players` (`uuid` VARCHAR NOT NULL, `ips` VARCHAR NOT NULL, `notes` VARCHAR, `tag` VARCHAR, `discord_id` VARCHAR, `master_builder` BOOLEAN NOT NULL, `ride_mode` VARCHAR NOT NULL, `coins` INT, `items` VARCHAR, `total_votes` INT NOT NULL, `display_discord` BOOLEAN NOT NULL, `login_message` VARCHAR, `inspect` BOOLEAN NOT NULL);")) tableNotExists(meta, "bans").whenComplete((result, throwable) ->
{ {
statement.executeUpdate(); if (throwable != null)
} catch (SQLException e) {
{ FLog.severe(throwable.getMessage());
FLog.severe("Failed to create the players table: " + e.getMessage()); return;
} }
if (Boolean.TRUE.equals(result)) // using primitive for some reason... ask SonarLint.
{
createBanTable().whenComplete((result2, throwable2) ->
{
if (throwable2 != null)
{
FLog.severe(throwable2.getMessage());
}
});
}
});
} }
private void createAdminsTable() private CompletableFuture<Void> createPlayersTable()
{ {
try (PreparedStatement statement = connection.prepareStatement("CREATE TABLE `admins` (`uuid` VARCHAR NOT NULL, `ips` VARCHAR NOT NULL, `rank` VARCHAR NOT NULL, `active` BOOLEAN NOT NULL, `last_login` LONG NOT NULL, `command_spy` BOOLEAN NOT NULL, `potion_spy` BOOLEAN NOT NULL, `ac_format` VARCHAR);")) return CompletableFuture.supplyAsync(() ->
{ {
statement.executeUpdate(); try (PreparedStatement statement = connection.prepareStatement("CREATE TABLE `players` (`uuid` VARCHAR NOT NULL, `ips` VARCHAR NOT NULL, `notes` VARCHAR, `tag` VARCHAR, `discord_id` VARCHAR, `master_builder` BOOLEAN NOT NULL, `ride_mode` VARCHAR NOT NULL, `coins` INT, `items` VARCHAR, `total_votes` INT NOT NULL, `display_discord` BOOLEAN NOT NULL, `login_message` VARCHAR, `inspect` BOOLEAN NOT NULL);"))
} catch (SQLException e) {
{ statement.executeUpdate();
FLog.severe("Failed to create the admins table: " + e.getMessage()); } catch (SQLException e)
} {
throw new CompletionException("Failed to create the players table.", e);
}
return null;
});
} }
private void createBanTable() private CompletableFuture<Void> createAdminsTable()
{ {
try (PreparedStatement statement = connection.prepareStatement("CREATE TABLE `bans` (`name` VARCHAR, `uuid` VARCHAR, `ips` VARCHAR, `by` VARCHAR NOT NULL, `at` LONG NOT NULL, `expires` LONG, `reason` VARCHAR);")) return CompletableFuture.supplyAsync(() ->
{ {
statement.executeUpdate(); try (PreparedStatement statement = connection.prepareStatement("CREATE TABLE `admins` (`uuid` VARCHAR NOT NULL, `ips` VARCHAR NOT NULL, `rank` VARCHAR NOT NULL, `active` BOOLEAN NOT NULL, `last_login` LONG NOT NULL, `command_spy` BOOLEAN NOT NULL, `potion_spy` BOOLEAN NOT NULL, `ac_format` VARCHAR);"))
} catch (SQLException e) {
{ statement.executeUpdate();
FLog.severe("Failed to create the bans table: " + e.getMessage()); } catch (SQLException e)
} {
throw new CompletionException("Failed to create the admins table.", e);
}
return null;
});
} }
public void truncate(String table) private CompletableFuture<Void> createBanTable()
{ {
try (PreparedStatement statement = connection.prepareStatement("DELETE FROM ?")) return CompletableFuture.supplyAsync(() ->
{ {
statement.setString(1, table); try (PreparedStatement statement = connection.prepareStatement("CREATE TABLE `bans` (`name` VARCHAR, `uuid` VARCHAR, `ips` VARCHAR, `by` VARCHAR NOT NULL, `at` LONG NOT NULL, `expires` LONG, `reason` VARCHAR);"))
statement.executeUpdate(); {
} catch (SQLException e) statement.executeUpdate();
{ } catch (SQLException e)
FLog.severe("Failed to truncate " + table + ": " + e.getMessage()); {
} throw new CompletionException("Failed to create the bans table.", e);
}
return null;
});
} }
public ResultSetProvider getBanList() throws SQLException public CompletableFuture<Void> truncate(String table)
{ {
try (PreparedStatement statement = connection.prepareStatement("SELECT * FROM bans")) return CompletableFuture.supplyAsync(() ->
{ {
return ResultSetProvider.provideAllRows(statement.executeQuery()).join(); try (PreparedStatement statement = connection.prepareStatement("DELETE FROM ?"))
} {
statement.setString(1, table);
statement.executeUpdate();
} catch (SQLException e)
{
FLog.severe("Failed to truncate " + table + ": " + e.getMessage());
}
return null;
});
} }
public ResultSetProvider getAdminList() throws SQLException public CompletableFuture<ResultSetProvider> getBanList()
{ {
try (PreparedStatement statement = connection.prepareStatement("SELECT * FROM bans")) return CompletableFuture.supplyAsync(() ->
{ {
return ResultSetProvider.provideAllRows(statement.executeQuery()).join(); ResultSetProvider provider;
} try (PreparedStatement statement = connection.prepareStatement("SELECT * FROM bans"))
{
try (ResultSet result = statement.executeQuery())
{
provider = ResultSetProvider.provideAllRows(result);
}
} catch (SQLException e)
{
throw new CompletionException("Failed to get ban list.", e);
}
return provider;
});
} }
public void setAdminValue(Admin admin, String key, Object value) public CompletableFuture<ResultSetProvider> getAdminList()
{ {
try return CompletableFuture.supplyAsync(() ->
{ {
Object[] data = {key, admin.getUuid()}; ResultSetProvider provider;
PreparedStatement statement = connection.prepareStatement(MessageFormat.format("UPDATE admins SET {0}=? WHERE uuid=''{1}''", data)); try (PreparedStatement statement = connection.prepareStatement("SELECT * FROM bans"))
statement = setUnknownType(statement, 1, value); {
statement.executeUpdate(); try (ResultSet result = statement.executeQuery())
{
provider = ResultSetProvider.provideAllRows(result);
}
} catch (SQLException ex)
{
throw new CompletionException("Failed to get admin list.", ex);
}
return provider;
});
} catch (SQLException e)
{
FLog.severe("Failed to update admin value:");
FLog.severe(e);
}
} }
public void setPlayerValue(PlayerData player, String key, Object value) public CompletableFuture<Void> setAdminValue(Admin admin, String key, Object value)
{ {
try return CompletableFuture.supplyAsync(() ->
{ {
Object[] data = {key, player.getUuid()}; try
PreparedStatement statement = connection.prepareStatement(MessageFormat.format("UPDATE players SET {0}=? WHERE uuid=''{1}''", data)); {
statement = setUnknownType(statement, 1, value); Object[] data = {key, admin.getUuid()};
statement.executeUpdate(); PreparedStatement statement = connection.prepareStatement(MessageFormat.format("UPDATE admins SET {0}=? WHERE uuid=''{1}''", data));
setUnknownType(statement, 1, value).whenComplete((result, throwable) ->
{
if (throwable != null)
{
FLog.severe(throwable.getMessage());
}
} catch (SQLException e) try
{ {
FLog.severe("Failed to update player value: " + e.getMessage()); result.executeUpdate();
} } catch (SQLException ex)
{
throw new CompletionException("Failed to update admin value.", ex);
}
});
} catch (SQLException e)
{
throw new CompletionException("Failed to update admin value.", e);
}
return null;
});
} }
public PreparedStatement setUnknownType(PreparedStatement statement, int index, Object value) throws SQLException public CompletableFuture<Void> setPlayerValue(PlayerData player, String key, Object value)
{ {
if (value == null) return CompletableFuture.supplyAsync(() ->
{ {
statement.setString(index, null); try
} else if (value.getClass().equals(String.class)) {
{ Object[] data = {key, player.getUuid()};
String v = (String) value; PreparedStatement statement = connection.prepareStatement(MessageFormat.format("UPDATE players SET {0}=? WHERE uuid=''{1}''", data));
statement.setString(index, v); setUnknownType(statement, 1, value).whenComplete((result, throwable) ->
} else if (value.getClass().equals(Integer.class)) {
{ if (throwable != null)
int v = (int) value; {
statement.setInt(index, v); FLog.severe(throwable.getMessage());
} else if (value.getClass().equals(Boolean.class)) }
{
boolean v = (boolean) value; try
statement.setBoolean(index, v); {
} else if (value.getClass().equals(Long.class)) result.executeUpdate();
{ } catch (SQLException ex)
long v = (long) value; {
statement.setLong(index, v); throw new CompletionException("Failed to update player value.", ex);
} }
return statement; });
} catch (SQLException e)
{
throw new CompletionException("Failed to update player value.", e);
}
return null;
});
} }
public Object getValue(ResultSetProvider provider, String key, Object value) throws SQLException public CompletableFuture<PreparedStatement> setUnknownType(PreparedStatement statement, int index, Object value)
{
return CompletableFuture.supplyAsync(() ->
{
try
{
if (value == null)
{
statement.setString(index, null);
} else if (value.getClass().equals(String.class))
{
String v = (String) value;
statement.setString(index, v);
} else if (value.getClass().equals(Integer.class))
{
int v = (int) value;
statement.setInt(index, v);
} else if (value.getClass().equals(Boolean.class))
{
boolean v = (boolean) value;
statement.setBoolean(index, v);
} else if (value.getClass().equals(Long.class))
{
long v = (long) value;
statement.setLong(index, v);
}
return statement;
} catch (SQLException ex)
{
throw new CompletionException("Failed to set unknown type.", ex);
}
});
}
public Object getValue(ResultSetProvider provider, String key, Object value)
{ {
Object result = null; Object result = null;
if (value instanceof String) if (value instanceof String)
@ -216,174 +370,223 @@ public class SQLite extends FreedomService
return result; return result;
} }
public void addAdmin(Admin admin) public CompletableFuture<Void> addAdmin(Admin admin)
{ {
return CompletableFuture.supplyAsync(() ->
try (PreparedStatement statement = connection.prepareStatement("INSERT INTO admins VALUES (?, ?, ?, ?, ?, ?, ?, ?)"))
{ {
statement.setString(1, admin.getUuid().toString()); try (PreparedStatement statement = connection.prepareStatement("INSERT INTO admins VALUES (?, ?, ?, ?, ?, ?, ?, ?)"))
statement.setString(2, FUtil.listToString(admin.getIps()));
statement.setString(3, admin.getRank().toString());
statement.setBoolean(4, admin.isActive());
statement.setLong(5, admin.getLastLogin().getTime());
statement.setBoolean(6, admin.getCommandSpy());
statement.setBoolean(7, admin.getPotionSpy());
statement.setString(8, admin.getAcFormat());
statement.executeUpdate();
} catch (SQLException e)
{
FLog.severe("Failed to add admin:");
FLog.severe(e);
}
}
public void addPlayer(PlayerData player)
{
try (PreparedStatement statement = connection.prepareStatement("INSERT INTO players VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"))
{
statement.setString(1, player.getUuid().toString());
statement.setString(2, FUtil.listToString(player.getIps()));
statement.setString(3, FUtil.listToString(player.getNotes()));
statement.setString(4, FUtil.miniMessage(player.getTag()));
statement.setString(5, player.getDiscordID());
statement.setBoolean(6, player.isMasterBuilder());
statement.setString(7, player.getRideMode().name());
statement.setInt(8, player.getCoins());
statement.setString(9, FUtil.listToString(player.getItems()));
statement.setInt(10, player.getTotalVotes());
statement.setBoolean(11, player.doesDisplayDiscord());
statement.setString(12, player.getLoginMessage());
statement.setBoolean(13, player.hasInspection());
statement.executeUpdate();
} catch (SQLException e)
{
FLog.severe("Failed to add player:");
FLog.severe(e);
}
}
public ResultSetProvider getAdminByUuid(UUID uuid)
{
try (PreparedStatement statement = connection.prepareStatement("SELECT * FROM admins WHERE uuid=?"))
{
statement.setString(1, uuid.toString());
return ResultSetProvider.provideSpecificRow(statement.executeQuery()).join();
} catch (SQLException e)
{
FLog.severe("Failed to get admin by name:");
FLog.severe(e);
}
return null;
}
public ResultSetProvider getPlayerByUuid(UUID uuid)
{
try (PreparedStatement statement = connection.prepareStatement("SELECT * FROM players WHERE uuid=?"))
{
statement.setString(1, uuid.toString());
return ResultSetProvider.provideSpecificRow(statement.executeQuery()).join();
} catch (SQLException e)
{
FLog.severe("Failed to get player by UUID:");
FLog.severe(e);
}
return null;
}
public ResultSetProvider getMasterBuilders()
{
try (PreparedStatement statement = connection.prepareStatement("SELECT * FROM players WHERE master_builder=true"))
{
return ResultSetProvider.provideAllRows(statement.executeQuery()).join();
} catch (SQLException e)
{
FLog.severe("Failed to get Master Builders:");
FLog.severe(e);
}
return null;
}
public ResultSetProvider getPlayerByIp(String ip)
{
try (PreparedStatement statement = connection.prepareStatement("SELECT * FROM players WHERE ips LIKE %?%"))
{
statement.setString(1, ip);
return ResultSetProvider.provideSpecificRow(statement.executeQuery()).join();
} catch (SQLException e)
{
FLog.severe("Failed to get player by ip:");
FLog.severe(e);
}
return null;
}
public void removeAdmin(Admin admin)
{
try (PreparedStatement statement = connection.prepareStatement("DELETE FROM admins where name=?"))
{
statement.setString(1, admin.getName());
statement.executeUpdate();
} catch (SQLException e)
{
FLog.severe("Failed to remove admin:");
FLog.severe(e);
}
}
public void addBan(Ban ban)
{
try (PreparedStatement statement = connection.prepareStatement("INSERT INTO bans VALUES (?, ?, ?, ?, ?, ?, ?)"))
{
statement.setString(1, ban.getUsername());
String uuid = null;
if (ban.hasUUID())
{ {
uuid = ban.getUuid().toString(); statement.setString(1, admin.getUuid().toString());
statement.setString(2, FUtil.listToString(admin.getIps()));
statement.setString(3, admin.getRank().toString());
statement.setBoolean(4, admin.isActive());
statement.setLong(5, admin.getLastLogin().getTime());
statement.setBoolean(6, admin.getCommandSpy());
statement.setBoolean(7, admin.getPotionSpy());
statement.setString(8, admin.getAcFormat());
statement.executeUpdate();
} catch (SQLException e)
{
throw new CompletionException("Failed to add admin.", e);
} }
statement.setString(2, uuid); return null;
statement.setString(3, FUtil.listToString(ban.getIps())); });
statement.setString(4, ban.getBy());
statement.setLong(5, ban.getAt().getTime());
statement.setLong(6, ban.getExpiryUnix());
statement.setString(7, ban.getReason());
statement.executeUpdate();
} catch (SQLException e)
{
FLog.severe("Failed to add ban: " + e.getMessage());
}
} }
public void removeBan(Ban ban) public CompletableFuture<Void> addPlayer(PlayerData player)
{ {
try return CompletableFuture.supplyAsync(() ->
{ {
try (PreparedStatement statement = connection.prepareStatement("DELETE FROM bans WHERE name=?")) try (PreparedStatement statement = connection.prepareStatement("INSERT INTO players VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"))
{
statement.setString(1, player.getUuid().toString());
statement.setString(2, FUtil.listToString(player.getIps()));
statement.setString(3, FUtil.listToString(player.getNotes()));
statement.setString(4, FUtil.miniMessage(player.getTag()));
statement.setString(5, player.getDiscordID());
statement.setBoolean(6, player.isMasterBuilder());
statement.setString(7, player.getRideMode().name());
statement.setInt(8, player.getCoins());
statement.setString(9, FUtil.listToString(player.getItems()));
statement.setInt(10, player.getTotalVotes());
statement.setBoolean(11, player.doesDisplayDiscord());
statement.setString(12, player.getLoginMessage());
statement.setBoolean(13, player.hasInspection());
statement.executeUpdate();
} catch (SQLException e)
{
throw new CompletionException("Failed to add player.", e);
}
return null;
});
}
public CompletableFuture<ResultSetProvider> getAdminByUuid(UUID uuid)
{
return CompletableFuture.supplyAsync(() ->
{
ResultSetProvider provider;
try (PreparedStatement statement = connection.prepareStatement("SELECT * FROM admins WHERE uuid=?"))
{
statement.setString(1, uuid.toString());
try (ResultSet set = statement.executeQuery())
{
provider = ResultSetProvider.provideSpecificRow(set);
}
} catch (SQLException ex)
{
throw new CompletionException(ex);
}
return provider;
});
}
public CompletableFuture<ResultSetProvider> getPlayerByUuid(UUID uuid)
{
return CompletableFuture.supplyAsync(() ->
{
ResultSetProvider provider;
try (PreparedStatement statement = connection.prepareStatement("SELECT * FROM players WHERE uuid=?"))
{
statement.setString(1, uuid.toString());
try (ResultSet set = statement.executeQuery())
{
provider = ResultSetProvider.provideSpecificRow(set);
}
} catch (SQLException ex)
{
throw new CompletionException(ex);
}
return provider;
});
}
public CompletableFuture<ResultSetProvider> getMasterBuilders()
{
return CompletableFuture.supplyAsync(() ->
{
ResultSetProvider provider;
try (PreparedStatement statement = connection.prepareStatement("SELECT * FROM players WHERE master_builder=true"))
{
try (ResultSet result = statement.executeQuery())
{
provider = ResultSetProvider.provideAllRows(result);
}
} catch (SQLException ex)
{
throw new CompletionException(ex);
}
return provider;
});
}
public CompletableFuture<ResultSetProvider> getPlayerByIp(String ip)
{
return CompletableFuture.supplyAsync(() ->
{
ResultSetProvider provider;
try (PreparedStatement statement = connection.prepareStatement("SELECT * FROM players WHERE ips LIKE %?%"))
{
statement.setString(1, ip);
try (ResultSet set = statement.executeQuery())
{
provider = ResultSetProvider.provideSpecificRow(set);
}
} catch (SQLException e)
{
throw new CompletionException(e);
}
return provider;
});
}
public CompletableFuture<Void> removeAdmin(Admin admin)
{
return CompletableFuture.supplyAsync(() ->
{
try (PreparedStatement statement = connection.prepareStatement("DELETE FROM admins where name=?"))
{
statement.setString(1, admin.getName());
statement.executeUpdate();
} catch (SQLException e)
{
throw new CompletionException("Failed to remove admin.", e);
}
return null;
});
}
public CompletableFuture<Void> addBan(Ban ban)
{
return CompletableFuture.supplyAsync(() ->
{
try (PreparedStatement statement = connection.prepareStatement("INSERT INTO bans VALUES (?, ?, ?, ?, ?, ?, ?)"))
{ {
statement.setString(1, ban.getUsername()); statement.setString(1, ban.getUsername());
statement.executeUpdate(); String uuid = null;
} if (ban.hasUUID())
for (String ip : ban.getIps())
{
try (PreparedStatement statement = connection.prepareStatement("DELETE FROM bans WHERE ips LIKE %?%"))
{ {
statement.setString(1, ip); uuid = ban.getUuid().toString();
}
statement.setString(2, uuid);
statement.setString(3, FUtil.listToString(ban.getIps()));
statement.setString(4, ban.getBy());
statement.setLong(5, ban.getAt().getTime());
statement.setLong(6, ban.getExpiryUnix());
statement.setString(7, ban.getReason());
statement.executeUpdate();
} catch (SQLException e)
{
throw new CompletionException("Failed to add ban.", e);
}
return null;
});
}
public CompletableFuture<Void> removeBan(Ban ban)
{
return CompletableFuture.supplyAsync(() ->
{
try
{
try (PreparedStatement statement = connection.prepareStatement("DELETE FROM bans WHERE name=?"))
{
statement.setString(1, ban.getUsername());
statement.executeUpdate(); statement.executeUpdate();
} }
for (String ip : ban.getIps())
{
try (PreparedStatement statement = connection.prepareStatement("DELETE FROM bans WHERE ips LIKE %?%"))
{
statement.setString(1, ip);
statement.executeUpdate();
}
}
} catch (SQLException e)
{
throw new CompletionException("Failed to remove ban.", e);
} }
} catch (SQLException e) return null;
{ });
FLog.severe("Failed to remove ban: " + e.getMessage());
}
} }
// We've changed this to read tableNotExists because it's more accurate in context. // We've changed this to read tableNotExists because it's more accurate in context.
public boolean tableNotExists(DatabaseMetaData meta, String name) throws SQLException public CompletableFuture<Boolean> tableNotExists(DatabaseMetaData meta, String name)
{ {
return !meta.getTables(null, null, name, null).next(); return CompletableFuture.supplyAsync(() ->
{
try
{
return !meta.getTables(null, null, name, null).next();
} catch (SQLException ex)
{
throw new CompletionException(TABLE_CHECK_FAILURE, ex);
}
});
} }
} }