From 19e798a28b6420fa1785d4ead556c0a4de764ac1 Mon Sep 17 00:00:00 2001
From: ImmuState <kyoko12@gmx.at>
Date: Mon, 9 May 2022 10:03:51 -0700
Subject: [PATCH] Change ccount delete to delete all referenced items in the
 database.

---
 .../command/commands/AccountCommand.java      | 21 ++++++++++++--
 .../grasscutter/database/DatabaseHelper.java  | 29 +++++++++++++++++--
 2 files changed, 45 insertions(+), 5 deletions(-)

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<Player> getAllPlayers() {
