diff options
Diffstat (limited to 'src/main/java/me/xmrvizzy/skyblocker/utils')
8 files changed, 207 insertions, 64 deletions
diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/Constants.java b/src/main/java/me/xmrvizzy/skyblocker/utils/Constants.java new file mode 100644 index 00000000..aef55687 --- /dev/null +++ b/src/main/java/me/xmrvizzy/skyblocker/utils/Constants.java @@ -0,0 +1,8 @@ +package me.xmrvizzy.skyblocker.utils; + +/** + * Holds generic static constants + */ +public interface Constants { + String LEVEL_EMBLEMS = "\u2E15\u273F\u2741\u2E19\u03B1\u270E\u2615\u2616\u2663\u213B\u2694\u27B6\u26A1\u2604\u269A\u2693\u2620\u269B\u2666\u2660\u2764\u2727\u238A\u1360\u262C\u269D\u29C9\uA214\u32D6\u2E0E\u26A0\uA541\u3020\u30C4\u2948\u2622\u2623\u273E\u269C\u0BD0\u0A6D\u2742\u16C3\u3023\u10F6\u0444\u266A\u266B\u04C3\u26C1\u26C3\u16DD\uA03E\u1C6A\u03A3\u09EB\u2603\u2654\u26C2\u12DE"; +} diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/Http.java b/src/main/java/me/xmrvizzy/skyblocker/utils/Http.java new file mode 100644 index 00000000..3461189c --- /dev/null +++ b/src/main/java/me/xmrvizzy/skyblocker/utils/Http.java @@ -0,0 +1,89 @@ +package me.xmrvizzy.skyblocker.utils; + +import java.io.IOException; +import java.io.InputStream; +import java.io.UncheckedIOException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpClient.Version; +import java.net.http.HttpHeaders; +import java.net.http.HttpRequest; +import java.net.http.HttpRequest.BodyPublishers; +import java.net.http.HttpResponse; +import java.net.http.HttpResponse.BodyHandlers; +import java.time.Duration; +import java.util.zip.GZIPInputStream; +import java.util.zip.InflaterInputStream; + +import me.xmrvizzy.skyblocker.SkyblockerMod; +import net.minecraft.SharedConstants; + +/** + * @implNote All http requests are sent using HTTP 2 + */ +public class Http { + private static final String USER_AGENT = "Skyblocker/" + SkyblockerMod.VERSION + " (" + SharedConstants.getGameVersion().getName() + ")"; + private static final HttpClient HTTP_CLIENT = HttpClient.newBuilder() + .connectTimeout(Duration.ofSeconds(10)) + .build(); + + public static String sendGetRequest(String url) throws IOException, InterruptedException { + HttpRequest request = HttpRequest.newBuilder() + .GET() + .header("Accept", "application/json") + .header("Accept-Encoding", "gzip, deflate") + .header("User-Agent", USER_AGENT) + .version(Version.HTTP_2) + .uri(URI.create(url)) + .build(); + + HttpResponse<InputStream> response = HTTP_CLIENT.send(request, BodyHandlers.ofInputStream()); + InputStream decodedInputStream = getDecodedInputStream(response); + String body = new String(decodedInputStream.readAllBytes()); + + return body; + } + + public static HttpHeaders sendHeadRequest(String url) throws IOException, InterruptedException { + HttpRequest request = HttpRequest.newBuilder() + .method("HEAD", BodyPublishers.noBody()) + .header("User-Agent", USER_AGENT) + .version(Version.HTTP_2) + .uri(URI.create(url)) + .build(); + + HttpResponse<Void> response = HTTP_CLIENT.send(request, BodyHandlers.discarding()); + return response.headers(); + } + + private static InputStream getDecodedInputStream(HttpResponse<InputStream> response) { + String encoding = getContentEncoding(response); + + 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); + } + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + private static String getContentEncoding(HttpResponse<InputStream> response) { + return response.headers().firstValue("Content-Encoding").orElse(""); + } + + public static String getEtag(HttpHeaders headers) { + return headers.firstValue("Etag").orElse(""); + } + + public static String getLastModified(HttpHeaders headers) { + return headers.firstValue("Last-Modified").orElse(""); + } +} diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/PosUtils.java b/src/main/java/me/xmrvizzy/skyblocker/utils/PosUtils.java new file mode 100644 index 00000000..4f32292c --- /dev/null +++ b/src/main/java/me/xmrvizzy/skyblocker/utils/PosUtils.java @@ -0,0 +1,14 @@ +package me.xmrvizzy.skyblocker.utils; + +import net.minecraft.util.math.BlockPos; + +public final class PosUtils { + public static BlockPos parsePosString(String posData) { + String[] posArray = posData.split(","); + return new BlockPos(Integer.parseInt(posArray[0]), Integer.parseInt(posArray[1]), Integer.parseInt(posArray[2])); + } + + public static String getPosString(BlockPos blockPos) { + return blockPos.getX() + "," + blockPos.getY() + "," + blockPos.getZ(); + } +} diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/Utils.java b/src/main/java/me/xmrvizzy/skyblocker/utils/Utils.java index 839e0dae..755e191d 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/utils/Utils.java +++ b/src/main/java/me/xmrvizzy/skyblocker/utils/Utils.java @@ -2,13 +2,14 @@ package me.xmrvizzy.skyblocker.utils; import com.google.gson.JsonObject; import com.google.gson.JsonParser; -import me.xmrvizzy.skyblocker.SkyblockerMod; import me.xmrvizzy.skyblocker.skyblock.item.PriceInfoTooltip; import me.xmrvizzy.skyblocker.skyblock.rift.TheRift; +import me.xmrvizzy.skyblocker.utils.scheduler.MessageScheduler; import net.fabricmc.fabric.api.client.item.v1.ItemTooltipCallback; import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents; import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents; import net.fabricmc.fabric.api.networking.v1.PacketSender; +import net.fabricmc.loader.api.FabricLoader; import net.minecraft.client.MinecraftClient; import net.minecraft.client.network.ClientPlayNetworkHandler; import net.minecraft.client.network.ClientPlayerEntity; @@ -19,6 +20,8 @@ import net.minecraft.scoreboard.ScoreboardPlayerScore; import net.minecraft.scoreboard.Team; import net.minecraft.text.Text; import net.minecraft.util.Formatting; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.Collections; @@ -28,6 +31,7 @@ import java.util.List; * Utility variables and methods for retrieving Skyblock related information. */ public class Utils { + private static final Logger LOGGER = LoggerFactory.getLogger(Utils.class); private static final String ALTERNATE_HYPIXEL_ADDRESS = System.getProperty("skyblocker.alternateHypixelAddress", ""); private static final String PROFILE_PREFIX = "Profile: "; private static boolean isOnHypixel = false; @@ -45,7 +49,7 @@ public class Utils { private static String map = ""; private static long clientWorldJoinTime = 0; private static boolean sentLocRaw = false; - private static long lastLocRaw = 0; + private static boolean canSendLocRaw = false; public static boolean isOnHypixel() { return isOnHypixel; @@ -124,19 +128,25 @@ public class Utils { public static void updateFromScoreboard(MinecraftClient client) { List<String> sidebar; - if (client.world == null || client.isInSingleplayer() || (sidebar = getSidebar()) == null) { - isOnSkyblock = false; - isInDungeons = false; - return; + FabricLoader fabricLoader = FabricLoader.getInstance(); + if ((client.world == null || client.isInSingleplayer() || (sidebar = getSidebar()) == null)) { + if (fabricLoader.isDevelopmentEnvironment()) { + sidebar = Collections.emptyList(); + } else { + isOnSkyblock = false; + isInDungeons = false; + return; + } } + + if (sidebar.isEmpty() && !fabricLoader.isDevelopmentEnvironment()) return; String string = sidebar.toString(); - if (sidebar.isEmpty()) return; - if (isConnectedToHypixel(client)) { + if (fabricLoader.isDevelopmentEnvironment() || isConnectedToHypixel(client)) { if (!isOnHypixel) { isOnHypixel = true; } - if (sidebar.get(0).contains("SKYBLOCK") || sidebar.get(0).contains("SKIBLOCK")) { + if (fabricLoader.isDevelopmentEnvironment() || sidebar.get(0).contains("SKYBLOCK") || sidebar.get(0).contains("SKIBLOCK")) { if (!isOnSkyblock) { if (!isInjected) { isInjected = true; @@ -148,19 +158,18 @@ public class Utils { } else { leaveSkyblock(); } - isInDungeons = isOnSkyblock && string.contains("The Catacombs"); + isInDungeons = fabricLoader.isDevelopmentEnvironment() || isOnSkyblock && string.contains("The Catacombs"); } else if (isOnHypixel) { isOnHypixel = false; leaveSkyblock(); } } - + private static boolean isConnectedToHypixel(MinecraftClient client) { - String serverAddress = (client.getCurrentServerEntry() != null) ? client.getCurrentServerEntry().address.toLowerCase() : ""; - String serverBrand = (client.player != null && client.player.getServerBrand() != null) ? client.player.getServerBrand() : ""; - boolean isOnHypixel = (serverAddress.equalsIgnoreCase(ALTERNATE_HYPIXEL_ADDRESS) || serverAddress.contains("hypixel.net") || serverAddress.contains("hypixel.io") || serverBrand.contains("Hypixel BungeeCord")); - - return isOnHypixel; + String serverAddress = (client.getCurrentServerEntry() != null) ? client.getCurrentServerEntry().address.toLowerCase() : ""; + String serverBrand = (client.player != null && client.player.getServerBrand() != null) ? client.player.getServerBrand() : ""; + + return serverAddress.equalsIgnoreCase(ALTERNATE_HYPIXEL_ADDRESS) || serverAddress.contains("hypixel.net") || serverAddress.contains("hypixel.io") || serverBrand.contains("Hypixel BungeeCord"); } private static void leaveSkyblock() { @@ -184,7 +193,7 @@ public class Utils { location = location.strip(); } } catch (IndexOutOfBoundsException e) { - e.printStackTrace(); + LOGGER.error("[Skyblocker] Failed to get location from sidebar", e); } return location; } @@ -206,7 +215,7 @@ public class Utils { else purse = 0; } catch (IndexOutOfBoundsException e) { - e.printStackTrace(); + LOGGER.error("[Skyblocker] Failed to get purse from sidebar", e); } return purse; } @@ -225,12 +234,11 @@ public class Utils { bits = Integer.parseInt(bitsString.replaceAll("[^0-9.]", "").strip()); } } catch (IndexOutOfBoundsException e) { - e.printStackTrace(); + LOGGER.error("[Skyblocker] Failed to get bits from sidebar", e); } return bits; } - public static List<String> getSidebar() { try { ClientPlayerEntity client = MinecraftClient.getInstance().player; @@ -242,7 +250,7 @@ public class Utils { Team team = scoreboard.getPlayerTeam(score.getPlayerName()); if (team != null) { String line = team.getPrefix().getString() + team.getSuffix().getString(); - if (line.trim().length() > 0) { + if (!line.trim().isEmpty()) { String formatted = Formatting.strip(line); lines.add(formatted); } @@ -285,10 +293,10 @@ public class Utils { private static void updateLocRaw() { if (isOnSkyblock) { long currentTime = System.currentTimeMillis(); - if (!sentLocRaw && currentTime > clientWorldJoinTime + 1000 && currentTime > lastLocRaw + 15000) { - SkyblockerMod.getInstance().messageScheduler.sendMessageAfterCooldown("/locraw"); + if (!sentLocRaw && canSendLocRaw && currentTime > clientWorldJoinTime + 1000) { + MessageScheduler.INSTANCE.sendMessageAfterCooldown("/locraw"); sentLocRaw = true; - lastLocRaw = currentTime; + canSendLocRaw = false; } } else { resetLocRawInfo(); @@ -315,7 +323,11 @@ public class Utils { if (locRaw.has("map")) { map = locRaw.get("map").getAsString(); } - return !sentLocRaw; + + boolean shouldFilter = !sentLocRaw; + sentLocRaw = false; + + return shouldFilter; } } return true; @@ -323,6 +335,7 @@ public class Utils { private static void resetLocRawInfo() { sentLocRaw = false; + canSendLocRaw = true; server = ""; gameType = ""; locationRaw = ""; diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/render/RenderHelper.java b/src/main/java/me/xmrvizzy/skyblocker/utils/render/RenderHelper.java index aaf92d68..907896e2 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/utils/render/RenderHelper.java +++ b/src/main/java/me/xmrvizzy/skyblocker/utils/render/RenderHelper.java @@ -95,7 +95,7 @@ public class RenderHelper { matrices.translate(-camera.getX(), -camera.getY(), -camera.getZ()); buffer.begin(DrawMode.LINES, VertexFormats.LINES); - WorldRenderer.drawBox(matrices, buffer, box, colorComponents[0] * 255f, colorComponents[1] * 255f, colorComponents[2] * 255f, 1f); + WorldRenderer.drawBox(matrices, buffer, box, colorComponents[0], colorComponents[1], colorComponents[2], 1f); tessellator.draw(); matrices.pop(); diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/render/title/TitleContainer.java b/src/main/java/me/xmrvizzy/skyblocker/utils/render/title/TitleContainer.java index 6e15c871..2555572c 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/utils/render/title/TitleContainer.java +++ b/src/main/java/me/xmrvizzy/skyblocker/utils/render/title/TitleContainer.java @@ -1,6 +1,5 @@ package me.xmrvizzy.skyblocker.utils.render.title; -import me.xmrvizzy.skyblocker.SkyblockerMod; import me.xmrvizzy.skyblocker.config.SkyblockerConfig; import me.xmrvizzy.skyblocker.utils.scheduler.Scheduler; import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; @@ -66,7 +65,7 @@ public class TitleContainer { */ public static boolean addTitle(Title title, int ticks) { if (addTitle(title)) { - SkyblockerMod.getInstance().scheduler.schedule(() -> TitleContainer.removeTitle(title), ticks); + Scheduler.INSTANCE.schedule(() -> TitleContainer.removeTitle(title), ticks); return true; } return false; diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/scheduler/MessageScheduler.java b/src/main/java/me/xmrvizzy/skyblocker/utils/scheduler/MessageScheduler.java index bde29c13..b8ffa548 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/utils/scheduler/MessageScheduler.java +++ b/src/main/java/me/xmrvizzy/skyblocker/utils/scheduler/MessageScheduler.java @@ -3,19 +3,22 @@ package me.xmrvizzy.skyblocker.utils.scheduler; import net.minecraft.client.MinecraftClient; /** - * A scheduler for sending chat messages or commands. Use the instance in {@link me.xmrvizzy.skyblocker.SkyblockerMod#messageScheduler SkyblockerMod.messageScheduler}. Do not instantiate this class. + * A scheduler for sending chat messages or commands. Use the instance in {@link #INSTANCE}. Do not instantiate this class. */ -@SuppressWarnings("deprecation") public class MessageScheduler extends Scheduler { /** * The minimum delay that the server will accept between chat messages. */ private static final int MIN_DELAY = 200; + public static final MessageScheduler INSTANCE = new MessageScheduler(); /** * The timestamp of the last message send, */ private long lastMessage = 0; + protected MessageScheduler() { + } + /** * Sends a chat message or command after the minimum cooldown. Prefer this method to send messages or commands to the server. * diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/scheduler/Scheduler.java b/src/main/java/me/xmrvizzy/skyblocker/utils/scheduler/Scheduler.java index 76112e0d..700bdce3 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/utils/scheduler/Scheduler.java +++ b/src/main/java/me/xmrvizzy/skyblocker/utils/scheduler/Scheduler.java @@ -1,30 +1,28 @@ package me.xmrvizzy.skyblocker.utils.scheduler; import com.mojang.brigadier.Command; -import me.xmrvizzy.skyblocker.SkyblockerMod; +import it.unimi.dsi.fastutil.ints.AbstractInt2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.screen.Screen; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.PriorityQueue; +import java.util.ArrayList; +import java.util.List; import java.util.function.Supplier; /** - * A scheduler for running tasks at a later time. Tasks will be run synchronously on the main client thread. Use the instance stored in {@link SkyblockerMod#scheduler}. Do not instantiate this class. + * A scheduler for running tasks at a later time. Tasks will be run synchronously on the main client thread. Use the instance stored in {@link #INSTANCE}. Do not instantiate this class. */ public class Scheduler { private static final Logger LOGGER = LoggerFactory.getLogger(Scheduler.class); + public static final Scheduler INSTANCE = new Scheduler(); private int currentTick = 0; - private final PriorityQueue<ScheduledTask> tasks = new PriorityQueue<>(); + private final AbstractInt2ObjectMap<List<ScheduledTask>> tasks = new Int2ObjectOpenHashMap<>(); - /** - * Do not instantiate this class. Use {@link SkyblockerMod#scheduler} instead. - */ - @SuppressWarnings("DeprecatedIsStillUsed") - @Deprecated - public Scheduler() { + protected Scheduler() { } /** @@ -34,11 +32,11 @@ public class Scheduler { * @param delay the delay in ticks */ public void schedule(Runnable task, int delay) { - if (delay < 0) { + if (delay >= 0) { + addTask(new ScheduledTask(task), currentTick + delay); + } else { LOGGER.warn("Scheduled a task with negative delay"); } - ScheduledTask tmp = new ScheduledTask(task, currentTick + delay); - tasks.add(tmp); } /** @@ -48,15 +46,15 @@ public class Scheduler { * @param period the period in ticks */ public void scheduleCyclic(Runnable task, int period) { - if (period <= 0) { - LOGGER.error("Attempted to schedule a cyclic task with period lower than 1"); + if (period > 0) { + addTask(new CyclicTask(task, period), currentTick); } else { - new CyclicTask(task, period).run(); + LOGGER.error("Attempted to schedule a cyclic task with period lower than 1"); } } public static Command<FabricClientCommandSource> queueOpenScreenCommand(Supplier<Screen> screenSupplier) { - return context -> SkyblockerMod.getInstance().scheduler.queueOpenScreen(screenSupplier); + return context -> INSTANCE.queueOpenScreen(screenSupplier); } /** @@ -71,11 +69,18 @@ public class Scheduler { } public void tick() { - currentTick += 1; - ScheduledTask task; - while ((task = tasks.peek()) != null && task.schedule <= currentTick && runTask(task)) { - tasks.poll(); + if (tasks.containsKey(currentTick)) { + List<ScheduledTask> currentTickTasks = tasks.get(currentTick); + //noinspection ForLoopReplaceableByForEach (or else we get a ConcurrentModificationException) + for (int i = 0; i < currentTickTasks.size(); i++) { + ScheduledTask task = currentTickTasks.get(i); + if (!runTask(task)) { + tasks.computeIfAbsent(currentTick + 1, key -> new ArrayList<>()).add(task); + } + } + tasks.remove(currentTick); } + currentTick += 1; } /** @@ -89,30 +94,42 @@ public class Scheduler { return true; } + private void addTask(ScheduledTask scheduledTask, int schedule) { + if (tasks.containsKey(schedule)) { + tasks.get(schedule).add(scheduledTask); + } else { + List<ScheduledTask> list = new ArrayList<>(); + list.add(scheduledTask); + tasks.put(schedule, list); + } + } + /** * A task that runs every period ticks. More specifically, this task reschedules itself to run again after period ticks every time it runs. - * - * @param inner the task to run - * @param period the period in ticks */ - protected record CyclicTask(Runnable inner, int period) implements Runnable { + protected class CyclicTask extends ScheduledTask { + private final int period; + + CyclicTask(Runnable inner, int period) { + super(inner); + this.period = period; + } + @Override public void run() { - SkyblockerMod.getInstance().scheduler.schedule(this, period); - inner.run(); + super.run(); + addTask(this, currentTick + period); } } /** * A task that runs at a specific tick, relative to {@link #currentTick}. - * - * @param inner the task to run - * @param schedule the tick to run at */ - protected record ScheduledTask(Runnable inner, int schedule) implements Comparable<ScheduledTask>, Runnable { - @Override - public int compareTo(ScheduledTask o) { - return schedule - o.schedule; + protected static class ScheduledTask implements Runnable { + private final Runnable inner; + + public ScheduledTask(Runnable inner) { + this.inner = inner; } @Override |
