From 82b70fb0f24e82ac03affbc52f777bcae6d08429 Mon Sep 17 00:00:00 2001 From: Paldiu Date: Sat, 28 Jan 2023 01:20:00 -0600 Subject: [PATCH] Bear Necessities --- build.gradle | 1 + .../mc/unraveled/reforged/command/BanCMD.java | 12 ++- .../unraveled/reforged/command/MuteCMD.java | 44 +++++++++ .../mc/unraveled/reforged/config/Yaml.java | 75 ++++++++++++++ .../reforged/config/YamlManager.java | 96 ++++++++++++++++++ .../unraveled/reforged/data/DataManager.java | 1 + .../reforged/data/InfractionData.java | 71 ++++++++++++++ .../unraveled/reforged/data/PlayerData.java | 1 + .../reforged/data/PlayerDataListener.java | 2 +- .../reforged/storage/DBInfraction.java | 97 +++++++++++++++++++ .../mc/unraveled/reforged/storage/DBUser.java | 7 +- .../unraveled/reforged/storage/SQLConst.java | 21 ++++ 12 files changed, 423 insertions(+), 5 deletions(-) create mode 100644 src/main/java/mc/unraveled/reforged/command/MuteCMD.java create mode 100644 src/main/java/mc/unraveled/reforged/config/Yaml.java create mode 100644 src/main/java/mc/unraveled/reforged/config/YamlManager.java create mode 100644 src/main/java/mc/unraveled/reforged/data/InfractionData.java create mode 100644 src/main/java/mc/unraveled/reforged/storage/DBInfraction.java create mode 100644 src/main/java/mc/unraveled/reforged/storage/SQLConst.java diff --git a/build.gradle b/build.gradle index 768521e..3a6e295 100644 --- a/build.gradle +++ b/build.gradle @@ -21,6 +21,7 @@ repositories { dependencies { implementation 'org.projectlombok:lombok:1.18.20' implementation 'org.postgresql:postgresql:42.2.20' + implementation 'org.apache.commons:commons-lang3:3.12.0' shadow 'io.projectreactor:reactor-core:3.4.10' compileOnly 'io.papermc.paper:paper-api:1.19.3-R0.1-SNAPSHOT' } diff --git a/src/main/java/mc/unraveled/reforged/command/BanCMD.java b/src/main/java/mc/unraveled/reforged/command/BanCMD.java index 251fdfc..995905f 100644 --- a/src/main/java/mc/unraveled/reforged/command/BanCMD.java +++ b/src/main/java/mc/unraveled/reforged/command/BanCMD.java @@ -8,6 +8,8 @@ import mc.unraveled.reforged.plugin.Traverse; import mc.unraveled.reforged.util.Utilities; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.StringUtils; import org.bukkit.OfflinePlayer; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @@ -26,13 +28,13 @@ public class BanCMD extends AbstractCommandBase { @Override public Component cmd(CommandSender sender, String[] args) { if (args.length < 2) { - return Component.text("Usage: /ban [duration]"); + return Component.text("Usage: /ban "); } BanManager manager = getPlugin().getBanManager(); OfflinePlayer target = (getPlugin().getServer().getPlayer(args[0]) != null) ? getPlugin().getServer().getPlayer(args[0]) : getPlugin().getServer().getOfflinePlayer(args[0]); - String reason = args[1]; - String duration = args.length > 2 ? args[2] : String.valueOf(60 * 24L); + String duration = args[1]; + String reason = StringUtils.join(ArrayUtils.subarray(args, 2, args.length - 1), " "); Date expiry = Utilities.parseDate(duration); String expiryString = Utilities.parseDateToString(expiry); @@ -41,6 +43,10 @@ public class BanCMD extends AbstractCommandBase { return MessageDefaults.MSG_NOT_FOUND; } + if (reason.isEmpty() || duration.isEmpty()) { + return Component.text("Usage: /ban "); + } + manager.unbake(); SimpleBan ban = new SimpleBan(target, sender, reason, Utilities.parseDate(duration), true); diff --git a/src/main/java/mc/unraveled/reforged/command/MuteCMD.java b/src/main/java/mc/unraveled/reforged/command/MuteCMD.java new file mode 100644 index 0000000..1635f12 --- /dev/null +++ b/src/main/java/mc/unraveled/reforged/command/MuteCMD.java @@ -0,0 +1,44 @@ +package mc.unraveled.reforged.command; + +import mc.unraveled.reforged.api.annotations.CommandInfo; +import mc.unraveled.reforged.command.base.AbstractCommandBase; +import mc.unraveled.reforged.data.InfractionData; +import mc.unraveled.reforged.data.PlayerData; +import mc.unraveled.reforged.plugin.Traverse; +import net.kyori.adventure.text.Component; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +@CommandInfo(name = "mute", description = "Mute a player.", usage = "/mute ") +public class MuteCMD extends AbstractCommandBase { + public MuteCMD(@NotNull Traverse plugin) { + super(plugin, "mute"); + } + + @Override + public Component cmd(CommandSender sender, String[] args) { + if (args.length != 2) { + return Component.text("Usage: /mute "); + } + + Player target = Bukkit.getPlayer(args[0]); + if (target == null) { + return MessageDefaults.MSG_NOT_FOUND; + } + + PlayerData pData = getPlugin().getDataManager().getPlayerData(target.getUniqueId()); + if (pData == null) throw new IllegalStateException("PlayerData is null!"); + + InfractionData infData = pData.getInfractionData(); + if (infData == null) throw new IllegalStateException("InfractionData is null!"); + + if (!infData.isMuted()) { + infData.setMuted(true); + return Component.text("You have muted " + target.getName() + " for " + args[1] + " seconds."); + } else { + return Component.text("Target is already muted."); + } + } +} diff --git a/src/main/java/mc/unraveled/reforged/config/Yaml.java b/src/main/java/mc/unraveled/reforged/config/Yaml.java new file mode 100644 index 0000000..2166289 --- /dev/null +++ b/src/main/java/mc/unraveled/reforged/config/Yaml.java @@ -0,0 +1,75 @@ +package mc.unraveled.reforged.config; + +import lombok.Getter; +import lombok.SneakyThrows; +import mc.unraveled.reforged.api.Baker; +import mc.unraveled.reforged.plugin.Traverse; +import org.bukkit.configuration.file.YamlConfiguration; +import org.jetbrains.annotations.NotNull; + +import java.io.File; + +@Getter +abstract class Yaml extends YamlConfiguration implements Baker { + private final String fileName; + private final File dataFolder; + private final Traverse plugin; + private final File yamlFile; + + @Getter + private boolean baked = false; + + Yaml(@NotNull Traverse plugin, String fileName, File dataFolder, boolean copyDefaults) { + this.fileName = fileName; + this.plugin = plugin; + this.dataFolder = dataFolder; + this.yamlFile = new File(dataFolder, fileName); + + if (copyDefaults) { + plugin.saveResource(fileName, true); + } + + bake(); + } + + Yaml(@NotNull Traverse plugin, String fileName, boolean copyDefaults) { + this(plugin, fileName, plugin.getDataFolder(), copyDefaults); + } + + Yaml(@NotNull Traverse plugin, String fileName) { + this(plugin, fileName, false); + } + + /** + * Makes the file read only after saving to disk. + */ + @SneakyThrows + @Override + public void bake() { + if (baked) return; + + super.save(yamlFile); + + if (yamlFile.setWritable(false)) { + getPlugin().getLogger().info("Baked " + getFileName()); + baked = true; + } else { + getPlugin().getLogger().warning("Failed to bake " + getFileName()); + } + } + + /** + * Allows read and write access to the file. + */ + @Override + public void unbake() { + if (!baked) return; + + if (yamlFile.setWritable(true)) { + getPlugin().getLogger().info("Unbaked " + getFileName()); + baked = false; + } else { + getPlugin().getLogger().warning("Failed to unbake " + getFileName()); + } + } +} diff --git a/src/main/java/mc/unraveled/reforged/config/YamlManager.java b/src/main/java/mc/unraveled/reforged/config/YamlManager.java new file mode 100644 index 0000000..ddcc1ba --- /dev/null +++ b/src/main/java/mc/unraveled/reforged/config/YamlManager.java @@ -0,0 +1,96 @@ +package mc.unraveled.reforged.config; + +import lombok.Getter; +import lombok.SneakyThrows; +import mc.unraveled.reforged.plugin.Traverse; +import org.bukkit.configuration.InvalidConfigurationException; +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +@Getter +public class YamlManager { + private final List configurations = new ArrayList<>(); + private final Traverse plugin; + + public YamlManager(Traverse plugin) { + this.plugin = plugin; + } + + public Builder bldr() { + return new Builder(plugin); + } + + public void insert(Yaml yaml) { + configurations.add(yaml); + } + + public void eject(Yaml yaml) { + configurations.remove(yaml); + } + + @SneakyThrows + public void load(@NotNull Yaml yaml) { + yaml.load(yaml.getYamlFile()); + } + + @SneakyThrows + public void save(@NotNull Yaml yaml) { + yaml.save(yaml.getYamlFile()); + } + + public void loadAll() { + configurations.forEach(y -> { + try { + y.load(y.getYamlFile()); + } catch (IOException | InvalidConfigurationException e) { + getPlugin().getLogger().severe("Failed to load " + y.getYamlFile().getName() + "!"); + getPlugin().getLogger().severe(e.getMessage()); + } + }); + } + + public void saveAll() { + configurations.forEach(y -> { + try { + y.save(y.getYamlFile()); + } catch (IOException e) { + getPlugin().getLogger().severe("Failed to save " + y.getYamlFile().getName() + "!"); + getPlugin().getLogger().severe(e.getMessage()); + } + }); + } + public static class Builder { + + private final Traverse plugin; + private String fileName; + private File dataFolder; + private boolean copyDefaults; + + public Builder(Traverse plugin) { + this.plugin = plugin; + } + + public Builder fileName(String fileName) { + this.fileName = fileName; + return this; + } + + public Builder dataFolder(File dataFolder) { + this.dataFolder = dataFolder; + return this; + } + + public Builder copyDefaults(boolean copyDefaults) { + this.copyDefaults = copyDefaults; + return this; + } + + public Yaml build() { + return new Yaml(plugin, fileName, dataFolder, copyDefaults){}; + } + } +} diff --git a/src/main/java/mc/unraveled/reforged/data/DataManager.java b/src/main/java/mc/unraveled/reforged/data/DataManager.java index b5db73a..80c8d60 100644 --- a/src/main/java/mc/unraveled/reforged/data/DataManager.java +++ b/src/main/java/mc/unraveled/reforged/data/DataManager.java @@ -69,6 +69,7 @@ public final class DataManager implements Baker, Locker { public void saveData(PlayerData data) { DBUser user = new DBUser(plugin.getSQLManager().establish()); user.insert(data); + user.close(); } @SneakyThrows diff --git a/src/main/java/mc/unraveled/reforged/data/InfractionData.java b/src/main/java/mc/unraveled/reforged/data/InfractionData.java new file mode 100644 index 0000000..5534698 --- /dev/null +++ b/src/main/java/mc/unraveled/reforged/data/InfractionData.java @@ -0,0 +1,71 @@ +package mc.unraveled.reforged.data; + +import lombok.Getter; +import lombok.Setter; +import mc.unraveled.reforged.plugin.Traverse; +import mc.unraveled.reforged.storage.DBInfraction; +import org.bukkit.OfflinePlayer; +import org.bukkit.plugin.java.JavaPlugin; +import org.jetbrains.annotations.NotNull; + +import java.util.HashMap; +import java.util.Map; + +public class InfractionData { + private static final Map playerInfractionDataMap = new HashMap<>() {{ + DBInfraction infraction = new DBInfraction(JavaPlugin.getPlugin(Traverse.class).getSQLManager().establish()); + for (InfractionData data : infraction.getStoredInfractionsFromUUID()) { + put(data.getPlayer(), data); + } + }}; + + @Getter + private final OfflinePlayer player; + @Getter + private int infractions = 0; + + @Getter + @Setter + private boolean muted = false; + @Getter + @Setter + private boolean frozen = false; + @Getter + @Setter + private boolean locked = false; + @Getter + @Setter + private boolean jailed = false; + + public InfractionData(OfflinePlayer player) { + this.player = player; + } + + public InfractionData(OfflinePlayer player, int infractions, boolean muted, boolean frozen, boolean locked, boolean jailed) { + this.player = player.getPlayer(); + this.infractions = infractions; + this.muted = muted; + this.frozen = frozen; + this.locked = locked; + this.jailed = jailed; + } + + public static InfractionData getCachedInfractionData(OfflinePlayer player) { + return playerInfractionDataMap.computeIfAbsent(player, InfractionData::new); + } + + public static InfractionData getInfractionFromDB(@NotNull OfflinePlayer player) { + DBInfraction infraction = new DBInfraction(JavaPlugin.getPlugin(Traverse.class).getSQLManager().establish()); + InfractionData data = infraction.getInfraction(player.getUniqueId().toString()); + infraction.close(); + return data; + } + + public void increment() { + infractions++; + } + + public void decrement() { + infractions--; + } +} diff --git a/src/main/java/mc/unraveled/reforged/data/PlayerData.java b/src/main/java/mc/unraveled/reforged/data/PlayerData.java index a18c507..908b554 100644 --- a/src/main/java/mc/unraveled/reforged/data/PlayerData.java +++ b/src/main/java/mc/unraveled/reforged/data/PlayerData.java @@ -16,4 +16,5 @@ public class PlayerData { private long playtime; private int coins; private Date lastLogin; + private InfractionData infractionData; } diff --git a/src/main/java/mc/unraveled/reforged/data/PlayerDataListener.java b/src/main/java/mc/unraveled/reforged/data/PlayerDataListener.java index b056592..3f9114e 100644 --- a/src/main/java/mc/unraveled/reforged/data/PlayerDataListener.java +++ b/src/main/java/mc/unraveled/reforged/data/PlayerDataListener.java @@ -24,7 +24,7 @@ public class PlayerDataListener implements Listener { PlayerData data = plugin.getDataManager().getPlayerData(player.getUniqueId()); if (data == null) { - data = new PlayerData(player.getUniqueId(), player.getName(), Rank.NON_OP, 0L, 0, Date.from(Instant.now())); + data = new PlayerData(player.getUniqueId(), player.getName(), Rank.NON_OP, 0L, 0, Date.from(Instant.now()), InfractionData.getCachedInfractionData(player)); plugin.getDataManager().addPlayerData(data); } diff --git a/src/main/java/mc/unraveled/reforged/storage/DBInfraction.java b/src/main/java/mc/unraveled/reforged/storage/DBInfraction.java new file mode 100644 index 0000000..f376648 --- /dev/null +++ b/src/main/java/mc/unraveled/reforged/storage/DBInfraction.java @@ -0,0 +1,97 @@ +package mc.unraveled.reforged.storage; + +import lombok.SneakyThrows; +import mc.unraveled.reforged.data.InfractionData; +import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +public class DBInfraction { + private final String INSERT = "INSERT INTO infractions (uuid, infractions, muted, frozen, locked, jailed) VALUES (?, ?, ?, ?, ?, ?);"; + private final String UPDATE = "UPDATE infractions SET infractions = ?, muted = ?, frozen = ?, locked = ?, jailed = ? WHERE uuid = ?;"; + private final String SELECT = "SELECT * FROM infractions;"; + private final String SELECT_UUID = "SELECT * FROM infractions WHERE uuid = ?;"; + private final String DELETE = "DELETE FROM infractions WHERE uuid = ?;"; + private final String CREATE_TABLE = "CREATE TABLE IF NOT EXISTS infractions (uuid VARCHAR(36), infractions INT, muted BOOLEAN, frozen BOOLEAN, locked BOOLEAN, jailed BOOLEAN, PRIMARY KEY (uuid));"; + + private final Connection connection; + + public DBInfraction(Connection connection) { + this.connection = connection; + } + + @SneakyThrows + public void createTable() { + PreparedStatement statement = connection.prepareStatement(CREATE_TABLE); + statement.executeUpdate(); + } + + @SneakyThrows + public void insert(Player player, InfractionData data) { + PreparedStatement statement = connection.prepareStatement(SQLConst.IF("infractions", "uuid") + + SQLConst.BEGIN + + INSERT + + SQLConst.END_SPACE + + SQLConst.ELSE + + UPDATE + + SQLConst.END + + SQLConst.SEMICOLON); + statement.setString(1, player.getUniqueId().toString()); + statement.setInt(2, data.getInfractions()); + statement.setBoolean(3, data.isMuted()); + statement.setBoolean(4, data.isFrozen()); + statement.setBoolean(5, data.isLocked()); + statement.setBoolean(6, data.isJailed()); + statement.executeUpdate(); + + } + + @SneakyThrows + public void eject(Player player) { + PreparedStatement statement = connection.prepareStatement(DELETE); + statement.setString(1, player.getUniqueId().toString()); + statement.executeUpdate(); + } + + @SneakyThrows + public List getStoredInfractionsFromUUID() { + List temp = new ArrayList<>(); + + PreparedStatement statement = connection.prepareStatement(SELECT); + ResultSet rs = statement.executeQuery(); + while (rs.next()) { + OfflinePlayer player = Bukkit.getOfflinePlayer(UUID.fromString(rs.getString("uuid"))); + int infractions = rs.getInt("infractions"); + boolean muted = rs.getBoolean("muted"); + boolean frozen = rs.getBoolean("frozen"); + boolean locked = rs.getBoolean("locked"); + boolean jailed = rs.getBoolean("jailed"); + + temp.add(new InfractionData(player, infractions, muted, frozen, locked, jailed)); + } + return temp; + } + + @SneakyThrows + public InfractionData getInfraction(String uuid) { + PreparedStatement statement = connection.prepareStatement(SELECT_UUID); + statement.setString(1, uuid); + ResultSet rs = statement.executeQuery(); + if (rs.next()) { + return new InfractionData(Bukkit.getOfflinePlayer(UUID.fromString(rs.getString("uuid"))), rs.getInt("infractions"), rs.getBoolean("muted"), rs.getBoolean("frozen"), rs.getBoolean("locked"), rs.getBoolean("jailed")); + } + return new InfractionData(Bukkit.getOfflinePlayer(UUID.fromString(uuid))); + } + + @SneakyThrows + public void close() { + connection.close(); + } +} diff --git a/src/main/java/mc/unraveled/reforged/storage/DBUser.java b/src/main/java/mc/unraveled/reforged/storage/DBUser.java index f56b7a9..fc0eba0 100644 --- a/src/main/java/mc/unraveled/reforged/storage/DBUser.java +++ b/src/main/java/mc/unraveled/reforged/storage/DBUser.java @@ -2,8 +2,10 @@ package mc.unraveled.reforged.storage; import lombok.Getter; import lombok.SneakyThrows; +import mc.unraveled.reforged.data.InfractionData; import mc.unraveled.reforged.data.PlayerData; import mc.unraveled.reforged.permission.Rank; +import org.bukkit.Bukkit; import org.jetbrains.annotations.NotNull; import java.sql.Connection; @@ -69,13 +71,16 @@ public class DBUser { PreparedStatement statement = getConnection().prepareStatement("SELECT * FROM users;"); ResultSet resultSet = statement.executeQuery(); List dataList = new ArrayList<>(); + InfractionData data = InfractionData.getCachedInfractionData(Bukkit.getOfflinePlayer(UUID.fromString(resultSet.getString("uuid")))); + while (resultSet.next()) { PlayerData playerData = new PlayerData(UUID.fromString(resultSet.getString("uuid")), resultSet.getString("username"), Rank.valueOf(resultSet.getString("rank")), resultSet.getLong("play_time"), resultSet.getInt("coins"), - new Date(resultSet.getLong("last_login"))); + new Date(resultSet.getLong("last_login")), + data); dataList.add(playerData); } return dataList; diff --git a/src/main/java/mc/unraveled/reforged/storage/SQLConst.java b/src/main/java/mc/unraveled/reforged/storage/SQLConst.java new file mode 100644 index 0000000..36198eb --- /dev/null +++ b/src/main/java/mc/unraveled/reforged/storage/SQLConst.java @@ -0,0 +1,21 @@ +package mc.unraveled.reforged.storage; + +public final class SQLConst { + private SQLConst() { + throw new AssertionError(); + } + + public static final String BEGIN = "BEGIN "; + public static final String END = "END"; + public static final String END_SPACE = "END "; + public static final String SEMICOLON = ";"; + public static final String ELSE = "ELSE "; + + public static String IF(String v) { + return "IF NOT EXISTS (SELECT * FROM " + v + ") "; + } + + public static String IF(String v, String u) { + return "IF NOT EXISTS (SELECT * FROM " + v + " WHERE " + u + " = ?) "; + } +}