diff options
Diffstat (limited to 'src/main/java/de/hysky/skyblocker/utils')
7 files changed, 221 insertions, 75 deletions
diff --git a/src/main/java/de/hysky/skyblocker/utils/ItemUtils.java b/src/main/java/de/hysky/skyblocker/utils/ItemUtils.java index e43ce783..65807886 100644 --- a/src/main/java/de/hysky/skyblocker/utils/ItemUtils.java +++ b/src/main/java/de/hysky/skyblocker/utils/ItemUtils.java @@ -43,7 +43,7 @@ import java.util.regex.Pattern; import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal; -public class ItemUtils { +public final class ItemUtils { public static final String ID = "id"; public static final String UUID = "uuid"; public static final Pattern NOT_DURABILITY = Pattern.compile("[^0-9 /]"); @@ -55,6 +55,8 @@ public class ItemUtils { ComponentChanges.CODEC.optionalFieldOf("components", ComponentChanges.EMPTY).forGetter(ItemStack::getComponentChanges) ).apply(instance, ItemStack::new))); + private ItemUtils() {} + public static LiteralArgumentBuilder<FabricClientCommandSource> dumpHeldItemCommand() { return literal("dumpHeldItem").executes(context -> { context.getSource().sendFeedback(Text.literal("[Skyblocker Debug] Held Item: " + SkyblockerMod.GSON_COMPACT.toJson(ItemStack.CODEC.encodeStart(JsonOps.INSTANCE, context.getSource().getPlayer().getMainHandStack()).getOrThrow()))); @@ -198,9 +200,13 @@ public class ItemUtils { return null; } + /** + * Gets the first line of the lore that matches the specified predicate. + * @return The first line of the lore that matches the predicate, or {@code null} if no line matches. + */ @Nullable - public static String getLoreLineIf(ItemStack item, Predicate<String> predicate) { - for (Text line : getLore(item)) { + public static String getLoreLineIf(ItemStack stack, Predicate<String> predicate) { + for (Text line : getLore(stack)) { String string = line.getString(); if (predicate.test(string)) { return string; @@ -210,21 +216,40 @@ public class ItemUtils { return null; } + /** + * Gets the first line of the lore that matches the specified pattern, using {@link Matcher#matches()}. + * @return A matcher that contains match results if the pattern was found in the lore, otherwise {@code null}. + */ @Nullable - public static Matcher getLoreLineIfMatch(ItemStack item, Pattern pattern) { - for (Text line : getLore(item)) { - String string = line.getString(); - Matcher matcher = pattern.matcher(string); - if (matcher.matches()) { + public static Matcher getLoreLineIfMatch(ItemStack stack, Pattern pattern) { + Matcher matcher = pattern.matcher(""); + for (Text line : getLore(stack)) { + if (matcher.reset(line.getString()).matches()) { return matcher; } } - return null; } - public static @NotNull List<Text> getLore(ItemStack item) { - return item.getOrDefault(DataComponentTypes.LORE, LoreComponent.DEFAULT).styledLines(); + /** + * Gets the first line of the lore that matches the specified pattern, using {@link Matcher#find()}. + * @param pattern the pattern to search for + * @param stack the stack to search the lore of + * @return A {@link Matcher matcher} that contains match results if the pattern was found in the lore, otherwise {@code null}. + */ + @Nullable + public static Matcher getLoreLineIfContainsMatch(ItemStack stack, Pattern pattern) { + Matcher matcher = pattern.matcher(""); + for (Text line : getLore(stack)) { + if (matcher.reset(line.getString()).find()) { + return matcher; + } + } + return null; + } + + public static @NotNull List<Text> getLore(ItemStack stack) { + return stack.getOrDefault(DataComponentTypes.LORE, LoreComponent.DEFAULT).styledLines(); } public static @NotNull PropertyMap propertyMapWithTexture(String textureValue) { diff --git a/src/main/java/de/hysky/skyblocker/utils/TextTransformer.java b/src/main/java/de/hysky/skyblocker/utils/TextTransformer.java new file mode 100644 index 00000000..b8fb5101 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/TextTransformer.java @@ -0,0 +1,92 @@ +package de.hysky.skyblocker.utils; + +import org.jetbrains.annotations.NotNull; + +import it.unimi.dsi.fastutil.chars.CharList; +import net.minecraft.text.MutableText; +import net.minecraft.text.Style; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +/** + * Contains utilities for transforming text. These methods are from Aaron's Mod. + * + * @author AzureAaron + */ +public class TextTransformer { + private static final CharList FORMAT_CODES = CharList.of('4', 'c', '6', 'e', '2', 'a','b', '3', '1', '9', 'd', '5', 'f', '7', '8', '0', 'r', 'k', 'l', 'm', 'n', 'o'); + + /** + * Converts strings with section symbol/legacy formatting to MutableText objects. + * + * @param legacy The string with legacy formatting to be transformed + * @return A {@link MutableText} object matching the exact formatting of the input + * + * @author AzureAaron + */ + public static MutableText fromLegacy(@NotNull String legacy) { + MutableText newText = Text.empty(); + StringBuilder builder = new StringBuilder(); + Formatting formatting = null; + boolean bold = false; + boolean italic = false; + boolean underline = false; + boolean strikethrough = false; + boolean obfuscated = false; + + for (int i = 0; i < legacy.length(); i++) { + //If we've encountered a new formatting code then append the text from the previous "sequence" and reset state + if (i != 0 && legacy.charAt(i - 1) == '§' && FORMAT_CODES.contains(Character.toLowerCase(legacy.charAt(i))) && !builder.isEmpty()) { + newText.append(Text.literal(builder.toString()).setStyle(Style.EMPTY + .withColor(formatting) + .withBold(bold) + .withItalic(italic) + .withUnderline(underline) + .withStrikethrough(strikethrough) + .withObfuscated(obfuscated))); + + //Erase all characters in the builder so we can reuse it, also clear formatting + builder.delete(0, builder.length()); + formatting = null; + bold = false; + italic = false; + underline = false; + strikethrough = false; + obfuscated = false; + } + + if (i != 0 && legacy.charAt(i - 1) == '§') { + Formatting fmt = Formatting.byCode(legacy.charAt(i)); + + switch (fmt) { + case BOLD -> bold = true; + case ITALIC -> italic = true; + case UNDERLINE -> underline = true; + case STRIKETHROUGH -> strikethrough = true; + case OBFUSCATED -> obfuscated = true; + + default -> formatting = fmt; + } + + continue; + } + + //This character isn't the start of a formatting sequence or this character isn't part of a formatting sequence + if (legacy.charAt(i) != '§' && (i == 0 || (i != 0 && legacy.charAt(i - 1) != '§'))) { + builder.append(legacy.charAt(i)); + } + + // We've read the last character so append the last text with all of the formatting + if (i == legacy.length() - 1) { + newText.append(Text.literal(builder.toString()).setStyle(Style.EMPTY + .withColor(formatting) + .withBold(bold) + .withItalic(italic) + .withUnderline(underline) + .withStrikethrough(strikethrough) + .withObfuscated(obfuscated))); + } + } + return newText; + } +} diff --git a/src/main/java/de/hysky/skyblocker/utils/Utils.java b/src/main/java/de/hysky/skyblocker/utils/Utils.java index 051110b2..bbeb11b5 100644 --- a/src/main/java/de/hysky/skyblocker/utils/Utils.java +++ b/src/main/java/de/hysky/skyblocker/utils/Utils.java @@ -6,21 +6,27 @@ 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.MessageScheduler; 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; +import net.azureaaron.hmapi.events.HypixelPacketEvents; +import net.azureaaron.hmapi.network.HypixelNetworking; +import net.azureaaron.hmapi.network.packet.s2c.ErrorS2CPacket; +import net.azureaaron.hmapi.network.packet.s2c.HelloS2CPacket; +import net.azureaaron.hmapi.network.packet.s2c.HypixelS2CPacket; +import net.azureaaron.hmapi.network.packet.v1.s2c.LocationUpdateS2CPacket; 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.*; 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; @@ -45,7 +51,7 @@ public class Utils { private static boolean isOnSkyblock = false; private static boolean isInjected = false; /** - * Current Skyblock location (from /locraw) + * Current Skyblock location (from the Mod API) */ @NotNull private static Location location = Location.UNKNOWN; @@ -60,10 +66,12 @@ public class Utils { @NotNull private static String profileId = ""; /** - * The following fields store data returned from /locraw: {@link #server}, {@link #gameType}, {@link #locationRaw}, and {@link #map}. + * The following fields store data returned from the Mod API: {@link #environment}, {@link #server}, {@link #gameType}, {@link #locationRaw}, and {@link #map}. */ @SuppressWarnings("JavadocDeclaration") @NotNull + private static Environment environment = Environment.PRODUCTION; + @NotNull private static String server = ""; @NotNull private static String gameType = ""; @@ -71,11 +79,6 @@ public class Utils { private static String locationRaw = ""; @NotNull private static String map = ""; - private static long clientWorldJoinTime = 0; - private static boolean sentLocRaw = false; - 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 int mayorTickRetryAttempts = 0; private static String mayor = ""; @@ -146,7 +149,7 @@ public class Utils { } /** - * @return the location parsed from /locraw. + * @return the location parsed from the Mod API. */ @NotNull public static Location getLocation() { @@ -154,7 +157,17 @@ public class Utils { } /** - * @return the server parsed from /locraw. + * Can be used to restrict features to being active only on the Alpha network. + * + * @return the current environment parsed from the Mod API. + */ + @NotNull + public static Environment getEnvironment() { + return environment; + } + + /** + * @return the server parsed from the Mod API. */ @NotNull public static String getServer() { @@ -162,7 +175,7 @@ public class Utils { } /** - * @return the game type parsed from /locraw. + * @return the game type parsed from the Mod API. */ @NotNull public static String getGameType() { @@ -170,7 +183,7 @@ public class Utils { } /** - * @return the location raw parsed from /locraw. + * @return the location raw parsed from the the Mod API. */ @NotNull public static String getLocationRaw() { @@ -178,7 +191,7 @@ public class Utils { } /** - * @return the map parsed from /locraw. + * @return the map parsed from the Mod API. */ @NotNull public static String getMap() { @@ -201,20 +214,23 @@ public class Utils { 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 + + //Register Mod API stuff + HypixelNetworking.registerToEvents(Util.make(new Object2IntOpenHashMap<>(), map -> map.put(LocationUpdateS2CPacket.ID, 1))); + HypixelPacketEvents.HELLO.register(Utils::onPacket); + HypixelPacketEvents.LOCATION_UPDATE.register(Utils::onPacket); } /** - * Updates all the fields stored in this class from the sidebar, player list, and /locraw. + * Updates all the fields stored in this class from the sidebar, and player list. */ public static void update() { MinecraftClient client = MinecraftClient.getInstance(); updateScoreboard(client); updatePlayerPresenceFromScoreboard(client); updateFromPlayerList(client); - updateLocRaw(); } /** @@ -244,7 +260,7 @@ public class Utils { if (!isInjected) { isInjected = true; } - isOnSkyblock = true; + isOnSkyblock = true; //TODO in the future we can probably replace these skyblock checks entirely with the Mod API SkyblockEvents.JOIN.invoker().onSkyblockJoin(); } } else { @@ -260,7 +276,7 @@ public class Utils { 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"); + return (!serverAddress.isEmpty() && serverAddress.equalsIgnoreCase(ALTERNATE_HYPIXEL_ADDRESS)) || serverAddress.contains("hypixel.net") || serverAddress.contains("hypixel.io") || serverBrand.contains("Hypixel BungeeCord"); } private static void onLeaveSkyblock() { @@ -395,25 +411,39 @@ public class Utils { } } - public static void onClientWorldJoin(ClientPlayNetworkHandler handler, PacketSender sender, MinecraftClient client) { - clientWorldJoinTime = System.currentTimeMillis(); - resetLocRawInfo(); - } + private static void onPacket(HypixelS2CPacket packet) { + switch (packet) { + case HelloS2CPacket(var environment) -> { + Utils.environment = environment; + } - /** - * 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; - locationChanged = true; + case LocationUpdateS2CPacket(var serverName, var serverType, var _lobbyName, var mode, var map) -> { + Utils.server = serverName; + Utils.gameType = serverType.orElse(""); + Utils.locationRaw = mode.orElse(""); + Utils.location = Location.from(locationRaw); + Utils.map = map.orElse(""); + + SkyblockEvents.LOCATION_CHANGE.invoker().onSkyblockLocationChange(location); } - } else { - resetLocRawInfo(); + + case ErrorS2CPacket(var id, var error) when id.equals(LocationUpdateS2CPacket.ID) -> { + server = ""; + gameType = ""; + locationRaw = ""; + location = Location.UNKNOWN; + map = ""; + + ClientPlayerEntity player = MinecraftClient.getInstance().player; + + if (player != null) { + player.sendMessage(Constants.PREFIX.get().append(Text.translatable("skyblocker.utils.locationUpdateError").formatted(Formatting.RED))); + } + + LOGGER.error("[Skyblocker] Failed to update your current location! Some features of the mod may not work correctly :( - Error: {}", error); + } + + default -> {} //Do Nothing } } @@ -423,6 +453,7 @@ public class Utils { * * @param message json message from chat */ + @Deprecated private static void parseLocRaw(String message) { JsonObject locRaw = JsonParser.parseString(message).getAsJsonObject(); @@ -441,11 +472,6 @@ public class Utils { if (locRaw.has("map")) { map = locRaw.get("map").getAsString(); } - - if (locationChanged) { - SkyblockEvents.LOCATION_CHANGE.invoker().onSkyblockLocationChange(location); - locationChanged = false; - } } /** @@ -458,10 +484,6 @@ public class Utils { if (message.startsWith("{\"server\":") && message.endsWith("}")) { parseLocRaw(message); - boolean shouldFilter = !sentLocRaw; - sentLocRaw = false; - - return shouldFilter; } if (isOnSkyblock) { @@ -481,16 +503,6 @@ public class Utils { return true; } - private static void resetLocRawInfo() { - sentLocRaw = false; - canSendLocRaw = true; - server = ""; - gameType = ""; - locationRaw = ""; - map = ""; - 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 diff --git a/src/main/java/de/hysky/skyblocker/utils/chat/ChatPatternListener.java b/src/main/java/de/hysky/skyblocker/utils/chat/ChatPatternListener.java index 708af280..e701e24c 100644 --- a/src/main/java/de/hysky/skyblocker/utils/chat/ChatPatternListener.java +++ b/src/main/java/de/hysky/skyblocker/utils/chat/ChatPatternListener.java @@ -1,6 +1,7 @@ package de.hysky.skyblocker.utils.chat; import net.minecraft.text.Text; +import org.intellij.lang.annotations.Language; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -9,7 +10,7 @@ public abstract class ChatPatternListener implements ChatMessageListener { protected static final String NUMBER = "-?[0-9]{1,3}(?>,[0-9]{3})*(?:\\.[1-9])?"; public final Pattern pattern; - public ChatPatternListener(String pattern) { + protected ChatPatternListener(@Language("RegExp") String pattern) { this.pattern = Pattern.compile(pattern); } diff --git a/src/main/java/de/hysky/skyblocker/utils/render/RenderHelper.java b/src/main/java/de/hysky/skyblocker/utils/render/RenderHelper.java index a5b9bf6b..1b16b138 100644 --- a/src/main/java/de/hysky/skyblocker/utils/render/RenderHelper.java +++ b/src/main/java/de/hysky/skyblocker/utils/render/RenderHelper.java @@ -11,6 +11,7 @@ import de.hysky.skyblocker.utils.render.title.TitleContainer; import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents; import net.fabricmc.fabric.api.event.Event; +import net.minecraft.block.BlockState; import net.minecraft.client.MinecraftClient; import net.minecraft.client.font.TextRenderer; import net.minecraft.client.gui.DrawContext; @@ -20,6 +21,7 @@ import net.minecraft.client.texture.Scaling; import net.minecraft.client.texture.Sprite; import net.minecraft.client.util.BufferAllocator; import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.client.world.ClientWorld; import net.minecraft.sound.SoundEvents; import net.minecraft.text.OrderedText; import net.minecraft.text.Text; @@ -64,18 +66,22 @@ public class RenderHelper { } public static void renderFilled(WorldRenderContext context, BlockPos pos, Vec3d dimensions, float[] colorComponents, float alpha, boolean throughWalls) { + renderFilled(context, Vec3d.of(pos), dimensions, colorComponents, alpha, throughWalls); + } + + public static void renderFilled(WorldRenderContext context, Vec3d pos, Vec3d dimensions, float[] colorComponents, float alpha, boolean throughWalls) { if (throughWalls) { if (FrustumUtils.isVisible(pos.getX(), pos.getY(), pos.getZ(), pos.getX() + dimensions.x, pos.getY() + dimensions.y, pos.getZ() + dimensions.z)) { - renderFilled(context, Vec3d.of(pos), dimensions, colorComponents, alpha, true); + renderFilledInternal(context, pos, dimensions, colorComponents, alpha, true); } } else { if (OcclusionCulling.getRegularCuller().isVisible(pos.getX(), pos.getY(), pos.getZ(), pos.getX() + dimensions.x, pos.getY() + dimensions.y, pos.getZ() + dimensions.z)) { - renderFilled(context, Vec3d.of(pos), dimensions, colorComponents, alpha, false); + renderFilledInternal(context, pos, dimensions, colorComponents, alpha, false); } } } - private static void renderFilled(WorldRenderContext context, Vec3d pos, Vec3d dimensions, float[] colorComponents, float alpha, boolean throughWalls) { + private static void renderFilledInternal(WorldRenderContext context, Vec3d pos, Vec3d dimensions, float[] colorComponents, float alpha, boolean throughWalls) { MatrixStack matrices = context.matrixStack(); Vec3d camera = context.camera().getPos(); @@ -330,6 +336,14 @@ public class RenderHelper { } } + public static Box getBlockBoundingBox(ClientWorld world, BlockPos pos) { + return getBlockBoundingBox(world, world.getBlockState(pos), pos); + } + + public static Box getBlockBoundingBox(ClientWorld world, BlockState state, BlockPos pos) { + return state.getOutlineShape(world, pos).asCuboid().getBoundingBox().offset(pos); + } + /** * Adds the title to {@link TitleContainer} and {@link #playNotificationSound() plays the notification sound} if the title is not in the {@link TitleContainer} already. * No checking needs to be done on whether the title is in the {@link TitleContainer} already by the caller. diff --git a/src/main/java/de/hysky/skyblocker/utils/render/gui/ContainerSolver.java b/src/main/java/de/hysky/skyblocker/utils/render/gui/ContainerSolver.java index 81c9ebec..9a9d0907 100644 --- a/src/main/java/de/hysky/skyblocker/utils/render/gui/ContainerSolver.java +++ b/src/main/java/de/hysky/skyblocker/utils/render/gui/ContainerSolver.java @@ -4,6 +4,7 @@ import de.hysky.skyblocker.SkyblockerMod; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import net.minecraft.client.gui.screen.ingame.GenericContainerScreen; import net.minecraft.item.ItemStack; +import org.intellij.lang.annotations.Language; import java.util.List; import java.util.regex.Pattern; @@ -12,7 +13,7 @@ import java.util.regex.Pattern; * Abstract class for gui solvers. Extend this class to add a new gui solver, like terminal solvers or experiment solvers. */ public abstract class ContainerSolver extends AbstractContainerMatcher { - protected ContainerSolver(String titlePattern) { + protected ContainerSolver(@Language("RegExp") String titlePattern) { super(titlePattern); } diff --git a/src/main/java/de/hysky/skyblocker/utils/render/gui/ContainerSolverManager.java b/src/main/java/de/hysky/skyblocker/utils/render/gui/ContainerSolverManager.java index 9a1e3072..79cc78f5 100644 --- a/src/main/java/de/hysky/skyblocker/utils/render/gui/ContainerSolverManager.java +++ b/src/main/java/de/hysky/skyblocker/utils/render/gui/ContainerSolverManager.java @@ -1,10 +1,10 @@ package de.hysky.skyblocker.utils.render.gui; import com.mojang.blaze3d.systems.RenderSystem; - import de.hysky.skyblocker.mixins.accessors.HandledScreenAccessor; import de.hysky.skyblocker.skyblock.accessories.newyearcakes.NewYearCakeBagHelper; import de.hysky.skyblocker.skyblock.accessories.newyearcakes.NewYearCakesHelper; +import de.hysky.skyblocker.skyblock.bazaar.ReorderHelper; import de.hysky.skyblocker.skyblock.chocolatefactory.ChocolateFactorySolver; import de.hysky.skyblocker.skyblock.dungeon.CroesusHelper; import de.hysky.skyblocker.skyblock.dungeon.CroesusProfit; @@ -59,7 +59,8 @@ public class ContainerSolverManager { UltrasequencerSolver.INSTANCE, new NewYearCakeBagHelper(), NewYearCakesHelper.INSTANCE, - new ChocolateFactorySolver() + new ChocolateFactorySolver(), + new ReorderHelper() }; } |