diff options
Diffstat (limited to 'src/main/java/de/hysky/skyblocker/utils/Utils.java')
-rw-r--r-- | src/main/java/de/hysky/skyblocker/utils/Utils.java | 370 |
1 files changed, 370 insertions, 0 deletions
diff --git a/src/main/java/de/hysky/skyblocker/utils/Utils.java b/src/main/java/de/hysky/skyblocker/utils/Utils.java new file mode 100644 index 00000000..f046bffb --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/Utils.java @@ -0,0 +1,370 @@ +package de.hysky.skyblocker.utils; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +import de.hysky.skyblocker.events.SkyblockEvents; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import de.hysky.skyblocker.skyblock.item.PriceInfoTooltip; +import de.hysky.skyblocker.skyblock.rift.TheRift; +import de.hysky.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; +import net.minecraft.client.network.PlayerListEntry; +import net.minecraft.scoreboard.Scoreboard; +import net.minecraft.scoreboard.ScoreboardDisplaySlot; +import net.minecraft.scoreboard.ScoreboardObjective; +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.Collections; +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; + private static boolean isOnSkyblock = false; + private static boolean isInDungeons = false; + private static boolean isInjected = false; + /** + * The following fields store data returned from /locraw: {@link #profile}, {@link #server}, {@link #gameType}, {@link #locationRaw}, and {@link #map}. + */ + @SuppressWarnings("JavadocDeclaration") + private static String profile = ""; + private static String server = ""; + private static String gameType = ""; + private static String locationRaw = ""; + private static String map = ""; + private static long clientWorldJoinTime = 0; + private static boolean sentLocRaw = false; + private static boolean canSendLocRaw = false; + + /** + * @implNote The parent text will always be empty, the actual text content is inside the text's siblings. + */ + public static final ObjectArrayList<Text> TEXT_SCOREBOARD = new ObjectArrayList<>(); + public static final ObjectArrayList<String> STRING_SCOREBOARD = new ObjectArrayList<>(); + + public static boolean isOnHypixel() { + return isOnHypixel; + } + + public static boolean isOnSkyblock() { + return isOnSkyblock; + } + + public static boolean isInDungeons() { + return isInDungeons; + } + + public static boolean isInTheRift() { + return getLocationRaw().equals(TheRift.LOCATION); + } + + public static boolean isInjected() { + return isInjected; + } + + /** + * @return the profile parsed from the player list. + */ + public static String getProfile() { + return profile; + } + + /** + * @return the server parsed from /locraw. + */ + public static String getServer() { + return server; + } + + /** + * @return the game type parsed from /locraw. + */ + public static String getGameType() { + return gameType; + } + + /** + * @return the location raw parsed from /locraw. + */ + public static String getLocationRaw() { + return locationRaw; + } + + /** + * @return the map parsed from /locraw. + */ + public static String getMap() { + return map; + } + + public static void init() { + 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 + } + + /** + * Updates all the fields stored in this class from the sidebar, player list, and /locraw. + */ + public static void update() { + MinecraftClient client = MinecraftClient.getInstance(); + updateScoreboard(client); + updatePlayerPresenceFromScoreboard(client); + updateFromPlayerList(client); + updateLocRaw(); + } + + /** + * Updates {@link #isOnSkyblock}, {@link #isInDungeons}, and {@link #isInjected} from the scoreboard. + */ + public static void updatePlayerPresenceFromScoreboard(MinecraftClient client) { + List<String> sidebar = STRING_SCOREBOARD; + + FabricLoader fabricLoader = FabricLoader.getInstance(); + if ((client.world == null || client.isInSingleplayer() || sidebar == null || sidebar.isEmpty())) { + if (fabricLoader.isDevelopmentEnvironment()) { + sidebar = Collections.emptyList(); + } else { + isOnSkyblock = false; + isInDungeons = false; + return; + } + } + + if (sidebar.isEmpty() && !fabricLoader.isDevelopmentEnvironment()) return; + String string = sidebar.toString(); + + if (fabricLoader.isDevelopmentEnvironment() || isConnectedToHypixel(client)) { + if (!isOnHypixel) { + isOnHypixel = true; + } + if (fabricLoader.isDevelopmentEnvironment() || sidebar.get(0).contains("SKYBLOCK") || sidebar.get(0).contains("SKIBLOCK")) { + if (!isOnSkyblock) { + if (!isInjected) { + isInjected = true; + ItemTooltipCallback.EVENT.register(PriceInfoTooltip::onInjectTooltip); + } + isOnSkyblock = true; + SkyblockEvents.JOIN.invoker().onSkyblockJoin(); + } + } else { + onLeaveSkyblock(); + } + isInDungeons = fabricLoader.isDevelopmentEnvironment() || isOnSkyblock && string.contains("The Catacombs"); + } else if (isOnHypixel) { + isOnHypixel = false; + onLeaveSkyblock(); + } + } + + private static boolean isConnectedToHypixel(MinecraftClient client) { + String serverAddress = (client.getCurrentServerEntry() != null) ? client.getCurrentServerEntry().address.toLowerCase() : ""; + String serverBrand = (client.player != null && client.player.networkHandler != null && client.player.networkHandler.getBrand() != null) ? client.player.networkHandler.getBrand() : ""; + + return serverAddress.equalsIgnoreCase(ALTERNATE_HYPIXEL_ADDRESS) || serverAddress.contains("hypixel.net") || serverAddress.contains("hypixel.io") || serverBrand.contains("Hypixel BungeeCord"); + } + + private static void onLeaveSkyblock() { + if (isOnSkyblock) { + isOnSkyblock = false; + isInDungeons = false; + SkyblockEvents.LEAVE.invoker().onSkyblockLeave(); + } + } + + public static String getLocation() { + String location = null; + List<String> sidebarLines = STRING_SCOREBOARD; + try { + if (sidebarLines != null) { + for (String sidebarLine : sidebarLines) { + if (sidebarLine.contains("⏣")) location = sidebarLine; + if (sidebarLine.contains("ф")) location = sidebarLine; //Rift + } + if (location == null) location = "Unknown"; + location = location.strip(); + } + } catch (IndexOutOfBoundsException e) { + LOGGER.error("[Skyblocker] Failed to get location from sidebar", e); + } + return location; + } + + public static double getPurse() { + String purseString = null; + double purse = 0; + + List<String> sidebarLines = STRING_SCOREBOARD; + try { + + if (sidebarLines != null) { + for (String sidebarLine : sidebarLines) { + if (sidebarLine.contains("Piggy:")) purseString = sidebarLine; + if (sidebarLine.contains("Purse:")) purseString = sidebarLine; + } + } + if (purseString != null) purse = Double.parseDouble(purseString.replaceAll("[^0-9.]", "").strip()); + else purse = 0; + + } catch (IndexOutOfBoundsException e) { + LOGGER.error("[Skyblocker] Failed to get purse from sidebar", e); + } + return purse; + } + + public static int getBits() { + int bits = 0; + String bitsString = null; + List<String> sidebarLines = STRING_SCOREBOARD; + try { + if (sidebarLines != null) { + for (String sidebarLine : sidebarLines) { + if (sidebarLine.contains("Bits")) bitsString = sidebarLine; + } + } + if (bitsString != null) { + bits = Integer.parseInt(bitsString.replaceAll("[^0-9.]", "").strip()); + } + } catch (IndexOutOfBoundsException e) { + LOGGER.error("[Skyblocker] Failed to get bits from sidebar", e); + } + return bits; + } + + private static void updateScoreboard(MinecraftClient client) { + try { + TEXT_SCOREBOARD.clear(); + STRING_SCOREBOARD.clear(); + + ClientPlayerEntity player = client.player; + if (player == null) return; + + Scoreboard scoreboard = player.getScoreboard(); + ScoreboardObjective objective = scoreboard.getObjectiveForSlot(ScoreboardDisplaySlot.FROM_ID.apply(1)); + ObjectArrayList<Text> textLines = new ObjectArrayList<>(); + ObjectArrayList<String> stringLines = new ObjectArrayList<>(); + + for (ScoreboardPlayerScore score : scoreboard.getAllPlayerScores(objective)) { + Team team = scoreboard.getPlayerTeam(score.getPlayerName()); + + if (team != null) { + Text textLine = Text.empty().append(team.getPrefix().copy()).append(team.getSuffix().copy()); + String strLine = team.getPrefix().getString() + team.getSuffix().getString(); + + if (!strLine.trim().isEmpty()) { + String formatted = Formatting.strip(strLine); + + textLines.add(textLine); + stringLines.add(formatted); + } + } + } + + if (objective != null) { + stringLines.add(objective.getDisplayName().getString()); + textLines.add(Text.empty().append(objective.getDisplayName().copy())); + + Collections.reverse(stringLines); + Collections.reverse(textLines); + } + + TEXT_SCOREBOARD.addAll(textLines); + STRING_SCOREBOARD.addAll(stringLines); + } catch (NullPointerException e) { + //Do nothing + } + } + + private static void updateFromPlayerList(MinecraftClient client) { + if (client.getNetworkHandler() == null) { + return; + } + for (PlayerListEntry playerListEntry : client.getNetworkHandler().getPlayerList()) { + if (playerListEntry.getDisplayName() == null) { + continue; + } + String name = playerListEntry.getDisplayName().getString(); + if (name.startsWith(PROFILE_PREFIX)) { + profile = name.substring(PROFILE_PREFIX.length()); + } + } + } + + public static void onClientWorldJoin(ClientPlayNetworkHandler handler, PacketSender sender, MinecraftClient client) { + clientWorldJoinTime = System.currentTimeMillis(); + resetLocRawInfo(); + } + + /** + * Sends /locraw to the server if the player is on skyblock and on a new island. + */ + private static void updateLocRaw() { + if (isOnSkyblock) { + long currentTime = System.currentTimeMillis(); + if (!sentLocRaw && canSendLocRaw && currentTime > clientWorldJoinTime + 1000) { + MessageScheduler.INSTANCE.sendMessageAfterCooldown("/locraw"); + sentLocRaw = true; + canSendLocRaw = false; + } + } else { + resetLocRawInfo(); + } + } + + /** + * Parses the /locraw reply from the server + * + * @return not display the message in chat is the command is sent by the mod + */ + public static boolean onChatMessage(Text text, boolean overlay) { + String message = text.getString(); + if (message.startsWith("{\"server\":") && message.endsWith("}")) { + JsonObject locRaw = JsonParser.parseString(message).getAsJsonObject(); + if (locRaw.has("server")) { + server = locRaw.get("server").getAsString(); + if (locRaw.has("gameType")) { + gameType = locRaw.get("gameType").getAsString(); + } + if (locRaw.has("mode")) { + locationRaw = locRaw.get("mode").getAsString(); + } + if (locRaw.has("map")) { + map = locRaw.get("map").getAsString(); + } + + boolean shouldFilter = !sentLocRaw; + sentLocRaw = false; + + return shouldFilter; + } + } + return true; + } + + private static void resetLocRawInfo() { + sentLocRaw = false; + canSendLocRaw = true; + server = ""; + gameType = ""; + locationRaw = ""; + map = ""; + } +} |