From 2a6148068415f4441887670dc74eb733a6dd5013 Mon Sep 17 00:00:00 2001
From: EpicKnarvik97 <kristian.knarvik@knett.no>
Date: Mon, 1 Nov 2021 15:13:22 +0100
Subject: [PATCH] Adds UUID fetching on player join. See #12

Whenever a player joins, their names will be checked against a map containing all names which need to be migrated to UUID. All portals the player has created which still use the player name will be updated.
---
 .../listener/PlayerEventListener.java         |  41 +------
 .../stargate/portal/PortalOwner.java          |  16 +++
 .../stargate/utility/BungeeHelper.java        |  36 ++++++
 .../stargate/utility/UUIDMigrationHelper.java | 111 ++++++++++++++++++
 4 files changed, 168 insertions(+), 36 deletions(-)
 create mode 100644 src/main/java/net/knarcraft/stargate/utility/UUIDMigrationHelper.java

diff --git a/src/main/java/net/knarcraft/stargate/listener/PlayerEventListener.java b/src/main/java/net/knarcraft/stargate/listener/PlayerEventListener.java
index 4816eb2..f428ac2 100644
--- a/src/main/java/net/knarcraft/stargate/listener/PlayerEventListener.java
+++ b/src/main/java/net/knarcraft/stargate/listener/PlayerEventListener.java
@@ -10,6 +10,7 @@ import net.knarcraft.stargate.portal.VehicleTeleporter;
 import net.knarcraft.stargate.utility.BungeeHelper;
 import net.knarcraft.stargate.utility.MaterialHelper;
 import net.knarcraft.stargate.utility.PermissionHelper;
+import net.knarcraft.stargate.utility.UUIDMigrationHelper;
 import org.bukkit.GameMode;
 import org.bukkit.block.Block;
 import org.bukkit.block.data.type.WallSign;
