From 32f207aef707f96d659ef6c79feecf099f53b741 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Fri, 7 Jun 2024 18:06:34 +0300 Subject: [PATCH 1/5] Fix timer scheduling for SkyblockTime --- src/main/java/de/hysky/skyblocker/utils/SkyblockTime.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/de/hysky/skyblocker/utils/SkyblockTime.java b/src/main/java/de/hysky/skyblocker/utils/SkyblockTime.java index 045ecc4e65..8531ff197a 100644 --- a/src/main/java/de/hysky/skyblocker/utils/SkyblockTime.java +++ b/src/main/java/de/hysky/skyblocker/utils/SkyblockTime.java @@ -21,7 +21,7 @@ private SkyblockTime() { public static void init() { updateTime(); //ScheduleCyclic already runs the task upon scheduling, so there's no need to call updateTime() here - Scheduler.INSTANCE.schedule(() -> Scheduler.INSTANCE.scheduleCyclic(SkyblockTime::updateTime, 1200 * 24), (int) (1200000 - (getSkyblockMillis() % 1200000)) / 50); + Scheduler.INSTANCE.schedule(() -> Scheduler.INSTANCE.scheduleCyclic(SkyblockTime::updateTime, 1200 * 20), (int) (1200000 - (getSkyblockMillis() % 1200000)) / 50); } private static long getSkyblockMillis() { From faf902eb197c3030683c9bb7a965da7690a43d66 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Fri, 7 Jun 2024 18:19:37 +0300 Subject: [PATCH 2/5] Change mayor ticking to calculate the time of the next election and schedule for that --- .../hysky/skyblocker/utils/SkyblockTime.java | 2 +- .../java/de/hysky/skyblocker/utils/Utils.java | 28 +++++++++++-------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/main/java/de/hysky/skyblocker/utils/SkyblockTime.java b/src/main/java/de/hysky/skyblocker/utils/SkyblockTime.java index 8531ff197a..ec3effafe9 100644 --- a/src/main/java/de/hysky/skyblocker/utils/SkyblockTime.java +++ b/src/main/java/de/hysky/skyblocker/utils/SkyblockTime.java @@ -24,7 +24,7 @@ public static void init() { Scheduler.INSTANCE.schedule(() -> Scheduler.INSTANCE.scheduleCyclic(SkyblockTime::updateTime, 1200 * 20), (int) (1200000 - (getSkyblockMillis() % 1200000)) / 50); } - private static long getSkyblockMillis() { + public static long getSkyblockMillis() { return System.currentTimeMillis() - SKYBLOCK_EPOCH; } diff --git a/src/main/java/de/hysky/skyblocker/utils/Utils.java b/src/main/java/de/hysky/skyblocker/utils/Utils.java index 7ee5ec38f9..fc5909e858 100644 --- a/src/main/java/de/hysky/skyblocker/utils/Utils.java +++ b/src/main/java/de/hysky/skyblocker/utils/Utils.java @@ -3,7 +3,6 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParser; import com.mojang.util.UndashedUuid; - import de.hysky.skyblocker.events.SkyblockEvents; import de.hysky.skyblocker.mixins.accessors.MessageHandlerAccessor; import de.hysky.skyblocker.skyblock.item.MuseumItemCache; @@ -77,7 +76,7 @@ public class Utils { private static boolean canSendLocRaw = false; //This is required to prevent the location change event from being fired twice. private static boolean locationChanged = true; - + private static boolean mayorTickScheduled = false; private static String mayor = ""; /** @@ -191,11 +190,16 @@ public static String getMayor() { } public static void init() { - SkyblockEvents.JOIN.register(() -> tickMayorCache(false)); + SkyblockEvents.JOIN.register(() -> { + long millisUntilNextYear = 446400000L - (SkyblockTime.getSkyblockMillis() % 446400000L); + if (!mayorTickScheduled) { + Scheduler.INSTANCE.scheduleCyclic(Utils::tickMayorCache, (int) millisUntilNextYear / 50 + 1); + mayorTickScheduled = true; + } + }); ClientPlayConnectionEvents.JOIN.register(Utils::onClientWorldJoin); ClientReceiveMessageEvents.ALLOW_GAME.register(Utils::onChatMessage); ClientReceiveMessageEvents.GAME_CANCELED.register(Utils::onChatMessage); // Somehow this works even though onChatMessage returns a boolean - Scheduler.INSTANCE.scheduleCyclic(() -> tickMayorCache(true), 24_000, true); // Update every 20 minutes } /** @@ -358,8 +362,8 @@ private static void updateScoreboard(MinecraftClient client) { // TODO: Combine with `ChocolateFactorySolver.formatTime` and move into `SkyblockTime`. public static Text getDurationText(int timeInSeconds) { int seconds = timeInSeconds % 60; - int minutes = (timeInSeconds/60) % 60; - int hours = (timeInSeconds/3600); + int minutes = (timeInSeconds / 60) % 60; + int hours = (timeInSeconds / 3600); MutableText time = Text.empty(); if (hours > 0) { @@ -479,9 +483,7 @@ private static void resetLocRawInfo() { location = Location.UNKNOWN; } - private static void tickMayorCache(boolean refresh) { - if (!mayor.isEmpty() && !refresh) return; - + private static void tickMayorCache() { CompletableFuture.supplyAsync(() -> { try { JsonObject json = JsonParser.parseString(Http.sendGetRequest("https://api.hypixel.net/v2/resources/skyblock/election")).getAsJsonObject(); @@ -492,14 +494,16 @@ private static void tickMayorCache(boolean refresh) { } return ""; }).thenAccept(s -> { - if (!s.isEmpty()) mayor = s; + if (!s.isEmpty()) { + mayor = s; + LOGGER.info("[Skyblocker] Mayor set to {}", mayor); + } }); - } /** * Used to avoid triggering things like chat rules or chat listeners infinitely, do not use otherwise. - * + *

* Bypasses MessageHandler#onGameMessage */ public static void sendMessageToBypassEvents(Text message) { From 1d5752ed624184cf226a495dc441719cc7716d94 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Wed, 12 Jun 2024 01:03:01 +0300 Subject: [PATCH 3/5] Fix scheduled time being early spring 1st rather than late spring 27th Also adds more robust handling of get request failures --- .../java/de/hysky/skyblocker/utils/Http.java | 2 +- .../java/de/hysky/skyblocker/utils/Utils.java | 45 ++++++++++++++----- 2 files changed, 34 insertions(+), 13 deletions(-) diff --git a/src/main/java/de/hysky/skyblocker/utils/Http.java b/src/main/java/de/hysky/skyblocker/utils/Http.java index 1adf75d37e..043ab97fa1 100644 --- a/src/main/java/de/hysky/skyblocker/utils/Http.java +++ b/src/main/java/de/hysky/skyblocker/utils/Http.java @@ -33,7 +33,7 @@ public class Http { .followRedirects(Redirect.NORMAL) .build(); - private static ApiResponse sendCacheableGetRequest(String url, @Nullable String token) throws IOException, InterruptedException { + public static ApiResponse sendCacheableGetRequest(String url, @Nullable String token) throws IOException, InterruptedException { HttpRequest.Builder requestBuilder = HttpRequest.newBuilder() .GET() .header("Accept", "application/json") diff --git a/src/main/java/de/hysky/skyblocker/utils/Utils.java b/src/main/java/de/hysky/skyblocker/utils/Utils.java index fc5909e858..4ec951b745 100644 --- a/src/main/java/de/hysky/skyblocker/utils/Utils.java +++ b/src/main/java/de/hysky/skyblocker/utils/Utils.java @@ -21,11 +21,11 @@ import net.minecraft.text.MutableText; import net.minecraft.text.Text; import net.minecraft.util.Formatting; +import org.apache.http.client.HttpResponseException; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.IOException; import java.time.Instant; import java.util.Collections; import java.util.List; @@ -77,6 +77,7 @@ public class Utils { //This is required to prevent the location change event from being fired twice. private static boolean locationChanged = true; private static boolean mayorTickScheduled = false; + private static int mayorTickRetryAttempts = 0; private static String mayor = ""; /** @@ -191,9 +192,9 @@ public static String getMayor() { public static void init() { SkyblockEvents.JOIN.register(() -> { - long millisUntilNextYear = 446400000L - (SkyblockTime.getSkyblockMillis() % 446400000L); if (!mayorTickScheduled) { - Scheduler.INSTANCE.scheduleCyclic(Utils::tickMayorCache, (int) millisUntilNextYear / 50 + 1); + tickMayorCache(); + scheduleMayorTick(); mayorTickScheduled = true; } }); @@ -483,20 +484,40 @@ private static void resetLocRawInfo() { location = Location.UNKNOWN; } + private static void scheduleMayorTick() { + long currentYearMillis = SkyblockTime.getSkyblockMillis() % 446400000L; //446400000ms is 1 year, 105600000ms is the amount of time from early spring 1st to late spring 27th + // If current time is past late spring 27th, the next mayor change is at next year's spring 27th, otherwise it's at this year's spring 27th + long millisUntilNextMayorChange = currentYearMillis > 105600000L ? 446400000L - currentYearMillis + 105600000L : 105600000L - currentYearMillis; + Scheduler.INSTANCE.schedule(Utils::tickMayorCache, (int) (millisUntilNextMayorChange / 50) + 5 * 60 * 20); // 5 extra minutes to allow the cache to expire. This is a simpler than checking age and subtracting from max age and rescheduling again. + } + private static void tickMayorCache() { CompletableFuture.supplyAsync(() -> { try { - JsonObject json = JsonParser.parseString(Http.sendGetRequest("https://api.hypixel.net/v2/resources/skyblock/election")).getAsJsonObject(); - if (json.get("success").getAsBoolean()) return json.get("mayor").getAsJsonObject().get("name").getAsString(); - throw new IOException(json.get("cause").getAsString()); + Http.ApiResponse response = Http.sendCacheableGetRequest("https://api.hypixel.net/v2/resources/skyblock/election"); + if (!response.ok()) throw new HttpResponseException(response.statusCode(), response.content()); + JsonObject json = JsonParser.parseString(response.content()).getAsJsonObject(); + if (!json.get("success").getAsBoolean()) throw new RuntimeException("Request failed!"); //Can't find a more appropriate exception to throw here. + return json.get("mayor").getAsJsonObject().get("name").getAsString(); } catch (Exception e) { - LOGGER.error("[Skyblocker] Failed to get mayor status!", e); + throw new RuntimeException(e); //Wrap the exception to be handled by the exceptionally block + } + }).exceptionally(throwable -> { + LOGGER.error("[Skyblocker] Failed to get mayor status!", throwable.getCause()); + if (mayorTickRetryAttempts < 5) { + int minutes = 5 * (mayorTickRetryAttempts == 0 ? 1 : 2 << mayorTickRetryAttempts - 1); //5, 10, 20, 40, 80 minutes + mayorTickRetryAttempts++; + LOGGER.warn("[Skyblocker] Retrying in {} minutes.", minutes); + Scheduler.INSTANCE.schedule(Utils::tickMayorCache, minutes * 60 * 20); + } else { + LOGGER.warn("[Skyblocker] Failed to get mayor status after 5 retries! Stopping further retries until next reboot."); } - return ""; - }).thenAccept(s -> { - if (!s.isEmpty()) { - mayor = s; - LOGGER.info("[Skyblocker] Mayor set to {}", mayor); + return ""; //Have to return a value for the thenAccept block. + }).thenAccept(result -> { + if (!result.isEmpty()) { + mayor = result; + LOGGER.info("[Skyblocker] Mayor set to {}.", mayor); + scheduleMayorTick(); //Ends up as a cyclic task with finer control over scheduled time } }); } From 3d75f5b7db5145952fb9bfa1e1a705e48a28296a Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Mon, 1 Jul 2024 13:47:19 +0300 Subject: [PATCH 4/5] Clean up code after rebase --- .../java/de/hysky/skyblocker/utils/Http.java | 19 +++++++------------ .../java/de/hysky/skyblocker/utils/Utils.java | 2 +- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/src/main/java/de/hysky/skyblocker/utils/Http.java b/src/main/java/de/hysky/skyblocker/utils/Http.java index 043ab97fa1..051bd52e7e 100644 --- a/src/main/java/de/hysky/skyblocker/utils/Http.java +++ b/src/main/java/de/hysky/skyblocker/utils/Http.java @@ -66,9 +66,8 @@ public static InputStream downloadContent(String url) throws IOException, Interr .build(); HttpResponse response = HTTP_CLIENT.send(request, BodyHandlers.ofInputStream()); - InputStream decodedInputStream = getDecodedInputStream(response); - return decodedInputStream; + return getDecodedInputStream(response); } public static String sendGetRequest(String url) throws IOException, InterruptedException { @@ -125,16 +124,12 @@ private static InputStream getDecodedInputStream(HttpResponse respo String encoding = getContentEncoding(response.headers()); try { - switch (encoding) { - case "": - return response.body(); - case "gzip": - return new GZIPInputStream(response.body()); - case "deflate": - return new InflaterInputStream(response.body()); - default: - throw new UnsupportedOperationException("The server sent content in an unexpected encoding: " + encoding); - } + return switch (encoding) { + case "" -> response.body(); + case "gzip" -> new GZIPInputStream(response.body()); + case "deflate" -> new InflaterInputStream(response.body()); + default -> throw new UnsupportedOperationException("The server sent content in an unexpected encoding: " + encoding); + }; } catch (IOException e) { throw new UncheckedIOException(e); } diff --git a/src/main/java/de/hysky/skyblocker/utils/Utils.java b/src/main/java/de/hysky/skyblocker/utils/Utils.java index 4ec951b745..9ebd8bbbba 100644 --- a/src/main/java/de/hysky/skyblocker/utils/Utils.java +++ b/src/main/java/de/hysky/skyblocker/utils/Utils.java @@ -494,7 +494,7 @@ private static void scheduleMayorTick() { private static void tickMayorCache() { CompletableFuture.supplyAsync(() -> { try { - Http.ApiResponse response = Http.sendCacheableGetRequest("https://api.hypixel.net/v2/resources/skyblock/election"); + Http.ApiResponse response = Http.sendCacheableGetRequest("https://api.hypixel.net/v2/resources/skyblock/election", null); //Authentication is not required for this endpoint if (!response.ok()) throw new HttpResponseException(response.statusCode(), response.content()); JsonObject json = JsonParser.parseString(response.content()).getAsJsonObject(); if (!json.get("success").getAsBoolean()) throw new RuntimeException("Request failed!"); //Can't find a more appropriate exception to throw here. From b6fec0afbe6b3555e8544ff6dd1792ea3ed21d03 Mon Sep 17 00:00:00 2001 From: Kevin <92656833+kevinthegreat1@users.noreply.github.com> Date: Sat, 6 Jul 2024 16:31:18 +0800 Subject: [PATCH 5/5] Refactor retry bit shift --- src/main/java/de/hysky/skyblocker/utils/Utils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/de/hysky/skyblocker/utils/Utils.java b/src/main/java/de/hysky/skyblocker/utils/Utils.java index 9ebd8bbbba..0b18ed9d6f 100644 --- a/src/main/java/de/hysky/skyblocker/utils/Utils.java +++ b/src/main/java/de/hysky/skyblocker/utils/Utils.java @@ -505,7 +505,7 @@ private static void tickMayorCache() { }).exceptionally(throwable -> { LOGGER.error("[Skyblocker] Failed to get mayor status!", throwable.getCause()); if (mayorTickRetryAttempts < 5) { - int minutes = 5 * (mayorTickRetryAttempts == 0 ? 1 : 2 << mayorTickRetryAttempts - 1); //5, 10, 20, 40, 80 minutes + int minutes = 5 * 1 << mayorTickRetryAttempts; //5, 10, 20, 40, 80 minutes mayorTickRetryAttempts++; LOGGER.warn("[Skyblocker] Retrying in {} minutes.", minutes); Scheduler.INSTANCE.schedule(Utils::tickMayorCache, minutes * 60 * 20);