diff --git a/Spigot-API-Patches/Basic-PlayerProfile-API.patch b/Spigot-API-Patches/Basic-PlayerProfile-API.patch
index c28657d172..7583bbbec2 100644
--- a/Spigot-API-Patches/Basic-PlayerProfile-API.patch
+++ b/Spigot-API-Patches/Basic-PlayerProfile-API.patch
@@ -128,6 +128,25 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +    boolean completeFromCache();
 +
 +    /**
++     * Like {@link #complete(boolean)} but will try only from cache, and not make network calls
++     * Does not account for textures.
++     *
++     * @param onlineMode Treat this as online mode or not
++     * @return If the profile is now complete (has UUID and Name)
++     */
++    boolean completeFromCache(boolean onlineMode);
++
++    /**
++     * Like {@link #complete(boolean)} but will try only from cache, and not make network calls
++     * Does not account for textures.
++     *
++     * @param lookupUUID If only name is supplied, should we do a UUID lookup
++     * @param onlineMode Treat this as online mode or not
++     * @return If the profile is now complete (has UUID and Name)
++     */
++    boolean completeFromCache(boolean lookupUUID, boolean onlineMode);
++
++    /**
 +     * If this profile is not complete, then make the API call to complete it.
 +     * This is a blocking operation and should be done asynchronously.
 +     *
@@ -143,12 +162,25 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +     * This is a blocking operation and should be done asynchronously.
 +     *
 +     * Optionally will also fill textures.
++     *
++     * Online mode will be automatically determined
 +     * @param textures controls if we should fill the profile with texture properties
 +     * @return If the profile is now complete (has UUID and Name) (if you get rate limited, this operation may fail)
 +     */
 +    boolean complete(boolean textures);
 +
 +    /**
++     * If this profile is not complete, then make the API call to complete it.
++     * This is a blocking operation and should be done asynchronously.
++     *
++     * Optionally will also fill textures.
++     * @param textures controls if we should fill the profile with texture properties
++     * @param onlineMode Treat this server as online mode or not
++     * @return If the profile is now complete (has UUID and Name) (if you get rate limited, this operation may fail)
++     */
++    boolean complete(boolean textures, boolean onlineMode);
++
++    /**
 +     * Whether or not this Profile has textures associated to it
 +     * @return If has a textures property
 +     */
diff --git a/Spigot-Server-Patches/Basic-PlayerProfile-API.patch b/Spigot-Server-Patches/Basic-PlayerProfile-API.patch
index 79329ad63c..a15cdbb8dc 100644
--- a/Spigot-Server-Patches/Basic-PlayerProfile-API.patch
+++ b/Spigot-Server-Patches/Basic-PlayerProfile-API.patch
@@ -149,18 +149,22 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +
 +    @Override
 +    public boolean completeFromCache() {
-+        return completeFromCache(false);
++        MinecraftServer server = MinecraftServer.getServer();
++        return completeFromCache(false, server.getOnlineMode() || (SpigotConfig.bungee && PaperConfig.bungeeOnlineMode));
 +    }
 +
-+    public boolean completeFromCache(boolean lookupName) {
++    public boolean completeFromCache(boolean onlineMode) {
++        return completeFromCache(false, onlineMode);
++    }
++
++    public boolean completeFromCache(boolean lookupUUID, boolean onlineMode) {
 +        MinecraftServer server = MinecraftServer.getServer();
 +        String name = profile.getName();
 +        UserCache userCache = server.getUserCache();
 +        if (profile.getId() == null) {
 +            final GameProfile profile;
-+            boolean isOnlineMode = server.getOnlineMode() || (SpigotConfig.bungee && PaperConfig.bungeeOnlineMode);
-+            if (isOnlineMode) {
-+                profile = lookupName ? userCache.getProfile(name) : userCache.getProfileIfCached(name);
++            if (onlineMode) {
++                profile = lookupUUID ? userCache.getProfile(name) : userCache.getProfileIfCached(name);
 +            } else {
 +                // Make an OfflinePlayer using an offline mode UUID since the name has no profile
 +                profile = new GameProfile(UUID.nameUUIDFromBytes(("OfflinePlayer:" + name).getBytes(Charsets.UTF_8)), name);
@@ -185,17 +189,20 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +
 +    public boolean complete(boolean textures) {
 +        MinecraftServer server = MinecraftServer.getServer();
++        return complete(textures, server.getOnlineMode() || (SpigotConfig.bungee && PaperConfig.bungeeOnlineMode));
++    }
++    public boolean complete(boolean textures, boolean onlineMode) {
++        MinecraftServer server = MinecraftServer.getServer();
 +
-+        boolean isOnlineMode = server.getOnlineMode() || (SpigotConfig.bungee && PaperConfig.bungeeOnlineMode);
-+        boolean isCompleteFromCache = this.completeFromCache(true);
-+        if (isOnlineMode && (!isCompleteFromCache || textures && !hasTextures())) {
++        boolean isCompleteFromCache = this.completeFromCache(false, onlineMode);
++        if (onlineMode && (!isCompleteFromCache || textures && !hasTextures())) {
 +            GameProfile result = server.getSessionService().fillProfileProperties(profile, true);
 +            if (result != null) {
 +                copyProfileProperties(result, this.profile, true);
 +            }
 +            server.getUserCache().saveProfile(this.profile);
 +        }
-+        return profile.isComplete() && (!isOnlineMode || !textures || hasTextures());
++        return profile.isComplete() && (!onlineMode || !textures || hasTextures());
 +    }
 +
 +    private static void copyProfileProperties(GameProfile source, GameProfile target) {
@@ -478,9 +485,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +                com.destroystokyo.paper.profile.CraftPlayerProfile paperProfile = new com.destroystokyo.paper.profile.CraftPlayerProfile(gameprofile);
 +                if (sync) {
 +                    // might complete by cache, but if not, go ahead and do it now, avoid the code below
-+                    paperProfile.complete(true);
++                    paperProfile.complete(true, true);
 +                } else {
-+                    paperProfile.completeFromCache();
++                    paperProfile.completeFromCache(false, true);
 +                }
 +                GameProfile profile = paperProfile.getGameProfile();
 +                // Paper end
@@ -493,7 +500,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
                          public GameProfile call() {
 -                            final GameProfile profile = skinCache.getUnchecked(gameprofile.getName().toLowerCase(java.util.Locale.ROOT));
 +                            // Paper start
-+                            paperProfile.complete(true);
++                            paperProfile.complete(true, true);
 +                            final GameProfile profile = paperProfile.getGameProfile();
 +                            // Paper end
                              MinecraftServer.getServer().processQueue.add(new Runnable() {
@@ -593,7 +600,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +        // Paper start
 +        if (profile != null) {
 +            com.destroystokyo.paper.profile.CraftPlayerProfile paperProfile = new com.destroystokyo.paper.profile.CraftPlayerProfile(profile);
-+            paperProfile.completeFromCache();
++            paperProfile.completeFromCache(false, true);
 +            profile = paperProfile.getGameProfile();
 +        }
 +        // Paper end