UUIDUpdateAsyncTask.java 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. package com.gmail.nossr50.runnables.database;
  2. import com.gmail.nossr50.datatypes.database.UpgradeType;
  3. import com.gmail.nossr50.mcMMO;
  4. import com.gmail.nossr50.util.CancellableRunnable;
  5. import com.gmail.nossr50.util.Misc;
  6. import com.google.common.collect.ImmutableList;
  7. import com.google.gson.Gson;
  8. import com.google.gson.JsonObject;
  9. import java.io.IOException;
  10. import java.io.InputStream;
  11. import java.io.InputStreamReader;
  12. import java.io.OutputStream;
  13. import java.net.HttpURLConnection;
  14. import java.net.URL;
  15. import java.util.HashMap;
  16. import java.util.List;
  17. import java.util.Map;
  18. import java.util.UUID;
  19. import java.util.concurrent.CountDownLatch;
  20. import java.util.logging.Level;
  21. public class UUIDUpdateAsyncTask extends CancellableRunnable {
  22. private static final Gson GSON = new Gson();
  23. private static final String PROFILE_URL = "https://api.mojang.com/profiles/minecraft";
  24. private static final int HARD_LIMIT_PERIOD = 600; // Mojang rate limit period is 600 seconds, wait that long if they reject us
  25. private static final int RETRY_PERIOD = 60; // Wait 60 seconds on connection failure
  26. private static final int DELAY_PERIOD = 100; // Wait 100 seconds between each batch
  27. private static final int BATCH_SIZE = 100; // 100 at a time
  28. private final CountDownLatch awaiter = new CountDownLatch(1);
  29. private final mcMMO plugin;
  30. private final ImmutableList<String> userNames;
  31. private int position = 0;
  32. public UUIDUpdateAsyncTask(mcMMO plugin, List<String> userNames) {
  33. this.plugin = plugin;
  34. this.userNames = ImmutableList.copyOf(userNames);
  35. }
  36. @Override
  37. public void run() {
  38. // First iteration
  39. if (position == 0)
  40. plugin.getLogger().info("Starting to check and update UUIDs, total amount of users: " + userNames.size());
  41. List<String> batch = userNames.subList(position, Math.min(userNames.size(), position + BATCH_SIZE));
  42. Map<String, UUID> fetchedUUIDs = new HashMap<>();
  43. HttpURLConnection connection;
  44. try {
  45. connection = (HttpURLConnection) new URL(PROFILE_URL).openConnection();
  46. connection.setRequestMethod("POST");
  47. connection.setRequestProperty("Content-Type", "application/json");
  48. connection.setUseCaches(false);
  49. connection.setDoInput(true);
  50. connection.setDoOutput(true);
  51. String body = GSON.toJson(batch.toArray(new String[0]));
  52. try (OutputStream output = connection.getOutputStream()) {
  53. output.write(body.getBytes());
  54. output.flush();
  55. }
  56. switch (connection.getResponseCode()) {
  57. case HttpURLConnection.HTTP_OK:
  58. break; // All is good
  59. case HttpURLConnection.HTTP_BAD_REQUEST:
  60. case HttpURLConnection.HTTP_FORBIDDEN:
  61. // Rejected, probably rate limit, just wait it out
  62. this.runTaskLaterAsynchronously(plugin, Misc.TICK_CONVERSION_FACTOR * HARD_LIMIT_PERIOD);
  63. return;
  64. default:
  65. // Unknown failure
  66. this.runTaskLaterAsynchronously(plugin, Misc.TICK_CONVERSION_FACTOR * RETRY_PERIOD);
  67. return;
  68. }
  69. try (InputStream input = connection.getInputStream();
  70. InputStreamReader reader = new InputStreamReader(input)) {
  71. for (JsonObject jsonProfile : GSON.fromJson(reader, JsonObject[].class)) {
  72. UUID id = toUUID(jsonProfile.get("id").getAsString());
  73. String name = jsonProfile.get("name").getAsString();
  74. fetchedUUIDs.put(name, id);
  75. }
  76. }
  77. } catch (IOException e) {
  78. // failure
  79. plugin.getLogger().log(Level.SEVERE, "Unable to contact mojang API!", e);
  80. this.runTaskLaterAsynchronously(plugin, Misc.TICK_CONVERSION_FACTOR * RETRY_PERIOD);
  81. return;
  82. }
  83. if (fetchedUUIDs.size() != 0)
  84. mcMMO.getDatabaseManager().saveUserUUIDs(fetchedUUIDs);
  85. position += batch.size();
  86. plugin.getLogger().info(String.format("Conversion progress: %d/%d users", position, userNames.size()));
  87. if (position +1 >= userNames.size()) {
  88. mcMMO.getUpgradeManager().setUpgradeCompleted(UpgradeType.ADD_UUIDS);
  89. awaiter.countDown();
  90. plugin.getLogger().info("UUID checks completed");
  91. } else
  92. this.runTaskLaterAsynchronously(plugin, Misc.TICK_CONVERSION_FACTOR * DELAY_PERIOD); // Schedule next batch
  93. }
  94. // Bukkit runnables don't let themselves reschedule themselves, so we are a pseudo bukkit runnable.
  95. private void runTaskLaterAsynchronously(mcMMO plugin, int delay) {
  96. plugin.getFoliaLib().getScheduler().runLaterAsync(this, delay);
  97. }
  98. public void start() {
  99. plugin.getFoliaLib().getScheduler().runAsync(this);
  100. }
  101. private static UUID toUUID(String id) {
  102. return UUID.fromString(id.substring(0, 8) + "-" + id.substring(8, 12) + "-" + id.substring(12, 16) + "-" + id.substring(16, 20) + "-" + id.substring(20, 32));
  103. }
  104. public void waitUntilFinished() {
  105. try {
  106. awaiter.await();
  107. } catch (InterruptedException e) {
  108. // I guess we don't care in this event
  109. }
  110. }
  111. }