From 2308d98dcd47df2e69933fa93258de6bf2976b2e Mon Sep 17 00:00:00 2001 From: tbm00 <89701900+tbm00@users.noreply.github.com> Date: Mon, 26 May 2025 07:51:21 -0500 Subject: [PATCH] Added `/fldelete ` to delete profiles from storage (#1283) * Added `/fldelete ` to delete profiles from storage by name * Implemented requested changes for pull request #1283 * Update delete command's perm default --- .../fastlogin/bukkit/FastLoginBukkit.java | 2 + .../bukkit/command/DeleteCommand.java | 94 +++++++++++++++++++ bukkit/src/main/resources/plugin.yml | 9 ++ .../fastlogin/core/storage/AuthStorage.java | 2 + .../fastlogin/core/storage/SQLStorage.java | 21 +++++ 5 files changed, 128 insertions(+) create mode 100644 bukkit/src/main/java/com/github/games647/fastlogin/bukkit/command/DeleteCommand.java diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/FastLoginBukkit.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/FastLoginBukkit.java index 2f58615d..0ad09b95 100644 --- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/FastLoginBukkit.java +++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/FastLoginBukkit.java @@ -28,6 +28,7 @@ package com.github.games647.fastlogin.bukkit; import com.comphenix.protocol.ProtocolLibrary; import com.github.games647.fastlogin.bukkit.command.CrackedCommand; import com.github.games647.fastlogin.bukkit.command.PremiumCommand; +import com.github.games647.fastlogin.bukkit.command.DeleteCommand; import com.github.games647.fastlogin.bukkit.listener.ConnectionListener; import com.github.games647.fastlogin.bukkit.listener.PaperCacheListener; import com.github.games647.fastlogin.bukkit.listener.protocollib.ProtocolLibListener; @@ -155,6 +156,7 @@ public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin c.setExecutor(new PremiumCommand(this))); Optional.ofNullable(getCommand("cracked")).ifPresent(c -> c.setExecutor(new CrackedCommand(this))); + Optional.ofNullable(getCommand("fldelete")).ifPresent(c -> c.setExecutor(new DeleteCommand(this))); } private boolean initializeFloodgate() { diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/command/DeleteCommand.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/command/DeleteCommand.java new file mode 100644 index 00000000..63973eec --- /dev/null +++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/command/DeleteCommand.java @@ -0,0 +1,94 @@ +/* + * SPDX-License-Identifier: MIT + * + * The MIT License (MIT) + * + * Copyright (c) 2015-2024 games647 and contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.github.games647.fastlogin.bukkit.command; + +import java.util.ArrayList; +import java.util.List; + +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.command.ConsoleCommandSender; +import org.bukkit.command.TabExecutor; +import org.bukkit.entity.Player; + +import com.github.games647.fastlogin.bukkit.FastLoginBukkit; + +public class DeleteCommand implements TabExecutor { + private final FastLoginBukkit plugin; + + public DeleteCommand(FastLoginBukkit plugin) { + this.plugin = plugin; + } + + /** + * Handles the command to delete profiles. + */ + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + + if (!sender.hasPermission(command.getPermission())) { + plugin.getCore().sendLocaleMessage("no-permission", sender); + return true; + } + + if (plugin.getBungeeManager().isEnabled()) { + sender.sendMessage("Error: Cannot delete profile entries when using BungeeCord!"); + return false; + } + + if (args.length < 1) { + sender.sendMessage("Error: Must supply username to delete!"); + return false; + } + + Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> { + int count = plugin.getCore().getStorage().deleteProfile(args[0]); + if (!(sender instanceof ConsoleCommandSender)) { + Bukkit.getScheduler().runTask(plugin, () -> { + if (count == 0) { + sender.sendMessage("Error: No profile entries found!"); + } else { + sender.sendMessage("Deleted " + count + " matching profile entries"); + } + }); + } + }); + + return true; + } + + @Override + public List onTabComplete(CommandSender sender, Command command, String alias, String[] args) { + List list = new ArrayList<>(); + for (Player p : Bukkit.getOnlinePlayers()) { + if (p.getName().toLowerCase().startsWith(args[0])) { + list.add(p.getName()); + } + } + return null; + } +} diff --git a/bukkit/src/main/resources/plugin.yml b/bukkit/src/main/resources/plugin.yml index c6d810a3..ee2562ab 100644 --- a/bukkit/src/main/resources/plugin.yml +++ b/bukkit/src/main/resources/plugin.yml @@ -45,6 +45,11 @@ commands: usage: / [player] permission: ${project.artifactId}.command.cracked + fldelete: + description: 'Delete player profile data' + usage: / [player] + permission: ${project.artifactId}.command.delete + permissions: ${project.artifactId}.command.premium: description: 'Label themselves as premium' @@ -63,3 +68,7 @@ permissions: description: 'Label others as cracked' children: ${project.artifactId}.command.cracked: true + + ${project.artifactId}.command.delete: + description: 'Delete other players profile data' + default: op \ No newline at end of file diff --git a/core/src/main/java/com/github/games647/fastlogin/core/storage/AuthStorage.java b/core/src/main/java/com/github/games647/fastlogin/core/storage/AuthStorage.java index e88bf085..d561f9ee 100644 --- a/core/src/main/java/com/github/games647/fastlogin/core/storage/AuthStorage.java +++ b/core/src/main/java/com/github/games647/fastlogin/core/storage/AuthStorage.java @@ -32,6 +32,8 @@ public interface AuthStorage { StoredProfile loadProfile(UUID uuid); + int deleteProfile(String name); + void save(StoredProfile playerProfile); void close(); diff --git a/core/src/main/java/com/github/games647/fastlogin/core/storage/SQLStorage.java b/core/src/main/java/com/github/games647/fastlogin/core/storage/SQLStorage.java index 8eb70035..6d1089ea 100644 --- a/core/src/main/java/com/github/games647/fastlogin/core/storage/SQLStorage.java +++ b/core/src/main/java/com/github/games647/fastlogin/core/storage/SQLStorage.java @@ -65,6 +65,8 @@ public abstract class SQLStorage implements AuthStorage { + "` WHERE `Name`=? LIMIT 1"; protected static final String LOAD_BY_UUID = "SELECT * FROM `" + PREMIUM_TABLE + "` WHERE `UUID`=? LIMIT 1"; + protected static final String DELETE_BY_NAME = "DELETE FROM " + PREMIUM_TABLE + + " WHERE `Name` = ?"; protected static final String INSERT_PROFILE = "INSERT INTO `" + PREMIUM_TABLE + "` (`UUID`, `Name`, `Premium`, `Floodgate`, `LastIp`) " + "VALUES (?, ?, ?, ?, ?) "; // limit not necessary here, because it's unique @@ -143,6 +145,25 @@ public abstract class SQLStorage implements AuthStorage { return null; } + @Override + public int deleteProfile(String name) { + try (Connection con = dataSource.getConnection(); + PreparedStatement deleteStmt = con.prepareStatement(DELETE_BY_NAME)) { + deleteStmt.setString(1, name); + + int rowsDeleted = deleteStmt.executeUpdate(); + if (rowsDeleted > 0) { + log.info("Deleted {}'s profile data", name); + } else { + log.info("No profile data found for {}", name); + } + return rowsDeleted; + } catch (SQLException sqlEx) { + log.error("Failed to query profile: {}", name, sqlEx); + return 0; + } + } + private Optional parseResult(ResultSet resultSet) throws SQLException { if (resultSet.next()) { long userId = resultSet.getInt("UserID");