3131import org .jetbrains .annotations .Nullable ;
3232
3333import java .io .IOException ;
34- import java .nio .charset .StandardCharsets ;
3534import java .util .UUID ;
3635import java .util .concurrent .CompletableFuture ;
3736
@@ -48,7 +47,7 @@ private InspectCommandUtil() {
4847 /**
4948 * Attempts to resolve a player identifier (name or UUID) to a UUID asynchronously.
5049 * This method first tries to parse as UUID, then checks online players synchronously,
51- * and finally searches offline players asynchronously.
50+ * and finally searches cached offline players and Mojang asynchronously.
5251 *
5352 * @param identifier Player name or UUID string
5453 * @return CompletableFuture containing UUID if found, null otherwise
@@ -68,53 +67,30 @@ public static CompletableFuture<UUID> resolvePlayerIdentifierAsync(String identi
6867 return CompletableFuture .completedFuture (onlinePlayer .getUniqueId ());
6968 }
7069
71- // Look up UUID via Mojang API (avoids the very slow Bukkit.getOfflinePlayers() scan)
7270 return CompletableFuture .supplyAsync (() -> {
73- try {
74- OkHttpClient client = new OkHttpClient ();
75- Request request = new Request .Builder ()
76- .url ("https://api.mojang.com/users/profiles/minecraft/" + identifier )
77- .build ();
78- Response response = client .newCall (request ).execute ();
79- if (response .isSuccessful () && response .body () != null ) {
80- String body = response .body ().string ();
81- // Parse the "id" field: {"id":"<uuid-no-dashes>","name":"<name>"}
82- int idStart = body .indexOf ("\" id\" :\" " ) + 6 ;
83- int idEnd = body .indexOf ("\" " , idStart );
84- if (idStart > 5 && idEnd > idStart ) {
85- String raw = body .substring (idStart , idEnd );
86- // Insert dashes into the 32-char UUID string
87- String formatted = raw .substring (0 , 8 ) + "-"
88- + raw .substring (8 , 12 ) + "-"
89- + raw .substring (12 , 16 ) + "-"
90- + raw .substring (16 , 20 ) + "-"
91- + raw .substring (20 );
92- return UUID .fromString (formatted );
93- }
94- }
95- } catch (IOException ignored ) {
96- // Fall through to offline UUID computation
71+ UUID cachedOfflinePlayer = findCachedOfflinePlayerUuid (identifier );
72+ if (cachedOfflinePlayer != null ) {
73+ return cachedOfflinePlayer ;
9774 }
98- // Fallback for offline/cracked-mode servers: compute deterministic offline UUID
99- return UUID . nameUUIDFromBytes (( "OfflinePlayer:" + identifier ). getBytes ( StandardCharsets . UTF_8 ) );
75+
76+ return lookupPlayerUuidFromMojang ( identifier );
10077 });
10178 }
10279
10380 /**
104- * Gets a player's name from their UUID, falling back to UUID string if name is not available .
81+ * Gets a player's name from their UUID.
10582 *
10683 * @param uuid Player UUID
107- * @return Player name or UUID string
84+ * @return Player name, or null if the UUID cannot be associated with a known player name
10885 */
109- public static String getPlayerName (@ NotNull UUID uuid ) {
86+ public static @ Nullable String getPlayerName (@ NotNull UUID uuid ) {
11087 Player onlinePlayer = Bukkit .getPlayer (uuid );
11188 if (onlinePlayer != null ) {
11289 return onlinePlayer .getName ();
11390 }
11491
11592 OfflinePlayer offlinePlayer = Bukkit .getOfflinePlayer (uuid );
116- String name = offlinePlayer .getName ();
117- return name != null ? name : uuid .toString ();
93+ return offlinePlayer .getName ();
11894 }
11995
12096 /**
@@ -128,4 +104,55 @@ public static void showUsage(@NotNull Player player, @NotNull String commandName
128104 ERROR_PREFIX .append (
129105 mm .deserialize ("<red>Usage: /" + commandName + " <player|uuid> <slot></red>" )));
130106 }
107+
108+ private static @ Nullable UUID findCachedOfflinePlayerUuid (@ NotNull String identifier ) {
109+ for (OfflinePlayer offlinePlayer : Bukkit .getOfflinePlayers ()) {
110+ String name = offlinePlayer .getName ();
111+ if (name != null && name .equalsIgnoreCase (identifier )) {
112+ return offlinePlayer .getUniqueId ();
113+ }
114+ }
115+ return null ;
116+ }
117+
118+ private static @ Nullable UUID lookupPlayerUuidFromMojang (@ NotNull String identifier ) {
119+ try {
120+ OkHttpClient client = new OkHttpClient ();
121+ Request request = new Request .Builder ()
122+ .url ("https://api.mojang.com/users/profiles/minecraft/" + identifier )
123+ .build ();
124+ Response response = client .newCall (request ).execute ();
125+ try {
126+ if (!response .isSuccessful () || response .body () == null ) {
127+ return null ;
128+ }
129+
130+ String body = response .body ().string ();
131+ int idStart = body .indexOf ("\" id\" :\" " );
132+ if (idStart < 0 ) {
133+ return null ;
134+ }
135+
136+ idStart += 6 ;
137+ int idEnd = body .indexOf ("\" " , idStart );
138+ if (idEnd <= idStart ) {
139+ return null ;
140+ }
141+
142+ String raw = body .substring (idStart , idEnd );
143+ String formatted = raw .substring (0 , 8 ) + "-"
144+ + raw .substring (8 , 12 ) + "-"
145+ + raw .substring (12 , 16 ) + "-"
146+ + raw .substring (16 , 20 ) + "-"
147+ + raw .substring (20 );
148+ return UUID .fromString (formatted );
149+ } finally {
150+ if (response .body () != null ) {
151+ response .body ().close ();
152+ }
153+ }
154+ } catch (IOException | IllegalArgumentException ignored ) {
155+ return null ;
156+ }
157+ }
131158}
0 commit comments