@@ -42,6 +43,9 @@ public class PlayerEventListener implements Listener {
      */
     @EventHandler
     public void onPlayerJoin(PlayerJoinEvent event) {
+        //Migrate player name to UUID if necessary
+        UUIDMigrationHelper.migrateUUID(event.getPlayer());
+
         if (!Stargate.getGateConfig().enableBungee()) {
             return;
         }
@@ -154,7 +158,7 @@ public class PlayerEventListener implements Listener {
 
         //Decide if the user should be teleported to another bungee server
         if (entrancePortal.getOptions().isBungee()) {
-            if (bungeeTeleport(player, entrancePortal, event)) {
+            if (BungeeHelper.bungeeTeleport(player, entrancePortal, event)) {
                 Stargate.getMessageSender().sendSuccessMessage(player, Stargate.getString("teleportMsg"));
             }
             return false;
@@ -306,39 +310,4 @@ public class PlayerEventListener implements Listener {
         return false;
     }
 
-    /**
-     * Teleports a player to a bungee gate
-     *
-     * @param player         <p>The player to teleport</p>
-     * @param entrancePortal <p>The gate the player is entering from</p>
-     * @param event          <p>The event causing the teleportation</p>
-     * @return <p>True if the teleportation was successful</p>
-     */
-    private boolean bungeeTeleport(Player player, Portal entrancePortal, PlayerMoveEvent event) {
-        //Check if bungee is actually enabled
-        if (!Stargate.getGateConfig().enableBungee()) {
-            Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("bungeeDisabled"));
-            entrancePortal.getPortalOpener().closePortal(false);
-            return false;
-        }
-
-        //Teleport the player back to this gate, for sanity's sake
-        new PlayerTeleporter(entrancePortal, player).teleport(entrancePortal, event);
-
-        //Send the SGBungee packet first, it will be queued by BC if required
-        if (!BungeeHelper.sendTeleportationMessage(player, entrancePortal)) {
-            Stargate.debug("bungeeTeleport", "Unable to send teleportation message");
-            return false;
-        }
-
-        //Send the connect-message to make the player change server
-        if (!BungeeHelper.changeServer(player, entrancePortal)) {
-            Stargate.debug("bungeeTeleport", "Unable to change server");
-            return false;
-        }
-
-        Stargate.debug("bungeeTeleport", "Teleported player to another server");
-        return true;
-    }
-
 }
diff --git a/src/main/java/net/knarcraft/stargate/portal/PortalOwner.java b/src/main/java/net/knarcraft/stargate/portal/PortalOwner.java
index 019f57d..eb92fe8 100644
--- a/src/main/java/net/knarcraft/stargate/portal/PortalOwner.java
+++ b/src/main/java/net/knarcraft/stargate/portal/PortalOwner.java
@@ -43,6 +43,22 @@ public class PortalOwner {
         return ownerUUID;
     }
 
+    /**
+     * Sets the unique id for a portal owner without one
+     *
+     * <p>This method is only meant to be used to set the unique id for an owner without one. If the owner already has
+     * an unique id, an exception will be thrown.</p>
+     *
+     * @param uniqueId <p>The new unique id for the portal owner</p>
+     */
+    public void setUUID(UUID uniqueId) {
+        if (ownerUUID == null) {
+            ownerUUID = uniqueId;
+        } else {
+            throw new IllegalArgumentException("An existing UUID cannot be overwritten.");
+        }
+    }
+
     /**
      * Gets the name of this owner
      *
diff --git a/src/main/java/net/knarcraft/stargate/utility/BungeeHelper.java b/src/main/java/net/knarcraft/stargate/utility/BungeeHelper.java
index 81f608e..35229c2 100644
--- a/src/main/java/net/knarcraft/stargate/utility/BungeeHelper.java
+++ b/src/main/java/net/knarcraft/stargate/utility/BungeeHelper.java
@@ -5,6 +5,7 @@ import net.knarcraft.stargate.portal.PlayerTeleporter;
 import net.knarcraft.stargate.portal.Portal;
 import net.knarcraft.stargate.portal.PortalHandler;
 import org.bukkit.entity.Player;
+import org.bukkit.event.player.PlayerMoveEvent;
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
@@ -169,4 +170,39 @@ public final class BungeeHelper {
         }
     }
 
+    /**
+     * Teleports a player to a bungee gate
+     *
+     * @param player         <p>The player to teleport</p>
+     * @param entrancePortal <p>The gate the player is entering from</p>
+     * @param event          <p>The event causing the teleportation</p>
+     * @return <p>True if the teleportation was successful</p>
+     */
+    public static boolean bungeeTeleport(Player player, Portal entrancePortal, PlayerMoveEvent event) {
+        //Check if bungee is actually enabled
+        if (!Stargate.getGateConfig().enableBungee()) {
+            Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("bungeeDisabled"));
+            entrancePortal.getPortalOpener().closePortal(false);
+            return false;
+        }
+
+        //Teleport the player back to this gate, for sanity's sake
+        new PlayerTeleporter(entrancePortal, player).teleport(entrancePortal, event);
+
+        //Send the SGBungee packet first, it will be queued by BC if required
+        if (!BungeeHelper.sendTeleportationMessage(player, entrancePortal)) {
+            Stargate.debug("bungeeTeleport", "Unable to send teleportation message");
+            return false;
+        }
+
+        //Send the connect-message to make the player change server
+        if (!BungeeHelper.changeServer(player, entrancePortal)) {
+            Stargate.debug("bungeeTeleport", "Unable to change server");
+            return false;
+        }
+
+        Stargate.debug("bungeeTeleport", "Teleported player to another server");
+        return true;
+    }
+
 }
diff --git a/src/main/java/net/knarcraft/stargate/utility/UUIDMigrationHelper.java b/src/main/java/net/knarcraft/stargate/utility/UUIDMigrationHelper.java
new file mode 100644
index 0000000..e58141c
--- /dev/null
+++ b/src/main/java/net/knarcraft/stargate/utility/UUIDMigrationHelper.java
@@ -0,0 +1,111 @@
+package net.knarcraft.stargate.utility;
+
+import net.knarcraft.stargate.Stargate;
+import net.knarcraft.stargate.portal.Portal;
+import net.knarcraft.stargate.portal.PortalHandler;
+import net.knarcraft.stargate.portal.PortalOwner;
+import net.knarcraft.stargate.portal.PortalRegistry;
+import org.bukkit.OfflinePlayer;
+import org.bukkit.World;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * Helps migrate player names to UUID where necessary
+ */
+public class UUIDMigrationHelper {
+
+    private static Map<String, List<Portal>> playerNamesToMigrate;
+
+    /**
+     * Migrates the player's name to a UUID
+     *
+     * <p>If any portals are missing a UUID for their owner, and the given player is the owner of those portals, the
+     * given player's UUID will be used as UUID for the portals' owner.</p>
+     *
+     * @param player <p>The player to migrate</p>
+     */
+    public static void migrateUUID(OfflinePlayer player) {
+        Map<String, List<Portal>> playersToMigrate = getPlayersToMigrate();
+        String playerName = player.getName();
+
+        //Nothing to do
+        if (!playersToMigrate.containsKey(playerName)) {
+            return;
+        }
+
+        Stargate.debug("PlayerEventListener::migrateUUID", String.format("Migrating name to UUID for player %s",
+                playerName));
+        List<Portal> portalsOwned = playersToMigrate.get(playerName);
+        if (portalsOwned == null) {
+            return;
+        }
+
+        migratePortalsToUUID(portalsOwned, player.getUniqueId());
+
+        //Remove the player to prevent the migration to happen every time the player joins
+        playersToMigrate.remove(playerName);
+    }
+
+    /**
+     * Migrates a list of portals to use UUID instead of only player name
+     *
+     * @param portals  <p>The portals to migrate</p>
+     * @param uniqueId <p>The unique ID of the portals' owner</p>
+     */
+    private static void migratePortalsToUUID(List<Portal> portals, UUID uniqueId) {
+        Set<World> worldsToSave = new HashSet<>();
+
+        //Get the real portal from the copy and set UUID
+        for (Portal portalCopy : portals) {
+            Portal portal = PortalHandler.getByName(portalCopy.getName(), portalCopy.getNetwork());
+            if (portal != null) {
+                portal.getOwner().setUUID(uniqueId);
+                worldsToSave.add(portal.getWorld());
+            }
+        }
+
+        //Need to make sure the changes are saved
+        for (World world : worldsToSave) {
+            PortalFileHelper.saveAllPortals(world);
+        }
+    }
+
+    /**
+     * Gets all player names which need to be migrated to UUIDs
+     *
+     * @return <p>The player names to migrate</p>
+     */
+    private static Map<String, List<Portal>> getPlayersToMigrate() {
+        //Make sure to only go through portals once
+        if (playerNamesToMigrate != null) {
+            return playerNamesToMigrate;
+        }
+
+        playerNamesToMigrate = new HashMap<>();
+        for (Portal portal : PortalRegistry.getAllPortals()) {
+            PortalOwner owner = portal.getOwner();
+            String ownerName = owner.getName();
+
+            //If a UUID is missing, add the portal to the list owned by the player
+            if (owner.getUUID() == null) {
+                List<Portal> portalList = playerNamesToMigrate.get(ownerName);
+                if (portalList == null) {
+                    List<Portal> newList = new ArrayList<>();
+                    newList.add(portal);
+                    playerNamesToMigrate.put(ownerName, newList);
+                } else {
+                    portalList.add(portal);
+                }
+            }
+        }
+        return playerNamesToMigrate;
+    }
+
+}