diff --git a/pom.xml b/pom.xml index 0863a9b..f518b72 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ tsp.headdb HeadDB - 4.1.0 + 4.2.0 jar HeadDB diff --git a/src/main/java/tsp/headdb/implementation/HeadDatabase.java b/src/main/java/tsp/headdb/implementation/HeadDatabase.java index 1323329..488d3c8 100644 --- a/src/main/java/tsp/headdb/implementation/HeadDatabase.java +++ b/src/main/java/tsp/headdb/implementation/HeadDatabase.java @@ -7,6 +7,7 @@ import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import org.json.simple.parser.ParseException; +import tsp.headdb.HeadDB; import tsp.headdb.api.event.DatabaseUpdateEvent; import tsp.headdb.util.Log; import tsp.headdb.util.Utils; @@ -25,7 +26,6 @@ import java.util.Map; import java.util.UUID; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; -import java.util.stream.Collectors; import javax.annotation.Nonnull; /** @@ -40,6 +40,7 @@ public class HeadDatabase { private long refresh; private int timeout; private long updated; + private int nextId; // Internal only public HeadDatabase(JavaPlugin plugin) { this.plugin = plugin; @@ -142,52 +143,26 @@ public class HeadDatabase { Map> result = new HashMap<>(); Category[] categories = Category.cache; - int id = 1; for (Category category : categories) { Log.debug("Caching heads from: " + category.getName()); - long start = System.currentTimeMillis(); List heads = new ArrayList<>(); try { - String line; - StringBuilder response = new StringBuilder(); - - URLConnection connection = new URL("https://minecraft-heads.com/scripts/api.php?cat=" + category.getName() + "&tags=true").openConnection(); - connection.setConnectTimeout(timeout); - connection.setRequestProperty("User-Agent", plugin.getName() + "-DatabaseUpdater"); - try (BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()))) { - while ((line = in.readLine()) != null) { - response.append(line); - } - } - JSONParser parser = new JSONParser(); - JSONArray array = (JSONArray) parser.parse(response.toString()); - for (Object o : array) { - JSONObject obj = (JSONObject) o; - String rawUUID = obj.get("uuid").toString(); - - UUID uuid; - if (Utils.validateUniqueId(rawUUID)) { - uuid = UUID.fromString(rawUUID); - } else { - uuid = UUID.randomUUID(); - } - - Head head = new Head(id) - .name(obj.get("name").toString()) - .uniqueId(uuid) - .value(obj.get("value").toString()) - .tags(obj.get("tags") != null ? obj.get("tags").toString() : "None") - .category(category); - - id++; - heads.add(head); - } - - long elapsed = (System.currentTimeMillis() - start); - Log.debug(category.getName() + " -> Done! Time: " + elapsed + "ms (" + TimeUnit.MILLISECONDS.toSeconds(elapsed) + "s)"); + // First the original api is fetched + heads = gather("https://minecraft-heads.com/scripts/api.php?cat=" + category.getName() + "&tags=true", category); } catch (ParseException | IOException e) { - Log.error("[" + plugin.getName() + "] Failed to fetch heads (no-cache) | Stack Trace:"); - e.printStackTrace(); + Log.debug("[" + plugin.getName() + "] Failed to fetch heads (no-cache) from category " + category.getName() + " | Stack Trace:"); + Log.debug(e); + Log.warning("Failed to fetch heads for " + category.getName()); + if (HeadDB.getInstance().getConfig().getBoolean("fallback", true)) { + Log.info("Attempting fallback provider for: " + category.getName()); + try { + // If the original fails and fallback is enabled, fetch from static archive + heads = gather("https://heads.pages.dev/archive/" + category.getName() + ".json", category); + } catch (IOException | ParseException ex) { + Log.error("Failed to fetch heads for " + category.getName() + "! (OF)"); // OF = Original-Fallback, both failed + ex.printStackTrace(); + } + } } updated = System.nanoTime(); @@ -198,6 +173,71 @@ public class HeadDatabase { }); } + /** + * Fetches and gathers the heads from the url. + * For internal use only! + * + * @param url The url + * @param category The category of the heads + * @return List of heads for that category + * @throws IOException error + * @throws ParseException error + */ + private List gather(String url, Category category) throws IOException, ParseException { + long start = System.currentTimeMillis(); + List heads = new ArrayList<>(); + JSONParser parser = new JSONParser(); + JSONArray array = (JSONArray) parser.parse(fetch(url)); + for (Object o : array) { + JSONObject obj = (JSONObject) o; + String rawUUID = obj.get("uuid").toString(); + + UUID uuid; + if (Utils.validateUniqueId(rawUUID)) { + uuid = UUID.fromString(rawUUID); + } else { + uuid = UUID.randomUUID(); + } + + Head head = new Head(nextId) + .name(obj.get("name").toString()) + .uniqueId(uuid) + .value(obj.get("value").toString()) + .tags(obj.get("tags") != null ? obj.get("tags").toString() : "None") + .category(category); + + nextId++; + heads.add(head); + } + + long elapsed = (System.currentTimeMillis() - start); + Log.debug(category.getName() + " -> Done! Time: " + elapsed + "ms (" + TimeUnit.MILLISECONDS.toSeconds(elapsed) + "s)"); + return heads; + } + + /** + * Fetches heads from the url. + * For internal use only! + * + * @param url The url + * @return JSON-string response + * @throws IOException error + */ + private String fetch(String url) throws IOException { + String line; + StringBuilder response = new StringBuilder(); + + URLConnection connection = new URL(url).openConnection(); + connection.setConnectTimeout(timeout); + connection.setRequestProperty("User-Agent", plugin.getName() + "-DatabaseUpdater"); + BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream())); + while ((line = in.readLine()) != null) { + response.append(line); + } + + return response.toString(); + } + public void update(Consumer>> result) { Bukkit.getScheduler().runTaskAsynchronously(plugin, task -> getHeadsNoCache(heads -> { if (heads == null) { diff --git a/src/main/java/tsp/headdb/util/Log.java b/src/main/java/tsp/headdb/util/Log.java index ce1185b..76464a1 100644 --- a/src/main/java/tsp/headdb/util/Log.java +++ b/src/main/java/tsp/headdb/util/Log.java @@ -27,13 +27,17 @@ public class Log { } public static void error(Throwable ex) { - log(ex); + log(LogLevel.ERROR, ex); } public static void debug(String message) { log(LogLevel.DEBUG, message); } + public static void debug(Throwable ex) { + log(LogLevel.DEBUG, ex); + } + public static void log(LogLevel level, String message) { if (level == LogLevel.DEBUG && !HeadDB.getInstance().getConfig().getBoolean("debug")) { return; @@ -41,8 +45,11 @@ public class Log { Bukkit.getConsoleSender().sendMessage(Utils.colorize("&7[&9&l" + name + "&7] " + level.getColor() + "[" + level.name() + "]: " + message)); } - public static void log(Throwable ex) { - Bukkit.getConsoleSender().sendMessage(Utils.colorize("&7[" + name + "&7] " + "&4&l[EXCEPTION]: " + ex.getMessage())); + public static void log(LogLevel level, Throwable ex) { + if (level == LogLevel.DEBUG && !HeadDB.getInstance().getConfig().getBoolean("debug")) { + return; + } + Bukkit.getConsoleSender().sendMessage(Utils.colorize("&7[" + name + "&7] " + level.getColor() + "[" + level.name() + "]: " + "&4&l[EXCEPTION]: " + ex.getMessage())); Bukkit.getConsoleSender().sendMessage(Utils.colorize("&4&l[StackTrace]: " + getStackTrace(ex))); } diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 568a59a..a97e5cd 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -124,5 +124,9 @@ ui: volume: 1 pitch: 1 +# If the original fetching fails and this is enabled, +# the plugin will attempt to fetch the heads from an archive. +fallback: true + # Debug Mode debug: false \ No newline at end of file