diff --git a/src/main/java/emu/grasscutter/command/commands/AccountCommand.java b/src/main/java/emu/grasscutter/command/commands/AccountCommand.java index 4b287afa7..a16cc6480 100644 --- a/src/main/java/emu/grasscutter/command/commands/AccountCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/AccountCommand.java @@ -1,8 +1,10 @@ package emu.grasscutter.command.commands; +import emu.grasscutter.Grasscutter; import emu.grasscutter.command.Command; import emu.grasscutter.command.CommandHandler; import emu.grasscutter.database.DatabaseHelper; +import emu.grasscutter.game.Account; import emu.grasscutter.game.player.Player; import java.util.List; @@ -54,11 +56,24 @@ public final class AccountCommand implements CommandHandler { } return; case "delete": - if (DatabaseHelper.deleteAccount(username)) { - CommandHandler.sendMessage(null, translate("commands.account.delete")); - } else { + // Get the account we want to delete. + Account toDelete = DatabaseHelper.getAccountByName(username); + + if (toDelete == null) { CommandHandler.sendMessage(null, translate("commands.account.no_account")); + return; } + + // Get the player for the account. + // If that player is currently online, we kick them before proceeding with the deletion. + Player player = Grasscutter.getGameServer().getPlayerByUid(toDelete.getPlayerUid()); + if (player != null) { + player.getSession().close(); + } + + // Finally, we do the actual deletion. + DatabaseHelper.deleteAccount(toDelete); + CommandHandler.sendMessage(null, translate("commands.account.delete")); } } } diff --git a/src/main/java/emu/grasscutter/database/DatabaseHelper.java b/src/main/java/emu/grasscutter/database/DatabaseHelper.java index f63798988..14a3e1c72 100644 --- a/src/main/java/emu/grasscutter/database/DatabaseHelper.java +++ b/src/main/java/emu/grasscutter/database/DatabaseHelper.java @@ -3,6 +3,8 @@ package emu.grasscutter.database; import java.util.List; import com.mongodb.client.result.DeleteResult; + +import dev.morphia.experimental.MorphiaSession; import dev.morphia.query.FindOptions; import dev.morphia.query.Sort; import dev.morphia.query.experimental.filters.Filters; @@ -95,8 +97,31 @@ public final class DatabaseHelper { return DatabaseManager.getDatastore().find(Account.class).filter(Filters.eq("playerId", playerId)).first(); } - public static boolean deleteAccount(String username) { - return DatabaseManager.getDatastore().find(Account.class).filter(Filters.eq("username", username)).delete().getDeletedCount() > 0; + //public static boolean deleteAccount(String username) { + // return DatabaseManager.getDatastore().find(Account.class).filter(Filters.eq("username", username)).delete().getDeletedCount() > 0; + //} + public static void deleteAccount(Account target) { + // To delete an account, we need to also delete all the other documents in the database that reference the account. + // This should optimally be wrapped inside a transaction, to make sure an error thrown mid-way does not leave the + // database in an inconsistent state, but unfortunately Mongo only supports that when we have a replica set ... + + // Delete mails, gacha records, items and avatars. + DatabaseManager.getDatastore().find(Mail.class).filter(Filters.eq("ownerUid", target.getPlayerUid())).delete(); + DatabaseManager.getDatastore().find(GachaRecord.class).filter(Filters.eq("ownerId", target.getPlayerUid())).delete(); + DatabaseManager.getDatastore().find(GameItem.class).filter(Filters.eq("ownerId", target.getPlayerUid())).delete(); + DatabaseManager.getDatastore().find(Avatar.class).filter(Filters.eq("ownerId", target.getPlayerUid())).delete(); + + // Delete friendships. + // Here, we need to make sure to not only delete the deleted account's friendships, + // but also all friendship entries for that account's friends. + DatabaseManager.getDatastore().find(Friendship.class).filter(Filters.eq("ownerId", target.getPlayerUid())).delete(); + DatabaseManager.getDatastore().find(Friendship.class).filter(Filters.eq("friendId", target.getPlayerUid())).delete(); + + // Delete the player. + DatabaseManager.getDatastore().find(Player.class).filter(Filters.eq("id", target.getPlayerUid())).delete(); + + // Finally, delete the account itself. + DatabaseManager.getDatastore().find(Account.class).filter(Filters.eq("id", target.getId())).delete(); } public static List getAllPlayers() {