11package cat .nyaa .nyaacore .utils ;
22
3+ import cat .nyaa .nyaacore .NyaaCoreLoader ;
4+ import cat .nyaa .nyaacore .http .client .HttpClient ;
5+ import com .google .common .collect .BiMap ;
6+ import com .google .common .collect .HashBiMap ;
7+ import com .google .common .collect .ImmutableBiMap ;
8+ import com .google .gson .Gson ;
9+ import com .google .gson .reflect .TypeToken ;
10+ import io .netty .handler .codec .http .FullHttpResponse ;
311import org .bukkit .Bukkit ;
412import org .bukkit .OfflinePlayer ;
513import org .bukkit .event .EventHandler ;
816import org .bukkit .event .player .PlayerJoinEvent ;
917import org .bukkit .event .player .PlayerQuitEvent ;
1018
11- import java .util .Arrays ;
12- import java .util .Comparator ;
13- import java .util .Locale ;
14- import java .util .Map ;
19+ import java .util .*;
20+ import java .util .concurrent .CompletableFuture ;
21+ import java .util .concurrent .ConcurrentMap ;
1522import java .util .function .BinaryOperator ;
1623import java .util .function .Function ;
24+ import java .util .logging .Level ;
1725import java .util .stream .Collectors ;
26+ import java .util .stream .Stream ;
27+
28+ import static java .nio .charset .StandardCharsets .UTF_8 ;
1829
1930public class OfflinePlayerUtils {
20- private static Map <String , OfflinePlayer > playerCache ;
31+ private static ConcurrentMap <String , OfflinePlayer > playerCache ;
32+ private static ConcurrentMap <UUID , String > nameCache ;
33+ private static TypeToken <List <Map <String , Object >>> typeTokenListMap = new TypeToken <List <Map <String , Object >>>() {
34+ };
35+ private static final String UNDASHED = "(\\ w{8})(\\ w{4})(\\ w{4})(\\ w{4})(\\ w{12})" ;
36+ private static final String DASHED = "$1-$2-$3-$4-$5" ;
2137
2238 private OfflinePlayerUtils () {
2339 }
2440
2541 public static void init () {
2642 playerCache = Arrays .stream (Bukkit .getOfflinePlayers ())
2743 .filter (p -> p .getName () != null )
28- .collect (Collectors .toMap (p -> p .getName ().toLowerCase (Locale .ENGLISH ), Function .identity (), BinaryOperator .maxBy (Comparator .comparing (OfflinePlayer ::getLastPlayed ))));
44+ .collect (Collectors .toConcurrentMap (p -> p .getName ().toLowerCase (Locale .ENGLISH ), Function .identity (), BinaryOperator .maxBy (Comparator .comparing (OfflinePlayer ::getLastPlayed ))));
45+ nameCache = playerCache .entrySet ().stream ().collect (Collectors .toConcurrentMap (e -> e .getValue ().getUniqueId (), Map .Entry ::getKey ));
2946 }
3047
3148 public static OfflinePlayer lookupPlayer (String name ) {
@@ -34,6 +51,63 @@ public static OfflinePlayer lookupPlayer(String name) {
3451 return playerCache .get (name .toLowerCase (Locale .ENGLISH ));
3552 }
3653
54+ public static CompletableFuture <BiMap <String , UUID >> lookupPlayerNamesOnline (String ... names ) {
55+ List <String > nameList = new LinkedList <>(Arrays .asList (names ));
56+ BiMap <String , UUID > ret = HashBiMap .create ();
57+ Iterator <String > iterator = nameList .iterator ();
58+ while (iterator .hasNext ()) {
59+ String n = iterator .next ();
60+ OfflinePlayer player = playerCache .get (n .toLowerCase (Locale .ENGLISH ));
61+ if (player != null ) {
62+ iterator .remove ();
63+ ret .put (n , player .getUniqueId ());
64+ }
65+ }
66+ CompletableFuture <FullHttpResponse > response = HttpClient .postJson ("https://api.mojang.com/profiles/minecraft" , Collections .emptyMap (), new Gson ().toJson (Stream .of (names ).collect (Collectors .toList ())));
67+ return response .thenApply ((r ) -> {
68+ NyaaCoreLoader .getInstance ().getLogger ().log (Level .FINER , "request name -> uuid api " + r .status ().code ());
69+ if (r .status ().code () > 299 || r .content () == null ) {
70+ return HashBiMap .<UUID , String >create ();
71+ }
72+ List <Map <String , Object >> result = new Gson ().fromJson (r .content ().toString (UTF_8 ), typeTokenListMap .getType ());
73+ return result .stream ().collect (ImmutableBiMap .toImmutableBiMap (m -> {
74+ m .get ("id" );
75+ return UUID .fromString (((String ) m .get ("id" )).replaceAll (UNDASHED , DASHED ));
76+ },
77+ m -> (String ) m .get ("name" )));
78+ }).thenApply (u -> {
79+ nameCache .putAll (u );
80+ ret .putAll (u .inverse ());
81+ return ret ;
82+ }).exceptionally ((e ) -> {
83+ NyaaCoreLoader .getInstance ().getLogger ().log (Level .INFO , "failed to request name -> uuid api" , e );
84+ return ret ;
85+ });
86+ }
87+
88+ public static CompletableFuture <String > lookupPlayerNameByUuidOnline (UUID uuid ) {
89+ String s = nameCache .get (uuid );
90+ if (s != null ) {
91+ return CompletableFuture .completedFuture (s );
92+ }
93+ CompletableFuture <FullHttpResponse > response = HttpClient .get ("https://api.mojang.com/user/profiles/" + uuid .toString ().toLowerCase ().replace ("-" , "" ) + "/names" , Collections .emptyMap ());
94+ return response .thenApply ((r ) -> {
95+ NyaaCoreLoader .getInstance ().getLogger ().log (Level .FINER , "request uuid -> name api " + r .status ().code ());
96+ if (r .status ().code () > 299 || r .content () == null ) {
97+ return null ;
98+ }
99+ List <Map <String , Object >> nameMapsList = new Gson ().fromJson (r .content ().toString (UTF_8 ), typeTokenListMap .getType ());
100+ if (nameMapsList .isEmpty ()){
101+ return null ;
102+ }
103+ nameCache .put (uuid , nameMapsList .get (nameMapsList .size () - 1 ).get ("name" ).toString ());
104+ return nameCache .get (uuid );
105+ }).exceptionally ((e ) -> {
106+ NyaaCoreLoader .getInstance ().getLogger ().log (Level .INFO , "failed to request uuid -> name api" , e );
107+ return null ;
108+ });
109+ }
110+
37111 public static class _Listener implements Listener {
38112 @ EventHandler (priority = EventPriority .MONITOR )
39113 public void onPlayerJoin (PlayerJoinEvent event ) {
0 commit comments