From d7173e5eb84947616ca3d14ece77783368f7bb02 Mon Sep 17 00:00:00 2001 From: BigloBot <95036804+BigloBot@users.noreply.github.com> Date: Sat, 24 Aug 2024 08:13:37 +0100 Subject: [Pv & SlayerHighlight] Fixes and improvements@ (#952) --- .../mixins/ClientPlayNetworkHandlerMixin.java | 6 +- .../hysky/skyblocker/skyblock/entity/MobGlow.java | 5 +- .../profileviewer/collections/GenericCategory.java | 10 ++-- .../skyblock/slayers/SlayerEntitiesGlow.java | 66 +++++++++++++++++++--- .../de/hysky/skyblocker/utils/SlayerUtils.java | 7 +-- 5 files changed, 75 insertions(+), 19 deletions(-) (limited to 'src/main/java') diff --git a/src/main/java/de/hysky/skyblocker/mixins/ClientPlayNetworkHandlerMixin.java b/src/main/java/de/hysky/skyblocker/mixins/ClientPlayNetworkHandlerMixin.java index 8cafb601..1a161b95 100644 --- a/src/main/java/de/hysky/skyblocker/mixins/ClientPlayNetworkHandlerMixin.java +++ b/src/main/java/de/hysky/skyblocker/mixins/ClientPlayNetworkHandlerMixin.java @@ -127,7 +127,11 @@ public abstract class ClientPlayNetworkHandlerMixin { if (SkyblockerConfigManager.get().slayers.highlightMinis == SlayersConfig.HighlightSlayerEntities.GLOW && SlayerEntitiesGlow.isSlayerMiniMob(armorStandEntity) || SkyblockerConfigManager.get().slayers.highlightBosses == SlayersConfig.HighlightSlayerEntities.GLOW && SlayerEntitiesGlow.isSlayer(armorStandEntity)) { - SlayerEntitiesGlow.setSlayerMobGlow(armorStandEntity); + if (armorStandEntity.isDead()) { + SlayerEntitiesGlow.cleanupArmorstand(armorStandEntity); + } else { + SlayerEntitiesGlow.setSlayerMobGlow(armorStandEntity); + } } if (SkyblockerConfigManager.get().slayers.blazeSlayer.firePillarCountdown != SlayersConfig.BlazeSlayer.FirePillar.OFF) FirePillarAnnouncer.checkFirePillar(entity); diff --git a/src/main/java/de/hysky/skyblocker/skyblock/entity/MobGlow.java b/src/main/java/de/hysky/skyblocker/skyblock/entity/MobGlow.java index b0436dd1..54cda5f1 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/entity/MobGlow.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/entity/MobGlow.java @@ -75,10 +75,11 @@ public class MobGlow { } private static boolean computeShouldMobGlow(Entity entity) { - String name = entity.getName().getString(); // Dungeons if (Utils.isInDungeons()) { + String name = entity.getName().getString(); + return switch (entity) { // Minibosses @@ -99,7 +100,7 @@ public class MobGlow { return switch (entity) { // Rift Blobbercyst - case PlayerEntity p when Utils.isInTheRift() && name.equals("Blobbercyst ") -> SkyblockerConfigManager.get().otherLocations.rift.blobbercystGlow; + case PlayerEntity p when Utils.isInTheRift() && p.getName().getString().equals("Blobbercyst ") -> SkyblockerConfigManager.get().otherLocations.rift.blobbercystGlow; // Dojo Helpers case ZombieEntity zombie when Utils.isInCrimson() && DojoManager.inArena -> DojoManager.shouldGlow(getArmorStandName(zombie)); diff --git a/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/collections/GenericCategory.java b/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/collections/GenericCategory.java index f34ad06e..10778adc 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/collections/GenericCategory.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/collections/GenericCategory.java @@ -54,7 +54,7 @@ public class GenericCategory implements ProfileViewerPage { setupItemStacks(hProfile, pProfile); } - private int calculateTier(int achieved, IntList requirements) { + private int calculateTier(long achieved, IntList requirements) { return (int) requirements.intStream().filter(req -> achieved >= req).count(); } @@ -75,13 +75,13 @@ public class GenericCategory implements ProfileViewerPage { itemStack.set(DataComponentTypes.CUSTOM_NAME, Text.literal(Formatting.strip(itemStack.getComponents().get(DataComponentTypes.CUSTOM_NAME).getString())).setStyle(style)); - int personalColl = playerCollection != null && playerCollection.has(collection) ? playerCollection.get(collection).getAsInt() : 0; + long personalColl = playerCollection != null && playerCollection.has(collection) ? playerCollection.get(collection).getAsLong() : 0; - int totalCollection = 0; + long totalCollection = 0; for (String member : hProfile.get("members").getAsJsonObject().keySet()) { if (!hProfile.getAsJsonObject("members").getAsJsonObject(member).has("collection")) continue; JsonObject memberColl = hProfile.getAsJsonObject("members").getAsJsonObject(member).getAsJsonObject("collection"); - totalCollection += memberColl.has(collection) ? memberColl.get(collection).getAsInt() : 0; + totalCollection += memberColl.has(collection) ? memberColl.get(collection).getAsLong() : 0; } int collectionTier = calculateTier(totalCollection, tierRequirementsMap.get(collection)); @@ -93,7 +93,7 @@ public class GenericCategory implements ProfileViewerPage { if (hProfile.get("members").getAsJsonObject().keySet().size() > 1) { lore.add(Text.literal("Personal: " + COMMA_FORMATTER.format(personalColl)).setStyle(style).formatted(Formatting.GOLD)); - lore.add(Text.literal("Co-op Collection: " + COMMA_FORMATTER.format(totalCollection-personalColl)).setStyle(style).formatted(Formatting.AQUA)); + lore.add(Text.literal("Co-op: " + COMMA_FORMATTER.format(totalCollection-personalColl)).setStyle(style).formatted(Formatting.AQUA)); } lore.add(Text.literal("Collection: " + COMMA_FORMATTER.format(totalCollection)).setStyle(style).formatted(Formatting.YELLOW)); diff --git a/src/main/java/de/hysky/skyblocker/skyblock/slayers/SlayerEntitiesGlow.java b/src/main/java/de/hysky/skyblocker/skyblock/slayers/SlayerEntitiesGlow.java index 79f91bfb..966f642e 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/slayers/SlayerEntitiesGlow.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/slayers/SlayerEntitiesGlow.java @@ -1,6 +1,10 @@ package de.hysky.skyblocker.skyblock.slayers; import de.hysky.skyblocker.utils.SlayerUtils; +import de.hysky.skyblocker.utils.render.RenderHelper; +import de.hysky.skyblocker.utils.scheduler.Scheduler; +import it.unimi.dsi.fastutil.objects.Object2BooleanOpenHashMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectMap; import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents; import net.minecraft.client.MinecraftClient; import net.minecraft.entity.Entity; @@ -12,6 +16,8 @@ import net.minecraft.util.math.Box; import org.jetbrains.annotations.Nullable; import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; public class SlayerEntitiesGlow { private static final Map SLAYER_MINI_NAMES = Map.ofEntries( @@ -47,6 +53,12 @@ public class SlayerEntitiesGlow { private static final Set MOBS_TO_GLOW = new HashSet<>(); + /** + * ARMORSTAND_TO_MOBS_TO_GLOW tracks if an armor stand already has an associated mob entity. This is used for trying to dedupe glows, + * where an armor stand has detected multiple candidates as its associated mob entity - in a vain attempt to reduce the amount of false positives + */ + private static final ConcurrentHashMap ARMORSTAND_TO_MOBS_TO_GLOW = new ConcurrentHashMap<>(); + public static boolean shouldGlow(UUID entityUUID) { return MOBS_TO_GLOW.contains(entityUUID); } @@ -80,13 +92,21 @@ public class SlayerEntitiesGlow { * @param armorStand the entity that contains the display name of the Slayer (mini)boss */ private static MobEntity findClosestMobEntity(Class entityClass, ArmorStandEntity armorStand) { - return armorStand.getWorld().getEntitiesByClass(entityClass, armorStand.getDimensions(null) - .getBoxAt(armorStand.getPos()).expand(1.5), entity -> - !entity.isDead() && entity.age > armorStand.age - 4 && entity.age < armorStand.age + 4) + List mobEntities = armorStand.getWorld().getEntitiesByClass(entityClass, armorStand.getDimensions(null) + .getBoxAt(armorStand.getPos()).expand(0.3f, 1.5f, 0.3f), entity -> !entity.isDead()) .stream() .filter(SlayerEntitiesGlow::isValidSlayerMob) - .min(Comparator.comparingDouble((MobEntity e) -> e.distanceTo(armorStand))) - .orElse(null); + .sorted(Comparator.comparingDouble(e -> e.squaredDistanceTo(armorStand))) + .collect(Collectors.toList()); + + return switch (mobEntities.size()) { + case 0 -> null; + case 1 -> mobEntities.getFirst(); + default -> mobEntities.stream() + .filter(entity -> entity.age > armorStand.age - 4 && entity.age < armorStand.age + 4) + .findFirst() + .orElse(mobEntities.getFirst()); + }; } /** @@ -107,16 +127,48 @@ public class SlayerEntitiesGlow { Class entityClass = SLAYER_MOB_TYPE.get(slayerType); if (entityClass != null) { MobEntity closestEntity = findClosestMobEntity(entityClass, armorStand); - if (closestEntity != null) MOBS_TO_GLOW.add(closestEntity.getUuid()); + if (closestEntity != null) { + UUID uuid = ARMORSTAND_TO_MOBS_TO_GLOW.putIfAbsent(armorStand.getUuid(), closestEntity.getUuid()); + if (uuid != null && closestEntity.getUuid() != uuid && closestEntity.age < 80) { + Scheduler.INSTANCE.schedule(() -> recalculateMobGlow(armorStand, entityClass, uuid), 30, true); + } + MOBS_TO_GLOW.add(closestEntity.getUuid()); + } + } + } + + /** + * This method attempts self-correct by finding the true slayer mob if there's 2 candidates + * @param armorStand the armor stand we know to be a slayer mob + * @param entityClass the java class of the entity we know the armor stand to belong to + * @param oldUUID the uuid of the first detected slayer mob + */ + private static void recalculateMobGlow(ArmorStandEntity armorStand, Class entityClass, UUID oldUUID) { + MobEntity entity = findClosestMobEntity(entityClass, armorStand); + if (entity.getUuid() != oldUUID) { + RenderHelper.runOnRenderThread(() -> { + MOBS_TO_GLOW.add(entity.getUuid()); + MOBS_TO_GLOW.remove(ARMORSTAND_TO_MOBS_TO_GLOW.put(armorStand.getUuid(), entity.getUuid())); + }); + } } public static void onEntityDeath(@Nullable Entity entity) { - if (entity != null && entity.getUuid() != null) MOBS_TO_GLOW.remove(entity.getUuid()); + if (entity != null && entity.getUuid() != null) { + MOBS_TO_GLOW.remove(entity.getUuid()); + } + } + + public static void cleanupArmorstand(@Nullable ArmorStandEntity entity) { + if (entity != null && entity.getUuid() != null) { + ARMORSTAND_TO_MOBS_TO_GLOW.remove(entity.getUuid()); + } } private static void clearGlow() { MOBS_TO_GLOW.clear(); + ARMORSTAND_TO_MOBS_TO_GLOW.clear(); } } \ No newline at end of file diff --git a/src/main/java/de/hysky/skyblocker/utils/SlayerUtils.java b/src/main/java/de/hysky/skyblocker/utils/SlayerUtils.java index 363de85a..8944eb3d 100644 --- a/src/main/java/de/hysky/skyblocker/utils/SlayerUtils.java +++ b/src/main/java/de/hysky/skyblocker/utils/SlayerUtils.java @@ -23,7 +23,7 @@ public class SlayerUtils { //TODO: Cache this, probably included in Packet system public static List getEntityArmorStands(Entity entity) { - return entity.getEntityWorld().getOtherEntities(entity, entity.getBoundingBox().expand(1F, 2.5F, 1F), x -> x instanceof ArmorStandEntity && x.hasCustomName()); + return entity.getEntityWorld().getOtherEntities(entity, entity.getBoundingBox().expand(0.3F, 2.5F, 0.3F), x -> x instanceof ArmorStandEntity && x.hasCustomName()); } //Eventually this should be modified so that if you hit a slayer boss all slayer features will work on that boss. @@ -64,9 +64,8 @@ public class SlayerUtils { boolean type = false; for (String line : Utils.STRING_SCOREBOARD) { switch (line) { - case String a when a.contains("Slayer Quest") -> { return false; } - case String b when b.contains("Slay the boss!") -> inFight = true; - case String c when c.contains(slayer) -> type = true; + case String a when a.contains("Slay the boss!") -> inFight = true; + case String b when b.contains(slayer) -> type = true; default -> { continue; } } if (inFight && type) return true; -- cgit