Add paralellism to the Fetcher framework. Seems to reduce time spent from O(N) to O(1).
This commit is contained in:
parent
666e4d0dff
commit
2b6cce2607
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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<Map<String, UUID>>
|
||||
{
|
||||
// -------------------------------------------- //
|
||||
// FIELDS
|
||||
// -------------------------------------------- //
|
||||
|
||||
private final Collection<String> playerNames;
|
||||
|
||||
// -------------------------------------------- //
|
||||
// CONSTRUCT
|
||||
// -------------------------------------------- //
|
||||
|
||||
public FetcherPlayerIdCached(Collection<String> playerNames)
|
||||
{
|
||||
this.playerNames = playerNames;
|
||||
}
|
||||
|
||||
// -------------------------------------------- //
|
||||
// OVERRIDE
|
||||
// -------------------------------------------- //
|
||||
|
||||
@Override
|
||||
public Map<String, UUID> call() throws Exception
|
||||
{
|
||||
return fetch(this.playerNames);
|
||||
}
|
||||
|
||||
// -------------------------------------------- //
|
||||
// STATIC
|
||||
// -------------------------------------------- //
|
||||
|
||||
public static Map<String, UUID> fetch(Collection<String> playerNames) throws Exception
|
||||
{
|
||||
Map<String, UUID> ret = new TreeMap<String, UUID>(String.CASE_INSENSITIVE_ORDER);
|
||||
List<String> playerNamesCopy = new ArrayList<String>(playerNames);
|
||||
|
||||
// Use Cache
|
||||
Iterator<String> 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<String, UUID> mojangApiResult = FetcherPlayerIdMojang.fetch(playerNamesCopy);
|
||||
// Add to the cache
|
||||
for (Entry<String, UUID> 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;
|
||||
}
|
||||
|
||||
}
|
106
src/com/massivecraft/mcore/fetcher/FetcherPlayerIdMojang.java
Normal file
106
src/com/massivecraft/mcore/fetcher/FetcherPlayerIdMojang.java
Normal file
@ -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<Map<String, UUID>>
|
||||
{
|
||||
// -------------------------------------------- //
|
||||
// CONSTANTS
|
||||
// -------------------------------------------- //
|
||||
|
||||
public static final int BATCH_SIZE = FetcherPlayerIdMojangSingle.MAX_PAGE_SIZE;
|
||||
public static final ExecutorService ES = Executors.newCachedThreadPool();
|
||||
|
||||
// -------------------------------------------- //
|
||||
// FIELDS
|
||||
// -------------------------------------------- //
|
||||
|
||||
private final Collection<String> playerNames;
|
||||
|
||||
// -------------------------------------------- //
|
||||
// CONSTRUCT
|
||||
// -------------------------------------------- //
|
||||
|
||||
public FetcherPlayerIdMojang(Collection<String> playerNames)
|
||||
{
|
||||
this.playerNames = playerNames;
|
||||
}
|
||||
|
||||
// -------------------------------------------- //
|
||||
// OVERRIDE
|
||||
// -------------------------------------------- //
|
||||
|
||||
@Override
|
||||
public Map<String, UUID> call() throws Exception
|
||||
{
|
||||
return fetch(this.playerNames);
|
||||
}
|
||||
|
||||
// -------------------------------------------- //
|
||||
// STATIC
|
||||
// -------------------------------------------- //
|
||||
|
||||
public static Map<String, UUID> fetch(Collection<String> playerNames) throws Exception
|
||||
{
|
||||
// Create batches
|
||||
List<List<String>> batches = new ArrayList<List<String>>();
|
||||
playerNames = new ArrayList<String>(playerNames);
|
||||
while (playerNames.size() > 0)
|
||||
{
|
||||
List<String> batch = take(playerNames, BATCH_SIZE);
|
||||
batches.add(batch);
|
||||
}
|
||||
|
||||
// Create Tasks
|
||||
final List<Callable<Map<String, UUID>>> tasks = new ArrayList<Callable<Map<String, UUID>>>();
|
||||
for (List<String> batch : batches)
|
||||
{
|
||||
tasks.add(new FetcherPlayerIdMojangSingle(batch));
|
||||
}
|
||||
|
||||
// Invoke All Tasks
|
||||
List<Future<Map<String, UUID>>> futures = ES.invokeAll(tasks);
|
||||
|
||||
// Merge Return Value
|
||||
Map<String, UUID> ret = new TreeMap<String, UUID> (String.CASE_INSENSITIVE_ORDER);
|
||||
for (Future<Map<String, UUID>> future : futures)
|
||||
{
|
||||
|
||||
ret.putAll(future.get());
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static <T> List<T> take(Collection<T> coll, int count)
|
||||
{
|
||||
List<T> ret = new ArrayList<T>();
|
||||
|
||||
Iterator<T> iter = coll.iterator();
|
||||
int i = 0;
|
||||
while (iter.hasNext() && i < count)
|
||||
{
|
||||
i++;
|
||||
ret.add(iter.next());
|
||||
iter.remove();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
@ -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<Map<String, UUID>>
|
||||
{
|
||||
// -------------------------------------------- //
|
||||
// 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<String> playerNames;
|
||||
|
||||
// -------------------------------------------- //
|
||||
// CONSTRUCT
|
||||
// -------------------------------------------- //
|
||||
|
||||
public FetcherPlayerIdMojangSingle(Collection<String> playerNames)
|
||||
{
|
||||
this.playerNames = playerNames;
|
||||
}
|
||||
|
||||
// -------------------------------------------- //
|
||||
// OVERRIDE
|
||||
// -------------------------------------------- //
|
||||
|
||||
@Override
|
||||
public Map<String, UUID> call() throws Exception
|
||||
{
|
||||
return fetch(this.playerNames);
|
||||
}
|
||||
|
||||
// -------------------------------------------- //
|
||||
// STATIC
|
||||
// -------------------------------------------- //
|
||||
|
||||
public static Map<String, UUID> fetch(Collection<String> playerNames) throws Exception
|
||||
{
|
||||
Map<String, UUID> ret = new TreeMap<String, UUID>(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<String> playerNames)
|
||||
{
|
||||
List<JSONObject> lookups = new ArrayList<JSONObject>();
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
@ -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<Map<UUID, String>>
|
||||
{
|
||||
// -------------------------------------------- //
|
||||
// FIELDS
|
||||
// -------------------------------------------- //
|
||||
|
||||
private final Collection<UUID> playerIds;
|
||||
|
||||
// -------------------------------------------- //
|
||||
// CONSTRUCT
|
||||
// -------------------------------------------- //
|
||||
|
||||
public FetcherPlayerNameCached(Collection<UUID> playerIds)
|
||||
{
|
||||
this.playerIds = playerIds;
|
||||
}
|
||||
|
||||
// -------------------------------------------- //
|
||||
// OVERRIDE
|
||||
// -------------------------------------------- //
|
||||
|
||||
@Override
|
||||
public Map<UUID, String> call() throws Exception
|
||||
{
|
||||
return fetch(this.playerIds);
|
||||
}
|
||||
|
||||
// -------------------------------------------- //
|
||||
// STATIC
|
||||
// -------------------------------------------- //
|
||||
|
||||
public static Map<UUID, String> fetch(Collection<UUID> playerIds) throws Exception
|
||||
{
|
||||
Map<UUID, String> ret = new HashMap<UUID, String>();
|
||||
List<UUID> playerIdsCopy = new ArrayList<UUID>(playerIds);
|
||||
|
||||
// Use Cache
|
||||
Iterator<UUID> 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<UUID, String> mojangApiResult = FetcherPlayerNameMojang.fetch(playerIdsCopy);
|
||||
// Add to the cache
|
||||
for (Entry<UUID, String> 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;
|
||||
}
|
||||
|
||||
}
|
@ -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<Map<UUID, String>>
|
||||
{
|
||||
// -------------------------------------------- //
|
||||
// CONSTANTS
|
||||
// -------------------------------------------- //
|
||||
|
||||
public static final ExecutorService ES = Executors.newCachedThreadPool();
|
||||
|
||||
// -------------------------------------------- //
|
||||
// FIELDS
|
||||
// -------------------------------------------- //
|
||||
|
||||
private final Collection<UUID> playerIds;
|
||||
|
||||
// -------------------------------------------- //
|
||||
// CONSTRUCT
|
||||
// -------------------------------------------- //
|
||||
|
||||
public FetcherPlayerNameMojang(Collection<UUID> playerIds)
|
||||
{
|
||||
this.playerIds = playerIds;
|
||||
}
|
||||
|
||||
// -------------------------------------------- //
|
||||
// OVERRIDE
|
||||
// -------------------------------------------- //
|
||||
|
||||
@Override
|
||||
public Map<UUID, String> call() throws Exception
|
||||
{
|
||||
return fetch(this.playerIds);
|
||||
}
|
||||
|
||||
// -------------------------------------------- //
|
||||
// STATIC
|
||||
// -------------------------------------------- //
|
||||
|
||||
public static Map<UUID, String> fetch(Collection<UUID> playerIds) throws Exception
|
||||
{
|
||||
// Create Tasks
|
||||
final List<Callable<Entry<UUID, String>>> tasks = new ArrayList<Callable<Entry<UUID, String>>>();
|
||||
for (UUID playerId : playerIds)
|
||||
{
|
||||
tasks.add(new FetcherPlayerNameMojangSingle(playerId));
|
||||
}
|
||||
|
||||
// Invoke All Tasks
|
||||
List<Future<Entry<UUID, String>>> futures = ES.invokeAll(tasks);
|
||||
|
||||
// Merge Return Value
|
||||
Map<UUID, String> ret = new HashMap<UUID, String>();
|
||||
for (Future<Entry<UUID, String>> future : futures)
|
||||
{
|
||||
Entry<UUID, String> entry = future.get();
|
||||
if (entry == null) continue;
|
||||
ret.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
@ -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<Entry<UUID, String>>
|
||||
{
|
||||
// -------------------------------------------- //
|
||||
// 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<UUID, String> call() throws Exception
|
||||
{
|
||||
String playerName = fetch(this.playerId);
|
||||
if (playerName == null) return null;
|
||||
return new SimpleEntry<UUID, String>(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;
|
||||
}
|
||||
|
||||
}
|
@ -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<String, UUID> getPlayerIds(Collection<String> playerNames) throws Exception
|
||||
{
|
||||
Map<String, UUID> ret = new HashMap<String, UUID>();
|
||||
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<String> playerNames)
|
||||
{
|
||||
List<JSONObject> lookups = new ArrayList<JSONObject>();
|
||||
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<UUID, String> getPlayerNames(Collection<UUID> playerIds) throws Exception
|
||||
{
|
||||
Map<UUID, String> ret = new HashMap<UUID, String>();
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
@ -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<UUID> playerIds = Collections.singletonList(playerId);
|
||||
Map<UUID, String> 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<String> playerNames = Collections.singletonList(playerName);
|
||||
Map<String, UUID> 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<String, UUID> ret = new TreeMap<String, UUID>(String.CASE_INSENSITIVE_ORDER);
|
||||
// ... since you achieve case insensitivity that way.
|
||||
public static Map<String, UUID> getPlayerIds(Collection<String> playerNames, final boolean usingCache, final boolean usingMojangApi, final boolean synchronous, final Runnable onComplete, Map<String, UUID> 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<String> playerNamesFinal = new ArrayList<String>(playerNames);
|
||||
final Map<String, UUID> retFinal = (ret != null ? ret : new TreeMap<String, UUID>(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<String> 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<String, UUID> mojangApiResult = MojangApiUtil.getPlayerIds(playerNamesFinal);
|
||||
// Add to the cache
|
||||
for (Entry<String, UUID> 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<String, UUID> getPlayerIds(Collection<String> playerNames, final boolean usingCache, final boolean usingMojangApi, final boolean synchronous, final Runnable onComplete)
|
||||
{
|
||||
return getPlayerIds(playerNames, usingCache, usingMojangApi, synchronous, onComplete, null);
|
||||
}
|
||||
public static Map<String, UUID> getPlayerIds(Collection<String> playerNames, final boolean usingCache, final boolean usingMojangApi, final boolean synchronous)
|
||||
{
|
||||
return getPlayerIds(playerNames, usingCache, usingMojangApi, synchronous, null);
|
||||
}
|
||||
public static Map<String, UUID> getPlayerIds(Collection<String> playerNames, final boolean usingCache, final boolean usingMojangApi)
|
||||
{
|
||||
return getPlayerIds(playerNames, usingCache, usingMojangApi, true);
|
||||
}
|
||||
public static Map<String, UUID> getPlayerIds(Collection<String> playerNames, final boolean usingCache)
|
||||
{
|
||||
return getPlayerIds(playerNames, usingCache, true);
|
||||
}
|
||||
public static Map<String, UUID> getPlayerIds(Collection<String> playerNames)
|
||||
{
|
||||
return getPlayerIds(playerNames, true);
|
||||
try
|
||||
{
|
||||
return FetcherPlayerIdCached.fetch(playerNames);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
return new TreeMap<String, UUID>(String.CASE_INSENSITIVE_ORDER);
|
||||
}
|
||||
}
|
||||
public static UUID getPlayerId(String playerName)
|
||||
{
|
||||
List<String> playerNames = Collections.singletonList(playerName);
|
||||
Map<String, UUID> map = getPlayerIds(playerNames);
|
||||
return map.get(playerName);
|
||||
}
|
||||
|
||||
public static Map<UUID, String> getPlayerNames(Collection<UUID> playerIds, final boolean usingCache, final boolean usingMojangApi, final boolean synchronous, final Runnable onComplete, Map<UUID, String> 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<UUID> playerIdsFinal = new ArrayList<UUID>(playerIds);
|
||||
final Map<UUID, String> retFinal = (ret != null ? ret : new HashMap<UUID, String>());
|
||||
|
||||
// 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<UUID> 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<UUID, String> mojangApiResult = MojangApiUtil.getPlayerNames(playerIdsFinal);
|
||||
// Add to the cache
|
||||
for (Entry<UUID, String> 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<UUID, String> getPlayerNames(Collection<UUID> playerIds, final boolean usingCache, final boolean usingMojangApi, final boolean synchronous, final Runnable onComplete)
|
||||
{
|
||||
return getPlayerNames(playerIds, usingCache, usingMojangApi, synchronous, onComplete, null);
|
||||
}
|
||||
public static Map<UUID, String> getPlayerNames(Collection<UUID> playerIds, final boolean usingCache, final boolean usingMojangApi, final boolean synchronous)
|
||||
{
|
||||
return getPlayerNames(playerIds, usingCache, usingMojangApi, synchronous, null);
|
||||
}
|
||||
public static Map<UUID, String> getPlayerNames(Collection<UUID> playerIds, final boolean usingCache, final boolean usingMojangApi)
|
||||
{
|
||||
return getPlayerNames(playerIds, usingCache, usingMojangApi, true);
|
||||
}
|
||||
public static Map<UUID, String> getPlayerNames(Collection<UUID> playerIds, final boolean usingCache)
|
||||
{
|
||||
return getPlayerNames(playerIds, usingCache, true);
|
||||
}
|
||||
public static Map<UUID, String> getPlayerNames(Collection<UUID> playerIds)
|
||||
{
|
||||
return getPlayerNames(playerIds, true);
|
||||
try
|
||||
{
|
||||
return FetcherPlayerNameCached.fetch(playerIds);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
return new HashMap<UUID, String>();
|
||||
}
|
||||
}
|
||||
public static String getPlayerName(UUID playerId)
|
||||
{
|
||||
List<UUID> playerIds = Collections.singletonList(playerId);
|
||||
Map<UUID, String> map = getPlayerNames(playerIds);
|
||||
return map.get(playerId);
|
||||
}
|
||||
|
||||
// -------------------------------------------- //
|
||||
// PLAYER ID <---> PLAYER NAME: FETCH ALL
|
||||
// -------------------------------------------- //
|
||||
|
||||
public static void fetchAllIds()
|
||||
{
|
||||
// --- Starting Information ---
|
||||
MCore.get().log(Txt.parse("<a>============================================"));
|
||||
MCore.get().log(Txt.parse("<i>We are preparing for Mojangs switch to UUIDs."));
|
||||
MCore.get().log(Txt.parse("<i>Learn more at: <aqua>https://forums.bukkit.org/threads/psa-the-switch-to-uuids-potential-plugin-server-breakage.250915/"));
|
||||
MCore.get().log(Txt.parse("<i>Now fetching and caching UUID for all player names on this server!"));
|
||||
MCore.get().log(Txt.parse("<i>The mstore collection \"<h>mcore_mplayer<i>\" will contain the cached information."));
|
||||
|
||||
// --- Find Player Names ---
|
||||
// Here we build a set containing all player names we know of!
|
||||
Set<String> playerNames = new TreeSet<String>(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<String> 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("<k>Player Names Found: <v>%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("<k>Player Names Cached: <v>%d", cached));
|
||||
MCore.get().log(Txt.parse("<k>Player Names Remaining: <v>%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("<i>Now fetching the remaining players from Mojang API ..."));
|
||||
|
||||
getPlayerIds(playerNames);
|
||||
|
||||
MCore.get().log(Txt.parse("<g> ... done!"));
|
||||
MCore.get().log(Txt.parse("<i>(database saving will now commence which might lock the server for a while)"));
|
||||
MCore.get().log(Txt.parse("<a>============================================"));
|
||||
}
|
||||
|
||||
public static <T> List<T> take(Collection<T> coll, int count)
|
||||
{
|
||||
List<T> ret = new ArrayList<T>();
|
||||
|
||||
Iterator<T> iter = coll.iterator();
|
||||
int i = 0;
|
||||
while (iter.hasNext() && i < count)
|
||||
{
|
||||
i++;
|
||||
ret.add(iter.next());
|
||||
iter.remove();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user