From cadce769205a999a019167350e5c9a98fcf19ca3 Mon Sep 17 00:00:00 2001 From: zyckk4 Date: Fri, 25 Sep 2026 05:49:01 +0800 Subject: [PATCH 1/2] Add KillAura option to ignore named mobs --- docs/wiki/Commands.md | 6 + .../zenith/command/impl/KillAuraCommand.java | 9 + .../java/com/zenith/module/impl/KillAura.java | 11 + .../java/com/zenith/util/config/Config.java | 1 + .../com/zenith/module/impl/KillAuraTest.java | 217 ++++++++++++++++++ 5 files changed, 244 insertions(+) create mode 100644 src/test/java/com/zenith/module/impl/KillAuraTest.java diff --git a/docs/wiki/Commands.md b/docs/wiki/Commands.md index 9431eac33..0a67fb7f5 100644 --- a/docs/wiki/Commands.md +++ b/docs/wiki/Commands.md @@ -1869,6 +1869,10 @@ Custom targets list: https://link.2b2t.vc/1 Aggressive mobs are mobs that are actively targeting and attacking the player. + + +ignoreNamedMobs skips non-player entities with a custom name, including custom targets. + **Aliases:** `ka` **Usage** @@ -1893,6 +1897,8 @@ Aggressive mobs are mobs that are actively targeting and attacking the player. ```killAura targetCustom add/del ``` + ```killAura ignoreNamedMobs on/off``` + ```killAura weaponSwitch on/off``` ```killAura weaponType ``` diff --git a/src/main/java/com/zenith/command/impl/KillAuraCommand.java b/src/main/java/com/zenith/command/impl/KillAuraCommand.java index a6b6314b0..6c4f63d15 100644 --- a/src/main/java/com/zenith/command/impl/KillAuraCommand.java +++ b/src/main/java/com/zenith/command/impl/KillAuraCommand.java @@ -34,6 +34,8 @@ public CommandUsage commandUsage() { Custom targets list: https://link.2b2t.vc/1 Aggressive mobs are mobs that are actively targeting and attacking the player. + + ignoreNamedMobs skips non-player entities with a custom name, including custom targets. """) .usageLines( "on/off", @@ -46,6 +48,7 @@ public CommandUsage commandUsage() { "targetNeutralMobs onlyAggressive on/off", "targetCustom on/off", "targetCustom add/del ", + "ignoreNamedMobs on/off", "weaponSwitch on/off", "weaponType ", "weaponMaterial ", @@ -103,6 +106,11 @@ public LiteralArgumentBuilder register() { c.getSource().getEmbed() .title("Target Neutral Mobs Only Aggressive " + toggleStrCaps(CONFIG.client.extra.killAura.onlyNeutralAggressive)); })))) + .then(literal("ignoreNamedMobs").then(argument("toggle", toggle()).executes(c -> { + CONFIG.client.extra.killAura.ignoreNamedMobs = getToggle(c, "toggle"); + c.getSource().getEmbed() + .title("Ignore Named Mobs " + toggleStrCaps(CONFIG.client.extra.killAura.ignoreNamedMobs)); + }))) .then(literal("weaponSwitch").then(argument("toggle", toggle()).executes(c -> { CONFIG.client.extra.killAura.switchWeapon = getToggle(c, "toggle"); c.getSource().getEmbed() @@ -165,6 +173,7 @@ public void defaultEmbed(Embed builder) { .addField("Target Hostile Mobs", toggleStr(CONFIG.client.extra.killAura.targetHostileMobs) + " [onlyAggressive: " + toggleStr(CONFIG.client.extra.killAura.onlyHostileAggressive) + "]") .addField("Target Neutral Mobs", toggleStr(CONFIG.client.extra.killAura.targetNeutralMobs) + " [onlyAggressive: " + toggleStr(CONFIG.client.extra.killAura.onlyNeutralAggressive) + "]") .addField("Target Custom", toggleStr(CONFIG.client.extra.killAura.targetCustom)) + .addField("Ignore Named Mobs", toggleStr(CONFIG.client.extra.killAura.ignoreNamedMobs)) .addField("Weapon Switching", toggleStr(CONFIG.client.extra.killAura.switchWeapon)) .addField("Weapon Type", CONFIG.client.extra.killAura.weaponType.name().toLowerCase()) .addField("Weapon Material", CONFIG.client.extra.killAura.weaponMaterial.name().toLowerCase()) diff --git a/src/main/java/com/zenith/module/impl/KillAura.java b/src/main/java/com/zenith/module/impl/KillAura.java index ff2667091..93e4c2c3d 100644 --- a/src/main/java/com/zenith/module/impl/KillAura.java +++ b/src/main/java/com/zenith/module/impl/KillAura.java @@ -23,6 +23,7 @@ import java.util.Comparator; import java.util.List; import java.util.Objects; +import java.util.Optional; import java.util.Set; import static com.github.rfresh2.EventConsumer.of; @@ -169,6 +170,7 @@ private boolean validTarget(EntityLiving entity) { && !PLAYER_LISTS.getSpectatorWhitelist().contains(player.getUuid()); } else if (entity instanceof EntityStandard e) { + if (CONFIG.client.extra.killAura.ignoreNamedMobs && hasCustomName(e)) return false; if (CONFIG.client.extra.killAura.targetCustom) { if (CONFIG.client.extra.killAura.customTargets.contains(e.getEntityType())) { return true; @@ -196,6 +198,15 @@ private boolean validTarget(EntityLiving entity) { return false; } + private static boolean hasCustomName(final EntityLiving entity) { + // Custom name is independent of the custom-name-visible flag at index 3. + var customName = entity.getMetadata().get(2); + return customName != null + && customName.getType() == MetadataTypes.OPTIONAL_CHAT + && customName.getValue() instanceof Optional name + && name.isPresent(); + } + private static boolean isAggressive(final EntityLiving entity) { // https://minecraft.wiki/w/Java_Edition_protocol/Entity_metadata#Mob var byteData = entity.getMetadataValue(15, MetadataTypes.BYTE, Byte.class); diff --git a/src/main/java/com/zenith/util/config/Config.java b/src/main/java/com/zenith/util/config/Config.java index 674b4978c..46cc2411c 100644 --- a/src/main/java/com/zenith/util/config/Config.java +++ b/src/main/java/com/zenith/util/config/Config.java @@ -398,6 +398,7 @@ public static final class KillAura { public boolean targetHostileMobs = true; public boolean targetNeutralMobs = false; public boolean targetCustom = false; + public boolean ignoreNamedMobs = false; public boolean onlyNeutralAggressive = false; public boolean onlyHostileAggressive = false; public boolean switchWeapon = true; diff --git a/src/test/java/com/zenith/module/impl/KillAuraTest.java b/src/test/java/com/zenith/module/impl/KillAuraTest.java new file mode 100644 index 000000000..c53798afc --- /dev/null +++ b/src/test/java/com/zenith/module/impl/KillAuraTest.java @@ -0,0 +1,217 @@ +package com.zenith.module.impl; + +import com.mojang.brigadier.CommandDispatcher; +import com.zenith.cache.data.entity.EntityLiving; +import com.zenith.cache.data.entity.EntityPlayer; +import com.zenith.cache.data.entity.EntityStandard; +import com.zenith.command.api.CommandContext; +import com.zenith.command.api.CommandSources; +import com.zenith.command.impl.KillAuraCommand; +import com.zenith.discord.Embed; +import com.zenith.plugin.DefaultGsonConfigSerializer; +import com.zenith.util.config.Config; +import net.kyori.adventure.text.Component; +import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.MetadataTypes; +import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata; +import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ByteEntityMetadata; +import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ObjectEntityMetadata; +import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.DisabledInNativeImage; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.junit.jupiter.params.provider.ValueSource; + +import java.io.StringReader; +import java.io.StringWriter; +import java.lang.reflect.Method; +import java.util.Optional; +import java.util.UUID; + +import static com.zenith.Globals.CONFIG; +import static com.zenith.Globals.GSON; +import static org.junit.jupiter.api.Assertions.*; + +@DisabledInNativeImage +public class KillAuraTest { + private Config.Client.Extra.KillAura previousSettings; + private KillAura killAura; + private Method validTarget; + + @BeforeEach + public void setUp() throws Exception { + previousSettings = GSON.fromJson(GSON.toJson(CONFIG.client.extra.killAura), Config.Client.Extra.KillAura.class); + applyTargetSettings(new Config.Client.Extra.KillAura()); + killAura = new KillAura(); + validTarget = KillAura.class.getDeclaredMethod("validTarget", EntityLiving.class); + validTarget.setAccessible(true); + } + + @AfterEach + public void tearDown() { + applyTargetSettings(previousSettings); + } + + @ParameterizedTest + @ValueSource(booleans = {false, true}) + public void namedHostileMobsAreIgnoredEvenWhenAggressive(boolean onlyAggressive) throws Exception { + CONFIG.client.extra.killAura.ignoreNamedMobs = true; + CONFIG.client.extra.killAura.onlyHostileAggressive = onlyAggressive; + var zombie = mob(EntityType.ZOMBIE); + makeAggressive(zombie); + assertTrue(validTarget(zombie)); + + name(zombie, Component.text("Keep me")); + assertFalse(validTarget(zombie)); + } + + @ParameterizedTest + @ValueSource(booleans = {false, true}) + public void namedNeutralMobsAreIgnoredEvenWhenAggressive(boolean onlyAggressive) throws Exception { + CONFIG.client.extra.killAura.ignoreNamedMobs = true; + CONFIG.client.extra.killAura.targetNeutralMobs = true; + CONFIG.client.extra.killAura.onlyNeutralAggressive = onlyAggressive; + var wolf = mob(EntityType.WOLF); + makeAggressive(wolf); + assertTrue(validTarget(wolf)); + + name(wolf, Component.text("Keep me")); + assertFalse(validTarget(wolf)); + } + + @ParameterizedTest + @EnumSource(value = EntityType.class, names = {"COW", "ARMOR_STAND"}) + public void customTargetsCannotOverrideNamedEntityProtection(EntityType type) throws Exception { + CONFIG.client.extra.killAura.ignoreNamedMobs = true; + CONFIG.client.extra.killAura.targetCustom = true; + CONFIG.client.extra.killAura.customTargets.add(type); + var entity = mob(type); + assertTrue(validTarget(entity)); + + name(entity, Component.text("Keep me")); + assertFalse(validTarget(entity)); + } + + @Test + public void disablingOptionPreservesExistingTargetCategories() throws Exception { + CONFIG.client.extra.killAura.targetNeutralMobs = true; + CONFIG.client.extra.killAura.targetCustom = true; + CONFIG.client.extra.killAura.customTargets.add(EntityType.COW); + for (var type : new EntityType[]{EntityType.ZOMBIE, EntityType.WOLF, EntityType.COW}) { + var entity = mob(type); + name(entity, Component.text("Keep me")); + assertTrue(validTarget(entity), type.name()); + CONFIG.client.extra.killAura.ignoreNamedMobs = true; + assertFalse(validTarget(entity), type.name()); + CONFIG.client.extra.killAura.ignoreNamedMobs = false; + assertTrue(validTarget(entity), type.name()); + } + } + + @Test + public void missingOrClearedCustomNameDoesNotProtectMob() throws Exception { + CONFIG.client.extra.killAura.ignoreNamedMobs = true; + var zombie = mob(EntityType.ZOMBIE); + assertTrue(validTarget(zombie)); + name(zombie, Component.text("Keep me")); + assertFalse(validTarget(zombie)); + + zombie.getMetadata().put(2, new ObjectEntityMetadata<>(2, MetadataTypes.OPTIONAL_CHAT, Optional.empty())); + assertTrue(validTarget(zombie)); + } + + @ParameterizedTest + @ValueSource(booleans = {false, true}) + public void nameVisibilityDoesNotDetermineProtection(boolean visible) throws Exception { + CONFIG.client.extra.killAura.ignoreNamedMobs = true; + var zombie = mob(EntityType.ZOMBIE); + zombie.getMetadata().put(3, new BooleanEntityMetadata(3, MetadataTypes.BOOLEAN, visible)); + assertTrue(validTarget(zombie)); + + name(zombie, Component.text("Keep me")); + assertFalse(validTarget(zombie)); + } + + @Test + public void presentEmptyComponentStillCountsAsCustomName() throws Exception { + CONFIG.client.extra.killAura.ignoreNamedMobs = true; + var zombie = mob(EntityType.ZOMBIE); + name(zombie, Component.empty()); + assertFalse(validTarget(zombie)); + } + + @Test + public void playerTargetingIsUnaffectedByCustomName() throws Exception { + CONFIG.client.extra.killAura.ignoreNamedMobs = true; + var player = new EntityPlayer(); + player.setUuid(UUID.randomUUID()); + name(player, Component.text("Keep me")); + assertFalse(validTarget(player)); + + CONFIG.client.extra.killAura.targetPlayers = true; + assertTrue(validTarget(player)); + player.setSelfPlayer(true); + assertFalse(validTarget(player)); + } + + @Test + public void newAndLegacyConfigsDefaultToExistingBehavior() { + assertFalse(new Config.Client.Extra.KillAura().ignoreNamedMobs); + var legacy = DefaultGsonConfigSerializer.INSTANCE.read(Config.Client.Extra.KillAura.class, new StringReader("{}")); + assertFalse(legacy.ignoreNamedMobs); + } + + @Test + public void commandTogglesOptionAndSettingSurvivesSerialization() throws Exception { + var command = new KillAuraCommand(); + var dispatcher = new CommandDispatcher(); + dispatcher.register(command.register()); + + assertEquals(1, dispatcher.execute("killaura ignoreNamedMobs on", CommandContext.create("killAura ignoreNamedMobs on", CommandSources.TERMINAL))); + assertTrue(CONFIG.client.extra.killAura.ignoreNamedMobs); + var output = new StringWriter(); + DefaultGsonConfigSerializer.INSTANCE.write(CONFIG.client.extra.killAura, output); + var restored = DefaultGsonConfigSerializer.INSTANCE.read(Config.Client.Extra.KillAura.class, new StringReader(output.toString())); + assertTrue(restored.ignoreNamedMobs); + var embed = new Embed(); + command.defaultEmbed(embed); + assertTrue(embed.fields().stream().anyMatch(field -> field.name().equals("Ignore Named Mobs") && field.value().equals("on"))); + + assertEquals(1, dispatcher.execute("killaura ignoreNamedMobs off", CommandContext.create("killAura ignoreNamedMobs off", CommandSources.TERMINAL))); + assertFalse(CONFIG.client.extra.killAura.ignoreNamedMobs); + } + + private boolean validTarget(EntityLiving entity) throws Exception { + return (boolean) validTarget.invoke(killAura, entity); + } + + private static EntityStandard mob(EntityType type) { + var entity = new EntityStandard(); + entity.setEntityType(type); + return entity; + } + + private static void name(EntityLiving entity, Component name) { + entity.getMetadata().put(2, new ObjectEntityMetadata<>(2, MetadataTypes.OPTIONAL_CHAT, Optional.of(name))); + } + + private static void makeAggressive(EntityLiving entity) { + entity.getMetadata().put(15, new ByteEntityMetadata(15, MetadataTypes.BYTE, (byte) 0x04)); + } + + private static void applyTargetSettings(Config.Client.Extra.KillAura settings) { + var config = CONFIG.client.extra.killAura; + config.targetPlayers = settings.targetPlayers; + config.targetHostileMobs = settings.targetHostileMobs; + config.targetNeutralMobs = settings.targetNeutralMobs; + config.targetCustom = settings.targetCustom; + config.targetArmorStands = settings.targetArmorStands; + config.onlyHostileAggressive = settings.onlyHostileAggressive; + config.onlyNeutralAggressive = settings.onlyNeutralAggressive; + config.ignoreNamedMobs = settings.ignoreNamedMobs; + config.customTargets.clear(); + config.customTargets.addAll(settings.customTargets); + } +} From da8afb33ca4592ea361ca72e3c043b1882b97547 Mon Sep 17 00:00:00 2001 From: rfresh2 <89827146+rfresh2@users.noreply.github.com> Date: Thu, 24 Sep 2026 17:36:58 -0700 Subject: [PATCH 2/2] remove unnecessary stuff and update metadata accessor --- docs/wiki/Commands.md | 4 - .../com/zenith/cache/data/entity/Entity.java | 10 +- .../zenith/command/impl/KillAuraCommand.java | 2 - .../java/com/zenith/module/impl/KillAura.java | 9 +- .../com/zenith/module/impl/KillAuraTest.java | 217 ------------------ 5 files changed, 10 insertions(+), 232 deletions(-) delete mode 100644 src/test/java/com/zenith/module/impl/KillAuraTest.java diff --git a/docs/wiki/Commands.md b/docs/wiki/Commands.md index 0a67fb7f5..e0a3fb335 100644 --- a/docs/wiki/Commands.md +++ b/docs/wiki/Commands.md @@ -1869,10 +1869,6 @@ Custom targets list: https://link.2b2t.vc/1 Aggressive mobs are mobs that are actively targeting and attacking the player. - - -ignoreNamedMobs skips non-player entities with a custom name, including custom targets. - **Aliases:** `ka` **Usage** diff --git a/src/main/java/com/zenith/cache/data/entity/Entity.java b/src/main/java/com/zenith/cache/data/entity/Entity.java index eef8cb781..124e1b3ae 100644 --- a/src/main/java/com/zenith/cache/data/entity/Entity.java +++ b/src/main/java/com/zenith/cache/data/entity/Entity.java @@ -108,6 +108,10 @@ public double distanceSqTo(Entity entity) { } public @Nullable T getMetadataValue(int index, MetadataType metadataType, Class valueClass) { + return getMetadataValue(index, metadataType); + } + + public @Nullable T getMetadataValue(int index, MetadataType metadataType) { var metadata = this.metadata.get(index); if (metadata == null) return null; if (metadata.getType() == metadataType) { @@ -115,8 +119,10 @@ public double distanceSqTo(Entity entity) { if (metadataValue == null) { return null; } - if (valueClass.isInstance(metadataValue)) { - return valueClass.cast(metadata.getValue()); + try { + return (T) metadataValue; + } catch (ClassCastException e) { + return null; } } return null; diff --git a/src/main/java/com/zenith/command/impl/KillAuraCommand.java b/src/main/java/com/zenith/command/impl/KillAuraCommand.java index 6c4f63d15..1d8b891a4 100644 --- a/src/main/java/com/zenith/command/impl/KillAuraCommand.java +++ b/src/main/java/com/zenith/command/impl/KillAuraCommand.java @@ -34,8 +34,6 @@ public CommandUsage commandUsage() { Custom targets list: https://link.2b2t.vc/1 Aggressive mobs are mobs that are actively targeting and attacking the player. - - ignoreNamedMobs skips non-player entities with a custom name, including custom targets. """) .usageLines( "on/off", diff --git a/src/main/java/com/zenith/module/impl/KillAura.java b/src/main/java/com/zenith/module/impl/KillAura.java index 93e4c2c3d..a3215c29e 100644 --- a/src/main/java/com/zenith/module/impl/KillAura.java +++ b/src/main/java/com/zenith/module/impl/KillAura.java @@ -23,7 +23,6 @@ import java.util.Comparator; import java.util.List; import java.util.Objects; -import java.util.Optional; import java.util.Set; import static com.github.rfresh2.EventConsumer.of; @@ -199,12 +198,8 @@ private boolean validTarget(EntityLiving entity) { } private static boolean hasCustomName(final EntityLiving entity) { - // Custom name is independent of the custom-name-visible flag at index 3. - var customName = entity.getMetadata().get(2); - return customName != null - && customName.getType() == MetadataTypes.OPTIONAL_CHAT - && customName.getValue() instanceof Optional name - && name.isPresent(); + var customName = entity.getMetadataValue(2, MetadataTypes.OPTIONAL_CHAT); + return customName != null && customName.isPresent(); } private static boolean isAggressive(final EntityLiving entity) { diff --git a/src/test/java/com/zenith/module/impl/KillAuraTest.java b/src/test/java/com/zenith/module/impl/KillAuraTest.java deleted file mode 100644 index c53798afc..000000000 --- a/src/test/java/com/zenith/module/impl/KillAuraTest.java +++ /dev/null @@ -1,217 +0,0 @@ -package com.zenith.module.impl; - -import com.mojang.brigadier.CommandDispatcher; -import com.zenith.cache.data.entity.EntityLiving; -import com.zenith.cache.data.entity.EntityPlayer; -import com.zenith.cache.data.entity.EntityStandard; -import com.zenith.command.api.CommandContext; -import com.zenith.command.api.CommandSources; -import com.zenith.command.impl.KillAuraCommand; -import com.zenith.discord.Embed; -import com.zenith.plugin.DefaultGsonConfigSerializer; -import com.zenith.util.config.Config; -import net.kyori.adventure.text.Component; -import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.MetadataTypes; -import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.BooleanEntityMetadata; -import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ByteEntityMetadata; -import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.ObjectEntityMetadata; -import org.geysermc.mcprotocollib.protocol.data.game.entity.type.EntityType; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.condition.DisabledInNativeImage; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.EnumSource; -import org.junit.jupiter.params.provider.ValueSource; - -import java.io.StringReader; -import java.io.StringWriter; -import java.lang.reflect.Method; -import java.util.Optional; -import java.util.UUID; - -import static com.zenith.Globals.CONFIG; -import static com.zenith.Globals.GSON; -import static org.junit.jupiter.api.Assertions.*; - -@DisabledInNativeImage -public class KillAuraTest { - private Config.Client.Extra.KillAura previousSettings; - private KillAura killAura; - private Method validTarget; - - @BeforeEach - public void setUp() throws Exception { - previousSettings = GSON.fromJson(GSON.toJson(CONFIG.client.extra.killAura), Config.Client.Extra.KillAura.class); - applyTargetSettings(new Config.Client.Extra.KillAura()); - killAura = new KillAura(); - validTarget = KillAura.class.getDeclaredMethod("validTarget", EntityLiving.class); - validTarget.setAccessible(true); - } - - @AfterEach - public void tearDown() { - applyTargetSettings(previousSettings); - } - - @ParameterizedTest - @ValueSource(booleans = {false, true}) - public void namedHostileMobsAreIgnoredEvenWhenAggressive(boolean onlyAggressive) throws Exception { - CONFIG.client.extra.killAura.ignoreNamedMobs = true; - CONFIG.client.extra.killAura.onlyHostileAggressive = onlyAggressive; - var zombie = mob(EntityType.ZOMBIE); - makeAggressive(zombie); - assertTrue(validTarget(zombie)); - - name(zombie, Component.text("Keep me")); - assertFalse(validTarget(zombie)); - } - - @ParameterizedTest - @ValueSource(booleans = {false, true}) - public void namedNeutralMobsAreIgnoredEvenWhenAggressive(boolean onlyAggressive) throws Exception { - CONFIG.client.extra.killAura.ignoreNamedMobs = true; - CONFIG.client.extra.killAura.targetNeutralMobs = true; - CONFIG.client.extra.killAura.onlyNeutralAggressive = onlyAggressive; - var wolf = mob(EntityType.WOLF); - makeAggressive(wolf); - assertTrue(validTarget(wolf)); - - name(wolf, Component.text("Keep me")); - assertFalse(validTarget(wolf)); - } - - @ParameterizedTest - @EnumSource(value = EntityType.class, names = {"COW", "ARMOR_STAND"}) - public void customTargetsCannotOverrideNamedEntityProtection(EntityType type) throws Exception { - CONFIG.client.extra.killAura.ignoreNamedMobs = true; - CONFIG.client.extra.killAura.targetCustom = true; - CONFIG.client.extra.killAura.customTargets.add(type); - var entity = mob(type); - assertTrue(validTarget(entity)); - - name(entity, Component.text("Keep me")); - assertFalse(validTarget(entity)); - } - - @Test - public void disablingOptionPreservesExistingTargetCategories() throws Exception { - CONFIG.client.extra.killAura.targetNeutralMobs = true; - CONFIG.client.extra.killAura.targetCustom = true; - CONFIG.client.extra.killAura.customTargets.add(EntityType.COW); - for (var type : new EntityType[]{EntityType.ZOMBIE, EntityType.WOLF, EntityType.COW}) { - var entity = mob(type); - name(entity, Component.text("Keep me")); - assertTrue(validTarget(entity), type.name()); - CONFIG.client.extra.killAura.ignoreNamedMobs = true; - assertFalse(validTarget(entity), type.name()); - CONFIG.client.extra.killAura.ignoreNamedMobs = false; - assertTrue(validTarget(entity), type.name()); - } - } - - @Test - public void missingOrClearedCustomNameDoesNotProtectMob() throws Exception { - CONFIG.client.extra.killAura.ignoreNamedMobs = true; - var zombie = mob(EntityType.ZOMBIE); - assertTrue(validTarget(zombie)); - name(zombie, Component.text("Keep me")); - assertFalse(validTarget(zombie)); - - zombie.getMetadata().put(2, new ObjectEntityMetadata<>(2, MetadataTypes.OPTIONAL_CHAT, Optional.empty())); - assertTrue(validTarget(zombie)); - } - - @ParameterizedTest - @ValueSource(booleans = {false, true}) - public void nameVisibilityDoesNotDetermineProtection(boolean visible) throws Exception { - CONFIG.client.extra.killAura.ignoreNamedMobs = true; - var zombie = mob(EntityType.ZOMBIE); - zombie.getMetadata().put(3, new BooleanEntityMetadata(3, MetadataTypes.BOOLEAN, visible)); - assertTrue(validTarget(zombie)); - - name(zombie, Component.text("Keep me")); - assertFalse(validTarget(zombie)); - } - - @Test - public void presentEmptyComponentStillCountsAsCustomName() throws Exception { - CONFIG.client.extra.killAura.ignoreNamedMobs = true; - var zombie = mob(EntityType.ZOMBIE); - name(zombie, Component.empty()); - assertFalse(validTarget(zombie)); - } - - @Test - public void playerTargetingIsUnaffectedByCustomName() throws Exception { - CONFIG.client.extra.killAura.ignoreNamedMobs = true; - var player = new EntityPlayer(); - player.setUuid(UUID.randomUUID()); - name(player, Component.text("Keep me")); - assertFalse(validTarget(player)); - - CONFIG.client.extra.killAura.targetPlayers = true; - assertTrue(validTarget(player)); - player.setSelfPlayer(true); - assertFalse(validTarget(player)); - } - - @Test - public void newAndLegacyConfigsDefaultToExistingBehavior() { - assertFalse(new Config.Client.Extra.KillAura().ignoreNamedMobs); - var legacy = DefaultGsonConfigSerializer.INSTANCE.read(Config.Client.Extra.KillAura.class, new StringReader("{}")); - assertFalse(legacy.ignoreNamedMobs); - } - - @Test - public void commandTogglesOptionAndSettingSurvivesSerialization() throws Exception { - var command = new KillAuraCommand(); - var dispatcher = new CommandDispatcher(); - dispatcher.register(command.register()); - - assertEquals(1, dispatcher.execute("killaura ignoreNamedMobs on", CommandContext.create("killAura ignoreNamedMobs on", CommandSources.TERMINAL))); - assertTrue(CONFIG.client.extra.killAura.ignoreNamedMobs); - var output = new StringWriter(); - DefaultGsonConfigSerializer.INSTANCE.write(CONFIG.client.extra.killAura, output); - var restored = DefaultGsonConfigSerializer.INSTANCE.read(Config.Client.Extra.KillAura.class, new StringReader(output.toString())); - assertTrue(restored.ignoreNamedMobs); - var embed = new Embed(); - command.defaultEmbed(embed); - assertTrue(embed.fields().stream().anyMatch(field -> field.name().equals("Ignore Named Mobs") && field.value().equals("on"))); - - assertEquals(1, dispatcher.execute("killaura ignoreNamedMobs off", CommandContext.create("killAura ignoreNamedMobs off", CommandSources.TERMINAL))); - assertFalse(CONFIG.client.extra.killAura.ignoreNamedMobs); - } - - private boolean validTarget(EntityLiving entity) throws Exception { - return (boolean) validTarget.invoke(killAura, entity); - } - - private static EntityStandard mob(EntityType type) { - var entity = new EntityStandard(); - entity.setEntityType(type); - return entity; - } - - private static void name(EntityLiving entity, Component name) { - entity.getMetadata().put(2, new ObjectEntityMetadata<>(2, MetadataTypes.OPTIONAL_CHAT, Optional.of(name))); - } - - private static void makeAggressive(EntityLiving entity) { - entity.getMetadata().put(15, new ByteEntityMetadata(15, MetadataTypes.BYTE, (byte) 0x04)); - } - - private static void applyTargetSettings(Config.Client.Extra.KillAura settings) { - var config = CONFIG.client.extra.killAura; - config.targetPlayers = settings.targetPlayers; - config.targetHostileMobs = settings.targetHostileMobs; - config.targetNeutralMobs = settings.targetNeutralMobs; - config.targetCustom = settings.targetCustom; - config.targetArmorStands = settings.targetArmorStands; - config.onlyHostileAggressive = settings.onlyHostileAggressive; - config.onlyNeutralAggressive = settings.onlyNeutralAggressive; - config.ignoreNamedMobs = settings.ignoreNamedMobs; - config.customTargets.clear(); - config.customTargets.addAll(settings.customTargets); - } -}