diff --git a/src/com/massivecraft/mcore/MCore.java b/src/com/massivecraft/mcore/MCore.java index abb7ad4d..5deb112c 100644 --- a/src/com/massivecraft/mcore/MCore.java +++ b/src/com/massivecraft/mcore/MCore.java @@ -177,12 +177,22 @@ public class MCore extends MPlugin VaultFeatures.get() ); - //test(); - // Delete Files (at once and additionally after all plugins loaded) TaskDeleteFiles.get().run(); Bukkit.getScheduler().scheduleSyncDelayedTask(this, TaskDeleteFiles.get()); + //test(); + + // Schedule fetch all + Bukkit.getScheduler().scheduleSyncDelayedTask(this, new Runnable() + { + @Override + public void run() + { + PlayerUtil.fetchAllIds(); + } + }); + this.postEnable(); } diff --git a/src/com/massivecraft/mcore/fetcher/FetcherPlayerIdCached.java b/src/com/massivecraft/mcore/fetcher/FetcherPlayerIdCached.java new file mode 100644 index 00000000..1bd2ea64 --- /dev/null +++ b/src/com/massivecraft/mcore/fetcher/FetcherPlayerIdCached.java @@ -0,0 +1,94 @@ +package com.massivecraft.mcore.fetcher; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import java.util.UUID; +import java.util.Map.Entry; +import java.util.concurrent.Callable; + +import com.massivecraft.mcore.MCoreMPlayer; + +/** + * Many thanks to evilmidget38! + * This utility class is based on his work. + * http://forums.bukkit.org/threads/player-name-uuid-fetcher.250926/ + */ +public class FetcherPlayerIdCached implements Callable> +{ + // -------------------------------------------- // + // FIELDS + // -------------------------------------------- // + + private final Collection playerNames; + + // -------------------------------------------- // + // CONSTRUCT + // -------------------------------------------- // + + public FetcherPlayerIdCached(Collection playerNames) + { + this.playerNames = playerNames; + } + + // -------------------------------------------- // + // OVERRIDE + // -------------------------------------------- // + + @Override + public Map call() throws Exception + { + return fetch(this.playerNames); + } + + // -------------------------------------------- // + // STATIC + // -------------------------------------------- // + + public static Map fetch(Collection playerNames) throws Exception + { + Map ret = new TreeMap(String.CASE_INSENSITIVE_ORDER); + List playerNamesCopy = new ArrayList(playerNames); + + // Use Cache + Iterator iter = playerNamesCopy.iterator(); + while (iter.hasNext()) + { + String playerName = iter.next(); + MCoreMPlayer mplayer = MCoreMPlayer.get(playerName); + if (mplayer == null) continue; + ret.put(mplayer.getName(), UUID.fromString(mplayer.getId())); + iter.remove(); + } + + // Use Mojang API for the rest + if (playerNamesCopy.size() > 0) + { + try + { + Map mojangApiResult = FetcherPlayerIdMojang.fetch(playerNamesCopy); + // Add to the cache + for (Entry entry : mojangApiResult.entrySet()) + { + String name = entry.getKey(); + UUID id = entry.getValue(); + MCoreMPlayer mplayer = MCoreMPlayer.get(id, true); + mplayer.setName(name); + } + // Add to the return value + ret.putAll(mojangApiResult); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + // Return + return ret; + } + +} diff --git a/src/com/massivecraft/mcore/fetcher/FetcherPlayerIdMojang.java b/src/com/massivecraft/mcore/fetcher/FetcherPlayerIdMojang.java new file mode 100644 index 00000000..3531a3f9 --- /dev/null +++ b/src/com/massivecraft/mcore/fetcher/FetcherPlayerIdMojang.java @@ -0,0 +1,106 @@ +package com.massivecraft.mcore.fetcher; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import java.util.UUID; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +/** + * Many thanks to evilmidget38! + * This utility class is based on his work. + * http://forums.bukkit.org/threads/player-name-uuid-fetcher.250926/ + */ +public class FetcherPlayerIdMojang implements Callable> +{ + // -------------------------------------------- // + // CONSTANTS + // -------------------------------------------- // + + public static final int BATCH_SIZE = FetcherPlayerIdMojangSingle.MAX_PAGE_SIZE; + public static final ExecutorService ES = Executors.newCachedThreadPool(); + + // -------------------------------------------- // + // FIELDS + // -------------------------------------------- // + + private final Collection playerNames; + + // -------------------------------------------- // + // CONSTRUCT + // -------------------------------------------- // + + public FetcherPlayerIdMojang(Collection playerNames) + { + this.playerNames = playerNames; + } + + // -------------------------------------------- // + // OVERRIDE + // -------------------------------------------- // + + @Override + public Map call() throws Exception + { + return fetch(this.playerNames); + } + + // -------------------------------------------- // + // STATIC + // -------------------------------------------- // + + public static Map fetch(Collection playerNames) throws Exception + { + // Create batches + List> batches = new ArrayList>(); + playerNames = new ArrayList(playerNames); + while (playerNames.size() > 0) + { + List batch = take(playerNames, BATCH_SIZE); + batches.add(batch); + } + + // Create Tasks + final List>> tasks = new ArrayList>>(); + for (List batch : batches) + { + tasks.add(new FetcherPlayerIdMojangSingle(batch)); + } + + // Invoke All Tasks + List>> futures = ES.invokeAll(tasks); + + // Merge Return Value + Map ret = new TreeMap (String.CASE_INSENSITIVE_ORDER); + for (Future> future : futures) + { + + ret.putAll(future.get()); + } + + return ret; + } + + public static List take(Collection coll, int count) + { + List ret = new ArrayList(); + + Iterator iter = coll.iterator(); + int i = 0; + while (iter.hasNext() && i < count) + { + i++; + ret.add(iter.next()); + iter.remove(); + } + + return ret; + } + +} diff --git a/src/com/massivecraft/mcore/fetcher/FetcherPlayerIdMojangSingle.java b/src/com/massivecraft/mcore/fetcher/FetcherPlayerIdMojangSingle.java new file mode 100644 index 00000000..16db9863 --- /dev/null +++ b/src/com/massivecraft/mcore/fetcher/FetcherPlayerIdMojangSingle.java @@ -0,0 +1,143 @@ +package com.massivecraft.mcore.fetcher; + +import java.io.DataOutputStream; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import java.util.UUID; +import java.util.concurrent.Callable; + +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.json.simple.JSONValue; +import org.json.simple.parser.JSONParser; + +/** + * Many thanks to evilmidget38! + * This utility class is based on his work. + * http://forums.bukkit.org/threads/player-name-uuid-fetcher.250926/ + */ +public class FetcherPlayerIdMojangSingle implements Callable> +{ + // -------------------------------------------- // + // CONSTANTS + // -------------------------------------------- // + + public final static String URL_BASE = "https://api.mojang.com/profiles/page/"; + public final static int MAX_PAGES = 100; + + // The maximum amount of profiles returned per page. + // Mojang might change this value. + // Thus we can not fully depend on it. + public final static int MAX_PAGE_SIZE = 50; + + public final static String KEY_PROFILES = "profiles"; + public final static String KEY_SIZE = "size"; + public final static String KEY_ID = "id"; + public final static String KEY_NAME = "name"; + public final static String KEY_AGENT = "agent"; + public final static String VALUGE_AGENT = "minecraft"; + + // -------------------------------------------- // + // FIELDS + // -------------------------------------------- // + + private final Collection playerNames; + + // -------------------------------------------- // + // CONSTRUCT + // -------------------------------------------- // + + public FetcherPlayerIdMojangSingle(Collection playerNames) + { + this.playerNames = playerNames; + } + + // -------------------------------------------- // + // OVERRIDE + // -------------------------------------------- // + + @Override + public Map call() throws Exception + { + return fetch(this.playerNames); + } + + // -------------------------------------------- // + // STATIC + // -------------------------------------------- // + + public static Map fetch(Collection playerNames) throws Exception + { + Map ret = new TreeMap(String.CASE_INSENSITIVE_ORDER); + JSONParser jsonParser = new JSONParser(); + String body = createBody(playerNames); + for (int i = 1; i < MAX_PAGES; i++) + { + // If the return object has as many entries as player names requested we must have gotten all the info. + // This will often help us avoid the extra useless last call of a page with 0 entries. + if (ret.size() == playerNames.size()) break; + + HttpURLConnection connection = createConnection(i); + writeBody(connection, body); + JSONObject jsonObject = (JSONObject) jsonParser.parse(new InputStreamReader(connection.getInputStream())); + JSONArray profiles = (JSONArray) jsonObject.get(KEY_PROFILES); + int size = ((Number) jsonObject.get(KEY_SIZE)).intValue(); + + // If the page is empty we are done + if (size == 0) break; + + for (Object profile : profiles) + { + JSONObject jsonProfile = (JSONObject) profile; + String id = (String) jsonProfile.get(KEY_ID); + String name = (String) jsonProfile.get(KEY_NAME); + UUID uuid = UUID.fromString(id.substring(0, 8) + "-" + id.substring(8, 12) + "-" + id.substring(12, 16) + "-" + id.substring(16, 20) + "-" + id.substring(20, 32)); + ret.put(name, uuid); + } + } + return ret; + } + + private static HttpURLConnection createConnection(int page) throws Exception + { + URL url = new URL(URL_BASE + page); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod("POST"); + connection.setRequestProperty("Content-Type", "application/json"); + connection.setConnectTimeout(15000); + connection.setReadTimeout(15000); + connection.setUseCaches(false); + connection.setDoInput(true); + connection.setDoOutput(true); + return connection; + } + + @SuppressWarnings("unchecked") + private static String createBody(Collection playerNames) + { + List lookups = new ArrayList(); + for (String playerName : playerNames) + { + JSONObject obj = new JSONObject(); + obj.put(KEY_NAME, playerName); + obj.put(KEY_AGENT, VALUGE_AGENT); + lookups.add(obj); + } + return JSONValue.toJSONString(lookups); + } + + private static void writeBody(HttpURLConnection connection, String body) throws Exception + { + DataOutputStream writer = new DataOutputStream(connection.getOutputStream()); + writer.write(body.getBytes()); + writer.flush(); + writer.close(); + } + +} diff --git a/src/com/massivecraft/mcore/fetcher/FetcherPlayerNameCached.java b/src/com/massivecraft/mcore/fetcher/FetcherPlayerNameCached.java new file mode 100644 index 00000000..b7a862a6 --- /dev/null +++ b/src/com/massivecraft/mcore/fetcher/FetcherPlayerNameCached.java @@ -0,0 +1,93 @@ +package com.massivecraft.mcore.fetcher; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.Map.Entry; +import java.util.concurrent.Callable; +import com.massivecraft.mcore.MCoreMPlayer; + +/** + * Many thanks to evilmidget38! + * This utility class is based on his work. + * http://forums.bukkit.org/threads/player-name-uuid-fetcher.250926/ + */ +public class FetcherPlayerNameCached implements Callable> +{ + // -------------------------------------------- // + // FIELDS + // -------------------------------------------- // + + private final Collection playerIds; + + // -------------------------------------------- // + // CONSTRUCT + // -------------------------------------------- // + + public FetcherPlayerNameCached(Collection playerIds) + { + this.playerIds = playerIds; + } + + // -------------------------------------------- // + // OVERRIDE + // -------------------------------------------- // + + @Override + public Map call() throws Exception + { + return fetch(this.playerIds); + } + + // -------------------------------------------- // + // STATIC + // -------------------------------------------- // + + public static Map fetch(Collection playerIds) throws Exception + { + Map ret = new HashMap(); + List playerIdsCopy = new ArrayList(playerIds); + + // Use Cache + Iterator iter = playerIdsCopy.iterator(); + while (iter.hasNext()) + { + UUID playerId = iter.next(); + MCoreMPlayer mplayer = MCoreMPlayer.get(playerId); + if (mplayer == null) continue; + ret.put(playerId, mplayer.getName()); + iter.remove(); + } + + // Use Mojang API for the rest + if (playerIdsCopy.size() > 0) + { + try + { + Map mojangApiResult = FetcherPlayerNameMojang.fetch(playerIdsCopy); + // Add to the cache + for (Entry entry : mojangApiResult.entrySet()) + { + UUID id = entry.getKey(); + String name = entry.getValue(); + MCoreMPlayer mplayer = MCoreMPlayer.get(id, true); + mplayer.setName(name); + } + // Add to the return value + ret.putAll(mojangApiResult); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + // Return + return ret; + } + +} diff --git a/src/com/massivecraft/mcore/fetcher/FetcherPlayerNameMojang.java b/src/com/massivecraft/mcore/fetcher/FetcherPlayerNameMojang.java new file mode 100644 index 00000000..9d21faa9 --- /dev/null +++ b/src/com/massivecraft/mcore/fetcher/FetcherPlayerNameMojang.java @@ -0,0 +1,81 @@ +package com.massivecraft.mcore.fetcher; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.UUID; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +/** + * Many thanks to evilmidget38! + * This utility class is based on his work. + * http://forums.bukkit.org/threads/player-name-uuid-fetcher.250926/ + */ +public class FetcherPlayerNameMojang implements Callable> +{ + // -------------------------------------------- // + // CONSTANTS + // -------------------------------------------- // + + public static final ExecutorService ES = Executors.newCachedThreadPool(); + + // -------------------------------------------- // + // FIELDS + // -------------------------------------------- // + + private final Collection playerIds; + + // -------------------------------------------- // + // CONSTRUCT + // -------------------------------------------- // + + public FetcherPlayerNameMojang(Collection playerIds) + { + this.playerIds = playerIds; + } + + // -------------------------------------------- // + // OVERRIDE + // -------------------------------------------- // + + @Override + public Map call() throws Exception + { + return fetch(this.playerIds); + } + + // -------------------------------------------- // + // STATIC + // -------------------------------------------- // + + public static Map fetch(Collection playerIds) throws Exception + { + // Create Tasks + final List>> tasks = new ArrayList>>(); + for (UUID playerId : playerIds) + { + tasks.add(new FetcherPlayerNameMojangSingle(playerId)); + } + + // Invoke All Tasks + List>> futures = ES.invokeAll(tasks); + + // Merge Return Value + Map ret = new HashMap(); + for (Future> future : futures) + { + Entry entry = future.get(); + if (entry == null) continue; + ret.put(entry.getKey(), entry.getValue()); + } + + return ret; + } + +} diff --git a/src/com/massivecraft/mcore/fetcher/FetcherPlayerNameMojangSingle.java b/src/com/massivecraft/mcore/fetcher/FetcherPlayerNameMojangSingle.java new file mode 100644 index 00000000..7b9c9405 --- /dev/null +++ b/src/com/massivecraft/mcore/fetcher/FetcherPlayerNameMojangSingle.java @@ -0,0 +1,90 @@ +package com.massivecraft.mcore.fetcher; + +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.Map.Entry; +import java.util.AbstractMap.SimpleEntry; +import java.util.UUID; +import java.util.concurrent.Callable; + +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; + +/** + * Many thanks to evilmidget38! + * This utility class is based on his work. + * http://forums.bukkit.org/threads/player-name-uuid-fetcher.250926/ + */ +public class FetcherPlayerNameMojangSingle implements Callable> +{ + // -------------------------------------------- // + // CONSTANTS + // -------------------------------------------- // + + public final static String URL_BASE = "https://sessionserver.mojang.com/session/minecraft/profile/"; + public final static String KEY_NAME = "name"; + public final static String KEY_CAUSE = "cause"; + public final static String KEY_ERROR_MESSAGE = "errorMessage"; + + // -------------------------------------------- // + // FIELDS + // -------------------------------------------- // + + private final UUID playerId; + public UUID getPlayerId() { return this.playerId; } + + // -------------------------------------------- // + // CONSTRUCT + // -------------------------------------------- // + + public FetcherPlayerNameMojangSingle(UUID playerId) + { + this.playerId = playerId; + } + + // -------------------------------------------- // + // OVERRIDE + // -------------------------------------------- // + + @Override + public Entry call() throws Exception + { + String playerName = fetch(this.playerId); + if (playerName == null) return null; + return new SimpleEntry(this.playerId, playerName); + } + + // -------------------------------------------- // + // STATIC + // -------------------------------------------- // + + public static String fetch(UUID playerId) throws Exception + { + JSONParser jsonParser = new JSONParser(); + HttpURLConnection connection = createConnection(playerId); + JSONObject response = (JSONObject) jsonParser.parse(new InputStreamReader(connection.getInputStream())); + String name = (String) response.get(KEY_NAME); + if (name == null) return null; + String cause = (String) response.get(KEY_CAUSE); + if (cause != null && cause.length() > 0) + { + String errorMessage = (String) response.get(KEY_ERROR_MESSAGE); + throw new IllegalStateException(errorMessage); + } + return name; + } + + private static HttpURLConnection createConnection(UUID playerId) throws Exception + { + URL url = new URL(URL_BASE + playerId.toString().replace("-", "")); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setConnectTimeout(15000); + connection.setReadTimeout(15000); + connection.setUseCaches(false); + connection.setDoInput(true); + connection.setDoOutput(true); + return connection; + } + +} diff --git a/src/com/massivecraft/mcore/util/MojangApiUtil.java b/src/com/massivecraft/mcore/util/MojangApiUtil.java deleted file mode 100644 index 91f04b32..00000000 --- a/src/com/massivecraft/mcore/util/MojangApiUtil.java +++ /dev/null @@ -1,124 +0,0 @@ -package com.massivecraft.mcore.util; - -import java.io.DataOutputStream; -import java.io.InputStreamReader; -import java.net.HttpURLConnection; -import java.net.URL; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; - -import org.json.simple.JSONArray; -import org.json.simple.JSONObject; -import org.json.simple.JSONValue; -import org.json.simple.parser.JSONParser; - -/** - * Many thanks to evilmidget38! - * This utility class is based on his work. - * http://forums.bukkit.org/threads/player-name-uuid-fetcher.250926/ - */ -public class MojangApiUtil -{ - // -------------------------------------------- // - // NAME --> ID - // -------------------------------------------- // - // The player names you supply does not have to use correct capitalization. - // In the map returned however, the names will have correction capitalization. - - public static Map getPlayerIds(Collection playerNames) throws Exception - { - Map ret = new HashMap(); - JSONParser jsonParser = new JSONParser(); - String body = createBody(playerNames); - for (int i = 1; i < 100; i++) - { - HttpURLConnection connection = createConnection(i); - writeBody(connection, body); - JSONObject jsonObject = (JSONObject) jsonParser.parse(new InputStreamReader(connection.getInputStream())); - JSONArray profiles = (JSONArray) jsonObject.get("profiles"); - Number count = (Number) jsonObject.get("size"); - - if (count.intValue() == 0) - { - break; - } - - for (Object profile : profiles) - { - JSONObject jsonProfile = (JSONObject) profile; - String id = (String) jsonProfile.get("id"); - String name = (String) jsonProfile.get("name"); - UUID uuid = UUID.fromString(id.substring(0, 8) + "-" + id.substring(8, 12) + "-" + id.substring(12, 16) + "-" + id.substring(16, 20) + "-" + id.substring(20, 32)); - ret.put(name, uuid); - } - } - return ret; - } - - private static void writeBody(HttpURLConnection connection, String body) throws Exception - { - DataOutputStream writer = new DataOutputStream(connection.getOutputStream()); - writer.write(body.getBytes()); - writer.flush(); - writer.close(); - } - - private static HttpURLConnection createConnection(int page) throws Exception - { - URL url = new URL("https://api.mojang.com/profiles/page/" + page); - HttpURLConnection connection = (HttpURLConnection) url.openConnection(); - connection.setRequestMethod("POST"); - connection.setRequestProperty("Content-Type", "application/json"); - connection.setUseCaches(false); - connection.setDoInput(true); - connection.setDoOutput(true); - return connection; - } - - @SuppressWarnings("unchecked") - private static String createBody(Collection playerNames) - { - List lookups = new ArrayList(); - for (String playerName : playerNames) - { - JSONObject obj = new JSONObject(); - obj.put("name", playerName); - obj.put("agent", "minecraft"); - lookups.add(obj); - } - return JSONValue.toJSONString(lookups); - } - - // -------------------------------------------- // - // ID --> NAME - // -------------------------------------------- // - - public static Map getPlayerNames(Collection playerIds) throws Exception - { - Map ret = new HashMap(); - JSONParser jsonParser = new JSONParser(); - for (UUID playerId: playerIds) - { - HttpURLConnection connection = (HttpURLConnection) new URL("https://sessionserver.mojang.com/session/minecraft/profile/" + playerId.toString().replace("-", "")).openConnection(); - JSONObject response = (JSONObject) jsonParser.parse(new InputStreamReader(connection.getInputStream())); - String name = (String) response.get("name"); - if (name == null) - { - continue; - } - String cause = (String) response.get("cause"); - if (cause != null && cause.length() > 0) - { - String errorMessage = (String) response.get("errorMessage"); - throw new IllegalStateException(errorMessage); - } - ret.put(playerId, name); - } - return ret; - } - -} diff --git a/src/com/massivecraft/mcore/util/PlayerUtil.java b/src/com/massivecraft/mcore/util/PlayerUtil.java index e07299c0..dccb20a1 100644 --- a/src/com/massivecraft/mcore/util/PlayerUtil.java +++ b/src/com/massivecraft/mcore/util/PlayerUtil.java @@ -7,9 +7,9 @@ import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import java.util.Set; import java.util.TreeMap; +import java.util.TreeSet; import java.util.UUID; import java.util.concurrent.ConcurrentSkipListSet; @@ -29,6 +29,11 @@ import org.bukkit.event.player.PlayerQuitEvent; import com.massivecraft.mcore.MCore; import com.massivecraft.mcore.MCoreMPlayer; +import com.massivecraft.mcore.fetcher.FetcherPlayerIdCached; +import com.massivecraft.mcore.fetcher.FetcherPlayerNameCached; +import com.massivecraft.mcore.mixin.Mixin; +import com.massivecraft.mcore.store.Coll; +import com.massivecraft.mcore.store.SenderColl; public class PlayerUtil implements Listener { @@ -152,42 +157,7 @@ public class PlayerUtil implements Listener // PLAYER ID <---> PLAYER NAME // -------------------------------------------- // - // getPlayerName - - public static String getPlayerName(final UUID playerId, final boolean usingCache, final boolean usingMojangApi) - { - List playerIds = Collections.singletonList(playerId); - Map map = getPlayerNames(playerIds, usingCache, usingMojangApi); - return map.get(playerId); - } - public static String getPlayerName(final UUID playerId, final boolean usingCache) - { - return getPlayerName(playerId, usingCache, true); - } - public static String getPlayerName(final UUID playerId) - { - return getPlayerName(playerId, true); - } - - // getPlayerId - - public static UUID getPlayerId(final String playerName, final boolean usingCache, final boolean usingMojangApi) - { - List playerNames = Collections.singletonList(playerName); - Map map = getPlayerIds(playerNames, usingCache, usingMojangApi); - return map.get(playerName); - } - public static UUID getPlayerId(final String playerName, final boolean usingCache) - { - return getPlayerId(playerName, usingCache, true); - } - public static UUID getPlayerId(final String playerName) - { - return getPlayerId(playerName, true); - } - // Update Cache on Login - @EventHandler(priority = EventPriority.LOWEST) public void playerIdPlayerName(PlayerLoginEvent event) { @@ -197,191 +167,127 @@ public class PlayerUtil implements Listener mplayer.setName(playerName); } - // Core Methods - - // I suggest using ... - // final Map ret = new TreeMap(String.CASE_INSENSITIVE_ORDER); - // ... since you achieve case insensitivity that way. - public static Map getPlayerIds(Collection playerNames, final boolean usingCache, final boolean usingMojangApi, final boolean synchronous, final Runnable onComplete, Map ret) - { - // Finalize Args - // To run the Async task we need final versions of all arguments. - // We shallow copy the array for the sake of concurrency and that we will want to remove those we could handle from cache in order to avoid contacting the mojang api in vain. - // We need a return value map. Create one if null. Here we do however not shallow copy. We aim to edit the supplied map instance so that it can be used inside the onComplete Runnable. - final List playerNamesFinal = new ArrayList(playerNames); - final Map retFinal = (ret != null ? ret : new TreeMap(String.CASE_INSENSITIVE_ORDER)); - - // Handle Async - // Just run sync from another thread. - if (!synchronous) - { - Bukkit.getScheduler().runTaskAsynchronously(MCore.get(), new Runnable() - { - @Override - public void run() - { - PlayerUtil.getPlayerIds(playerNamesFinal, usingCache, usingMojangApi, true, onComplete, retFinal); - } - }); - return retFinal; - } - - // Handle Cache - if (usingCache) - { - Iterator iter = playerNamesFinal.iterator(); - while (iter.hasNext()) - { - String playerName = iter.next(); - MCoreMPlayer mplayer = MCoreMPlayer.get(playerName); - if (mplayer == null) continue; - retFinal.put(mplayer.getName(), UUID.fromString(mplayer.getId())); - iter.remove(); - } - } - - // Handle Mojang Api - if (usingMojangApi && playerNamesFinal.size() > 0) - { - try - { - Map mojangApiResult = MojangApiUtil.getPlayerIds(playerNamesFinal); - // Add to the cache - for (Entry entry : mojangApiResult.entrySet()) - { - String name = entry.getKey(); - UUID id = entry.getValue(); - MCoreMPlayer mplayer = MCoreMPlayer.get(id, true); - mplayer.setName(name); - } - // Add to the return value - retFinal.putAll(mojangApiResult); - } - catch (Exception e) - { - e.printStackTrace(); - } - } - - // Run the onComplete task. - if (onComplete != null) - { - onComplete.run(); - } - - // Return - return retFinal; - } - public static Map getPlayerIds(Collection playerNames, final boolean usingCache, final boolean usingMojangApi, final boolean synchronous, final Runnable onComplete) - { - return getPlayerIds(playerNames, usingCache, usingMojangApi, synchronous, onComplete, null); - } - public static Map getPlayerIds(Collection playerNames, final boolean usingCache, final boolean usingMojangApi, final boolean synchronous) - { - return getPlayerIds(playerNames, usingCache, usingMojangApi, synchronous, null); - } - public static Map getPlayerIds(Collection playerNames, final boolean usingCache, final boolean usingMojangApi) - { - return getPlayerIds(playerNames, usingCache, usingMojangApi, true); - } - public static Map getPlayerIds(Collection playerNames, final boolean usingCache) - { - return getPlayerIds(playerNames, usingCache, true); - } public static Map getPlayerIds(Collection playerNames) { - return getPlayerIds(playerNames, true); + try + { + return FetcherPlayerIdCached.fetch(playerNames); + } + catch (Exception e) + { + e.printStackTrace(); + return new TreeMap(String.CASE_INSENSITIVE_ORDER); + } + } + public static UUID getPlayerId(String playerName) + { + List playerNames = Collections.singletonList(playerName); + Map map = getPlayerIds(playerNames); + return map.get(playerName); } - public static Map getPlayerNames(Collection playerIds, final boolean usingCache, final boolean usingMojangApi, final boolean synchronous, final Runnable onComplete, Map ret) - { - // Finalize Args - // To run the Async task we need final versions of all arguments. - // We shallow copy the array for the sake of concurrency and that we will want to remove those we could handle from cache in order to avoid contacting the mojang api in vain. - // We need a return value map. Create one if null. Here we do however not shallow copy. We aim to edit the supplied map instance so that it can be used inside the onComplete Runnable. - final List playerIdsFinal = new ArrayList(playerIds); - final Map retFinal = (ret != null ? ret : new HashMap()); - - // Handle Async - // Just run sync from another thread. - if (!synchronous) - { - Bukkit.getScheduler().runTaskAsynchronously(MCore.get(), new Runnable() - { - @Override - public void run() - { - PlayerUtil.getPlayerNames(playerIdsFinal, usingCache, usingMojangApi, true, onComplete, retFinal); - } - }); - return retFinal; - } - - // Handle Cache - if (usingCache) - { - Iterator iter = playerIdsFinal.iterator(); - while (iter.hasNext()) - { - UUID playerId = iter.next(); - MCoreMPlayer mplayer = MCoreMPlayer.get(playerId); - if (mplayer == null) continue; - retFinal.put(playerId, mplayer.getName()); - iter.remove(); - } - } - - // Handle Mojang Api - if (usingMojangApi && playerIdsFinal.size() > 0) - { - try - { - Map mojangApiResult = MojangApiUtil.getPlayerNames(playerIdsFinal); - // Add to the cache - for (Entry entry : mojangApiResult.entrySet()) - { - UUID id = entry.getKey(); - String name = entry.getValue(); - MCoreMPlayer mplayer = MCoreMPlayer.get(id, true); - mplayer.setName(name); - } - // Add to the return value - retFinal.putAll(mojangApiResult); - } - catch (Exception e) - { - e.printStackTrace(); - } - } - - // Run the onComplete task. - if (onComplete != null) - { - onComplete.run(); - } - - // Return - return retFinal; - } - public static Map getPlayerNames(Collection playerIds, final boolean usingCache, final boolean usingMojangApi, final boolean synchronous, final Runnable onComplete) - { - return getPlayerNames(playerIds, usingCache, usingMojangApi, synchronous, onComplete, null); - } - public static Map getPlayerNames(Collection playerIds, final boolean usingCache, final boolean usingMojangApi, final boolean synchronous) - { - return getPlayerNames(playerIds, usingCache, usingMojangApi, synchronous, null); - } - public static Map getPlayerNames(Collection playerIds, final boolean usingCache, final boolean usingMojangApi) - { - return getPlayerNames(playerIds, usingCache, usingMojangApi, true); - } - public static Map getPlayerNames(Collection playerIds, final boolean usingCache) - { - return getPlayerNames(playerIds, usingCache, true); - } public static Map getPlayerNames(Collection playerIds) { - return getPlayerNames(playerIds, true); + try + { + return FetcherPlayerNameCached.fetch(playerIds); + } + catch (Exception e) + { + e.printStackTrace(); + return new HashMap(); + } + } + public static String getPlayerName(UUID playerId) + { + List playerIds = Collections.singletonList(playerId); + Map map = getPlayerNames(playerIds); + return map.get(playerId); + } + + // -------------------------------------------- // + // PLAYER ID <---> PLAYER NAME: FETCH ALL + // -------------------------------------------- // + + public static void fetchAllIds() + { + // --- Starting Information --- + MCore.get().log(Txt.parse("============================================")); + MCore.get().log(Txt.parse("We are preparing for Mojangs switch to UUIDs.")); + MCore.get().log(Txt.parse("Learn more at: https://forums.bukkit.org/threads/psa-the-switch-to-uuids-potential-plugin-server-breakage.250915/")); + MCore.get().log(Txt.parse("Now fetching and caching UUID for all player names on this server!")); + MCore.get().log(Txt.parse("The mstore collection \"mcore_mplayer\" will contain the cached information.")); + + // --- Find Player Names --- + // Here we build a set containing all player names we know of! + Set playerNames = new TreeSet(String.CASE_INSENSITIVE_ORDER); + + // All from mixin + playerNames.addAll(Mixin.getAllPlayerIds()); + + // All from sender colls + for (Coll coll : Coll.getInstances()) + { + if (!(coll instanceof SenderColl)) continue; + playerNames.addAll(coll.getIds()); + } + + // Only valid player names + Iterator iter = playerNames.iterator(); + while (iter.hasNext()) + { + String playerName =iter.next(); + if (MUtil.isValidPlayerName(playerName)) continue; + iter.remove(); + } + + // Report: Player Names Found + MCore.get().log(Txt.parse("Player Names Found: %d", playerNames.size())); + + // --- Remove Cached --- + // Here we remove what we already have cached. + iter = playerNames.iterator(); + int cached = 0; + while (iter.hasNext()) + { + String playerName = iter.next(); + MCoreMPlayer mplayer = MCoreMPlayer.get(playerName); + if (mplayer == null) continue; + if (mplayer.getName() == null) continue; + cached++; + iter.remove(); + } + MCore.get().log(Txt.parse("Player Names Cached: %d", cached)); + MCore.get().log(Txt.parse("Player Names Remaining: %d", playerNames.size())); + + // --- Fetch --- + // Here we fetch the remaining player names. + // We fetch them through the cached fetcher. + // This way we will use the mojang fetcher but also cache the result for the future. + + MCore.get().log(Txt.parse("Now fetching the remaining players from Mojang API ...")); + + getPlayerIds(playerNames); + + MCore.get().log(Txt.parse(" ... done!")); + MCore.get().log(Txt.parse("(database saving will now commence which might lock the server for a while)")); + MCore.get().log(Txt.parse("============================================")); + } + + public static List take(Collection coll, int count) + { + List ret = new ArrayList(); + + Iterator iter = coll.iterator(); + int i = 0; + while (iter.hasNext() && i < count) + { + i++; + ret.add(iter.next()); + iter.remove(); + } + + return ret; } }