diff options
| author | Aaron <51387595+AzureAaron@users.noreply.github.com> | 2024-08-15 16:28:00 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-08-15 16:28:00 -0400 |
| commit | 05a603504c6b9225eaf5c95d5fe6919d9add06ed (patch) | |
| tree | 523280ecc29df11099f3c319a3909170e8b4469b /src/main/java | |
| parent | 9281b6e6d0758bb789b02c7ec18a1206903728c8 (diff) | |
| parent | c7810521aa4c2f3ac12948b1c67df9ede5c1b850 (diff) | |
| download | Skyblocker-05a603504c6b9225eaf5c95d5fe6919d9add06ed.tar.gz Skyblocker-05a603504c6b9225eaf5c95d5fe6919d9add06ed.tar.bz2 Skyblocker-05a603504c6b9225eaf5c95d5fe6919d9add06ed.zip | |
Merge pull request #923 from Emirlol/fix-paul-minister-perk
Fix paul's minister perk not being accounted for
Diffstat (limited to 'src/main/java')
8 files changed, 193 insertions, 63 deletions
diff --git a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java index 860067ad..2cfd61a7 100644 --- a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java +++ b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java @@ -57,6 +57,7 @@ import de.hysky.skyblocker.skyblock.waypoint.*; import de.hysky.skyblocker.utils.*; import de.hysky.skyblocker.utils.chat.ChatMessageListener; import de.hysky.skyblocker.utils.discord.DiscordRPCManager; +import de.hysky.skyblocker.utils.mayor.MayorUtils; import de.hysky.skyblocker.utils.render.RenderHelper; import de.hysky.skyblocker.utils.render.culling.OcclusionCulling; import de.hysky.skyblocker.utils.container.ContainerSolverManager; @@ -212,6 +213,7 @@ public class SkyblockerMod implements ClientModInitializer { SlotTextManager.init(); BazaarHelper.init(); MobGlow.init(); + MayorUtils.init(); SlayerEntitiesGlow.init(); Scheduler.INSTANCE.scheduleCyclic(Utils::update, 20); diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java index d1fc08ec..f470d5ad 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonScore.java @@ -1,9 +1,7 @@ package de.hysky.skyblocker.skyblock.dungeon; - import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import de.hysky.skyblocker.config.SkyblockerConfig; import de.hysky.skyblocker.config.SkyblockerConfigManager; import de.hysky.skyblocker.config.configs.DungeonsConfig; import de.hysky.skyblocker.skyblock.dungeon.secrets.DungeonManager; @@ -11,6 +9,7 @@ import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; import de.hysky.skyblocker.utils.Constants; import de.hysky.skyblocker.utils.ProfileUtils; import de.hysky.skyblocker.utils.Utils; +import de.hysky.skyblocker.utils.mayor.MayorUtils; import de.hysky.skyblocker.utils.scheduler.MessageScheduler; import de.hysky.skyblocker.utils.scheduler.Scheduler; import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents; @@ -152,7 +151,7 @@ public class DungeonScore { setCurrentFloor(); dungeonStarted = true; puzzleCount = getPuzzleCount(); - isMayorPaul = Utils.getMayor().equals("Paul"); + isMayorPaul = MayorUtils.getMayor().perks().stream().anyMatch(perk -> perk.name().equals("EZPZ")) || MayorUtils.getMinister().perk().name().equals("EZPZ"); startingTime = System.currentTimeMillis(); floorRequirement = FloorRequirement.valueOf(currentFloor); floorHasMimics = MIMIC_FLOORS_PATTERN.matcher(currentFloor).matches(); diff --git a/src/main/java/de/hysky/skyblocker/skyblock/mayors/JerryTimer.java b/src/main/java/de/hysky/skyblocker/skyblock/mayors/JerryTimer.java index 7131a567..4945c0bc 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/mayors/JerryTimer.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/mayors/JerryTimer.java @@ -3,6 +3,7 @@ package de.hysky.skyblocker.skyblock.mayors; import de.hysky.skyblocker.config.SkyblockerConfigManager; import de.hysky.skyblocker.utils.Constants; import de.hysky.skyblocker.utils.Utils; +import de.hysky.skyblocker.utils.mayor.MayorUtils; import de.hysky.skyblocker.utils.scheduler.Scheduler; import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents; import net.minecraft.client.MinecraftClient; @@ -20,7 +21,7 @@ public final class JerryTimer { //Example message: "§b ☺ §eThere is a §aGreen Jerry§e!" //There are various formats, all of which start with the "§b ☺ " prefix and contain the word "<color> Jerry" ClientReceiveMessageEvents.GAME.register((message, overlay) -> { - if (overlay || !Utils.getMayor().equals("Jerry") || !SkyblockerConfigManager.get().helpers.jerry.enableJerryTimer) return; + if (overlay || !MayorUtils.getMayor().name().equals("Jerry") || !SkyblockerConfigManager.get().helpers.jerry.enableJerryTimer) return; String text = message.getString(); //This part of hypixel still uses legacy text formatting, so we can't directly check for the actual text if (!text.startsWith("§b ☺ ") || !text.contains("Jerry")) return; diff --git a/src/main/java/de/hysky/skyblocker/utils/Utils.java b/src/main/java/de/hysky/skyblocker/utils/Utils.java index d6c0fc5e..73861f24 100644 --- a/src/main/java/de/hysky/skyblocker/utils/Utils.java +++ b/src/main/java/de/hysky/skyblocker/utils/Utils.java @@ -6,7 +6,6 @@ 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; -import de.hysky.skyblocker.utils.scheduler.Scheduler; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import net.azureaaron.hmapi.data.server.Environment; @@ -27,14 +26,12 @@ import net.minecraft.text.MutableText; import net.minecraft.text.Text; import net.minecraft.util.Formatting; import net.minecraft.util.Util; -import org.apache.http.client.HttpResponseException; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.time.Instant; import java.util.Collections; -import java.util.concurrent.CompletableFuture; /** * Utility variables and methods for retrieving Skyblock related information. @@ -77,9 +74,6 @@ public class Utils { private static String locationRaw = ""; @NotNull private static String map = ""; - private static boolean mayorTickScheduled = false; - private static int mayorTickRetryAttempts = 0; - private static String mayor = ""; /** * @implNote The parent text will always be empty, the actual text content is inside the text's siblings. @@ -192,22 +186,7 @@ public class Utils { return map; } - /** - * @return the current mayor as cached on skyblock join. - */ - @NotNull - public static String getMayor() { - return mayor; - } - public static void init() { - SkyblockEvents.JOIN.register(() -> { - if (!mayorTickScheduled) { - tickMayorCache(); - scheduleMayorTick(); - mayorTickScheduled = true; - } - }); ClientReceiveMessageEvents.ALLOW_GAME.register(Utils::onChatMessage); ClientReceiveMessageEvents.GAME_CANCELED.register(Utils::onChatMessage); // Somehow this works even though onChatMessage returns a boolean ClientPlayConnectionEvents.DISCONNECT.register((handler, client) -> onDisconnect()); @@ -496,44 +475,6 @@ public class Utils { return true; } - 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 { - 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. - return json.get("mayor").getAsJsonObject().get("name").getAsString(); - } catch (Exception 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; //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 ""; //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 - } - }); - } - /** * Used to avoid triggering things like chat rules or chat listeners infinitely, do not use otherwise. * <p> diff --git a/src/main/java/de/hysky/skyblocker/utils/mayor/Mayor.java b/src/main/java/de/hysky/skyblocker/utils/mayor/Mayor.java new file mode 100644 index 00000000..8944d6fc --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/mayor/Mayor.java @@ -0,0 +1,33 @@ +package de.hysky.skyblocker.utils.mayor; + +import it.unimi.dsi.fastutil.objects.ObjectLists; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +/** + * Represents a mayor as retrieved from the API. + * @param key The key of the mayor. + * @param name The name of the mayor. + * @param perks The perks of the mayor. + */ +public record Mayor(@NotNull String key, @NotNull String name, @NotNull List<Perk> perks) { + /** + * An empty mayor. Allows for better null safety. + */ + public static final Mayor EMPTY = new Mayor("", "", ObjectLists.emptyList()); + + /** + * For formatting purposes when printing out the result of the API call. + * @see MayorUtils#tickMayorCache() + */ + @Override + public String toString() { + if (isEmpty()) return "Mayor.EMPTY"; + return name; + } + + public boolean isEmpty() { + return this == EMPTY; + } +} diff --git a/src/main/java/de/hysky/skyblocker/utils/mayor/MayorUtils.java b/src/main/java/de/hysky/skyblocker/utils/mayor/MayorUtils.java new file mode 100644 index 00000000..eee35353 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/mayor/MayorUtils.java @@ -0,0 +1,107 @@ +package de.hysky.skyblocker.utils.mayor; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import de.hysky.skyblocker.events.SkyblockEvents; +import de.hysky.skyblocker.utils.Http; +import de.hysky.skyblocker.utils.SkyblockTime; +import de.hysky.skyblocker.utils.scheduler.Scheduler; +import org.apache.http.client.HttpResponseException; +import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.concurrent.CompletableFuture; + +public class MayorUtils { + private static Mayor mayor = Mayor.EMPTY; + private static Minister minister = Minister.EMPTY; + private static boolean mayorTickScheduled = false; + private static int mayorTickRetryAttempts = 0; + private static final Logger LOGGER = LoggerFactory.getLogger(MayorUtils.class); + + private MayorUtils() {} + + @NotNull + public static Mayor getMayor() { + return mayor; + } + + @NotNull + public static Minister getMinister() { + return minister; + } + + public static void init() { + SkyblockEvents.JOIN.register(() -> { + if (!mayorTickScheduled) { + tickMayorCache(); + scheduleMayorTick(); + mayorTickScheduled = true; + } + }); + } + + 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(MayorUtils::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 { + 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. + JsonObject mayorObject = json.getAsJsonObject("mayor"); + if (mayorObject == null) throw new RuntimeException("No mayor object found in response!"); + return mayorObject; + } catch (Exception 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; //5, 10, 20, 40, 80 minutes + mayorTickRetryAttempts++; + LOGGER.warn("[Skyblocker] Retrying in {} minutes.", minutes); + Scheduler.INSTANCE.schedule(MayorUtils::tickMayorCache, minutes * 60 * 20); + } else { + LOGGER.warn("[Skyblocker] Failed to get mayor status after 5 retries! Stopping further retries until next reboot."); + } + return new JsonObject(); //Have to return a value for the thenAccept block. + }).thenAccept(result -> { + if (!result.isEmpty()) { + try { + mayor = new Mayor(result.get("key").getAsString(), + result.get("name").getAsString(), + result.getAsJsonArray("perks") + .asList() + .stream() + .map(JsonElement::getAsJsonObject) + .map(object -> new Perk(object.get("name").getAsString(), object.get("description").getAsString())) + .toList()); + } catch (Exception e) { + LOGGER.warn("[Skyblocker] Failed to parse mayor status from the API response.", e); + mayor = Mayor.EMPTY; + } + try { + JsonObject ministerObject = result.getAsJsonObject("minister"); + JsonObject ministerPerk = ministerObject.getAsJsonObject("perk"); + minister = new Minister(ministerObject.get("key").getAsString(), + ministerObject.get("name").getAsString(), + new Perk(ministerPerk.get("name").getAsString(), ministerPerk.get("description").getAsString())); + } catch (Exception e) { + LOGGER.warn("[Skyblocker] Failed to parse minister status from the API response. This might be due to a special mayor, in which case there are no ministers.", e); + minister = Minister.EMPTY; + } + LOGGER.info("[Skyblocker] Mayor set to {}, minister set to {}.", mayor, minister); + scheduleMayorTick(); //Ends up as a cyclic task with finer control over scheduled time + } + }); + } +} diff --git a/src/main/java/de/hysky/skyblocker/utils/mayor/Minister.java b/src/main/java/de/hysky/skyblocker/utils/mayor/Minister.java new file mode 100644 index 00000000..3420019a --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/mayor/Minister.java @@ -0,0 +1,30 @@ +package de.hysky.skyblocker.utils.mayor; + +import org.jetbrains.annotations.NotNull; + +/** + * Represents a minister as retrieved from the API. + * @param key The key of the minister. + * @param name The name of the minister. + * @param perk The perk of the minister. + */ +public record Minister(@NotNull String key, @NotNull String name, @NotNull Perk perk) { + /** + * An empty minister. Allows for better null safety. + */ + public static final Minister EMPTY = new Minister("", "", Perk.EMPTY); + + /** + * For formatting purposes when printing out the result of the API call. + * @see MayorUtils#tickMayorCache() + */ + @Override + public String toString() { + if (isEmpty()) return "Mayor.EMPTY"; + return name; + } + + public boolean isEmpty() { + return this == EMPTY; + } +} diff --git a/src/main/java/de/hysky/skyblocker/utils/mayor/Perk.java b/src/main/java/de/hysky/skyblocker/utils/mayor/Perk.java new file mode 100644 index 00000000..91c289c9 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/mayor/Perk.java @@ -0,0 +1,17 @@ +package de.hysky.skyblocker.utils.mayor; + +import org.jetbrains.annotations.NotNull; + +/** + * Represents a mayor perk. + * @param name The name of the perk. + * @param description The description of the perk. This will include the formatting codes that are used in the game. + */ +public record Perk(@NotNull String name, @NotNull String description) { + /** + * An empty perk. + * Represents a perk that does not exist for ministers. + * Allows for better null safety. + */ + public static final Perk EMPTY = new Perk("", ""); +} |
