From 9938d8eed8f39d663dcefcb34c8538418153a77c Mon Sep 17 00:00:00 2001 From: MochaMilkie Date: Fri, 6 Nov 2026 02:11:27 -0600 Subject: [PATCH 01/10] Add event listener and logic for phantoms spawning and targeting a vanished player. --- .../earth2me/essentials/EssentialsEntityListener.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Essentials/src/main/java/com/earth2me/essentials/EssentialsEntityListener.java b/Essentials/src/main/java/com/earth2me/essentials/EssentialsEntityListener.java index 1d1fbff1ee..a7361bc950 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/EssentialsEntityListener.java +++ b/Essentials/src/main/java/com/earth2me/essentials/EssentialsEntityListener.java @@ -272,4 +272,14 @@ public void onEntityTarget(final EntityTargetEvent event) { } } } + @EventHandler(priority = EventPriority.HIGHEST) + public void onPhantomSpawn(final EntitySpawnEvent event) { + if (event.getEntity() instanceof Phantom) { + final Phantom phantom = (Phantom) event.getEntity(); + final User user = ess.getUser((Player) phantom.getTarget()); + if (user.isVanished()) { + event.setCancelled(true); + } + } + } } From 2c480e029c35840536deb22fdc3c94db91635af6 Mon Sep 17 00:00:00 2001 From: MochaMilkie Date: Fri, 6 Nov 2026 02:12:11 -0600 Subject: [PATCH 02/10] Add event listener for vanish status change and store the users time until last slept when entering vanish, and restore it when leaving vanish --- .../essentials/EssentialsPlayerListener.java | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/Essentials/src/main/java/com/earth2me/essentials/EssentialsPlayerListener.java b/Essentials/src/main/java/com/earth2me/essentials/EssentialsPlayerListener.java index 60d3b4c25f..c50977b225 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/EssentialsPlayerListener.java +++ b/Essentials/src/main/java/com/earth2me/essentials/EssentialsPlayerListener.java @@ -21,6 +21,7 @@ import io.papermc.paper.event.player.PlayerServerFullCheckEvent; import net.ess3.api.IEssentials; import net.ess3.api.events.AfkStatusChangeEvent; +import net.ess3.api.events.VanishStatusChangeEvent; import net.ess3.provider.CommandSendListenerProvider; import net.ess3.provider.FormattedCommandAliasProvider; import net.ess3.provider.InventoryViewProvider; @@ -30,12 +31,7 @@ import net.ess3.provider.providers.PaperCommandSendListenerProvider; import net.essentialsx.PaperAdventureSmuggler; import net.essentialsx.api.v2.events.AsyncUserDataLoadEvent; -import org.bukkit.BanEntry; -import org.bukkit.BanList; -import org.bukkit.GameMode; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.World; +import org.bukkit.*; import org.bukkit.command.Command; import org.bukkit.command.FormattedCommandAlias; import org.bukkit.command.PluginCommand; @@ -70,6 +66,7 @@ import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.PlayerInventory; +import org.eclipse.sisu.Priority; import java.io.IOException; import java.lang.management.ManagementFactory; @@ -147,6 +144,7 @@ private static boolean isGameEventEvent() { public void registerEvents() { ess.getServer().getPluginManager().registerEvents(this, ess); + ess.getServer().getPluginManager().registerEvents(new VanishListener(), ess); if (isArrowPickupEvent()) { ess.getServer().getPluginManager().registerEvents(new ArrowPickupListener(), ess); @@ -1285,4 +1283,17 @@ private boolean isEssentialsCommand(final String label) { && (ess.getSettings().isCommandOverridden(label) || ess.getAlternativeCommandsHandler().getAlternative(label) == null); } } + private class VanishListener implements Listener { + + @EventHandler(priority = EventPriority.LOWEST) + private void vanishEvent(VanishStatusChangeEvent event) { + User user = ess.getUser(event.getAffected().getUUID()); + if(event.getValue()){ + user.setLastSleptBeforeVanish(user.getBase().getStatistic(Statistic.TIME_SINCE_REST)); + return; + } + user.getBase().setStatistic(Statistic.TIME_SINCE_REST, user.getLastSleptBeforeVanish()); + } + + } } From c7aa1fb9199d64a4d5aca12def024a0761318d26 Mon Sep 17 00:00:00 2001 From: MochaMilkie Date: Fri, 6 Nov 2026 02:12:31 -0600 Subject: [PATCH 03/10] Add variable for lastSleptBeforeVanish and it's setter and getter --- .../src/main/java/com/earth2me/essentials/User.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Essentials/src/main/java/com/earth2me/essentials/User.java b/Essentials/src/main/java/com/earth2me/essentials/User.java index ab0281910e..0181127756 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/User.java +++ b/Essentials/src/main/java/com/earth2me/essentials/User.java @@ -103,6 +103,7 @@ public class User extends UserData implements Comparable, IMessageRecipien private transient int flightTick = -1; private String lastLocaleString; private Locale playerLocale; + private int lastSleptBeforeVanish; public User(final Player base, final IEssentials ess) { super(base, ess); @@ -1376,4 +1377,11 @@ public int getFlightTick() { public void setFlightTick(int flightTick) { this.flightTick = flightTick; } + + public void setLastSleptBeforeVanish(int lastSleptBeforeVanish) { + this.lastSleptBeforeVanish = lastSleptBeforeVanish; + } + public int getLastSleptBeforeVanish() { + return this.lastSleptBeforeVanish; + } } From 52205ed7675946948356439a52d1be7ae79f9ae6 Mon Sep 17 00:00:00 2001 From: MochaMilkie Date: Fri, 6 Nov 2026 02:12:47 -0600 Subject: [PATCH 04/10] Added vanish listener tests --- .../essentials/VanishListenerTest.java | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 Essentials/src/test/java/com/earth2me/essentials/VanishListenerTest.java diff --git a/Essentials/src/test/java/com/earth2me/essentials/VanishListenerTest.java b/Essentials/src/test/java/com/earth2me/essentials/VanishListenerTest.java new file mode 100644 index 0000000000..22bc140945 --- /dev/null +++ b/Essentials/src/test/java/com/earth2me/essentials/VanishListenerTest.java @@ -0,0 +1,71 @@ +package com.earth2me.essentials; + +import net.ess3.api.IUser; +import net.ess3.api.events.VanishStatusChangeEvent; +import org.bukkit.Statistic; +import org.bukkit.event.entity.PlayerLeashEntityEvent; +import org.bukkit.event.player.PlayerBedEnterEvent; +import org.bukkit.event.player.PlayerBedLeaveEvent; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockbukkit.mockbukkit.MockBukkit; +import org.mockbukkit.mockbukkit.ServerMock; +import org.mockbukkit.mockbukkit.entity.PlayerMock; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +public class VanishListenerTest { + + private ServerMock server; + private Essentials ess; + private PlayerMock player; + private User user; + private IUser vanishedUser; + + @BeforeEach + public void setUp() { + this.server = MockBukkit.mock(); + Essentials.TESTING = true; + ess = MockBukkit.load(Essentials.class); + player = server.addPlayer("testPlayer"); + ess.getUser(player); + user = ess.getUser(player); + assertNotNull(user); + } + + @AfterEach + public void tearDown() { + MockBukkit.unmock(); + } + + @Test + public void shouldSaveTimeSinceRestWhenEnteringVanish() { + player.setStatistic(Statistic.TIME_SINCE_REST, 1200); + VanishStatusChangeEvent event = new VanishStatusChangeEvent(user, user, true); + assertEquals(user, event.getAffected()); + server.getPluginManager().callEvent(event); + assertEquals(1200, user.getLastSleptBeforeVanish()); + } + + @Test + public void shouldRestoreTimeSinceRestWhenLeavingVanish() { + player.setStatistic(Statistic.TIME_SINCE_REST, 1200); + server.getPluginManager().callEvent(new VanishStatusChangeEvent(user, user, true)); + player.setStatistic(Statistic.TIME_SINCE_REST, 8000); + server.getPluginManager().callEvent(new VanishStatusChangeEvent(user, user, false)); + assertEquals(1200, player.getStatistic(Statistic.TIME_SINCE_REST)); + } + + @Test + public void shouldPreserveTimeSinceRestWhenSleepingWhileVanished() { + player.setStatistic(Statistic.TIME_SINCE_REST, 1200); + server.getPluginManager().callEvent(new VanishStatusChangeEvent(user, user, true)); + server.getPluginManager().callEvent(new PlayerBedEnterEvent(player, player.getLocation().getBlock(), PlayerBedEnterEvent.BedEnterResult.OK)); + server.getScheduler().performTicks(1500); + server.getPluginManager().callEvent(new PlayerBedLeaveEvent(player, player.getLocation().getBlock(), true)); + server.getPluginManager().callEvent(new VanishStatusChangeEvent(user, user, false)); + assertEquals(1200, player.getStatistic(Statistic.TIME_SINCE_REST)); + } +} \ No newline at end of file From 5f7effc6c1dd22628e487ec431141a07da031a5f Mon Sep 17 00:00:00 2001 From: MochaMilkie Date: Mon, 9 Nov 2026 04:41:00 -0600 Subject: [PATCH 05/10] Check if incoming message is the same as last message --- .../essentials/chat/processing/AntiSpam.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 EssentialsChat/src/main/java/com/earth2me/essentials/chat/processing/AntiSpam.java diff --git a/EssentialsChat/src/main/java/com/earth2me/essentials/chat/processing/AntiSpam.java b/EssentialsChat/src/main/java/com/earth2me/essentials/chat/processing/AntiSpam.java new file mode 100644 index 0000000000..9f5985d4ed --- /dev/null +++ b/EssentialsChat/src/main/java/com/earth2me/essentials/chat/processing/AntiSpam.java @@ -0,0 +1,18 @@ +package com.earth2me.essentials.chat.processing; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +public class AntiSpam { + //Needs a listener, last message cache, compare to last message, + Map lastMessage = new HashMap<>(); + public boolean newMessage(UUID uuid, String message){ + String storedMessage = lastMessage.get(uuid); + if(storedMessage == message){ + return true; + } + lastMessage.put(uuid, message); + return false; + } +} From c66c216aa7ec9423fc9d931f75b65b5f9a5345ff Mon Sep 17 00:00:00 2001 From: MochaMilkie Date: Mon, 9 Nov 2026 04:45:32 -0600 Subject: [PATCH 06/10] Add antispam to highest chat listner --- .../earth2me/essentials/chat/processing/PaperChatHandler.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/EssentialsChat/src/main/java/com/earth2me/essentials/chat/processing/PaperChatHandler.java b/EssentialsChat/src/main/java/com/earth2me/essentials/chat/processing/PaperChatHandler.java index 989816f069..daae111fe6 100644 --- a/EssentialsChat/src/main/java/com/earth2me/essentials/chat/processing/PaperChatHandler.java +++ b/EssentialsChat/src/main/java/com/earth2me/essentials/chat/processing/PaperChatHandler.java @@ -10,10 +10,11 @@ public class PaperChatHandler extends AbstractChatHandler { public PaperChatHandler(Essentials ess, EssentialsChat essChat) { super(ess, essChat); } - + AntiSpam antiSpam = new AntiSpam(); public void registerListeners() { final PluginManager pm = essChat.getServer().getPluginManager(); pm.registerEvents(new ChatListener(), essChat); + antiSpam = new AntiSpam(); } public final class ChatListener extends PaperChatListenerProvider { @@ -31,6 +32,7 @@ public void onChatNormal(AbstractChatEvent event) { public void onChatHighest(AbstractChatEvent event) { handleChatPostFormat(event); handleChatSubmit(event); + antiSpam.newMessage(event.getPlayer().getUniqueId(), event.getMessage()); } } } From 37d503ba465a4bfdfe757328fb2ad72fe9bcf1ad Mon Sep 17 00:00:00 2001 From: MochaMilkie Date: Mon, 9 Nov 2026 04:47:29 -0600 Subject: [PATCH 07/10] Check for message matching just with more length --- .../java/com/earth2me/essentials/chat/processing/AntiSpam.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EssentialsChat/src/main/java/com/earth2me/essentials/chat/processing/AntiSpam.java b/EssentialsChat/src/main/java/com/earth2me/essentials/chat/processing/AntiSpam.java index 9f5985d4ed..fa952ff2e4 100644 --- a/EssentialsChat/src/main/java/com/earth2me/essentials/chat/processing/AntiSpam.java +++ b/EssentialsChat/src/main/java/com/earth2me/essentials/chat/processing/AntiSpam.java @@ -9,7 +9,7 @@ public class AntiSpam { Map lastMessage = new HashMap<>(); public boolean newMessage(UUID uuid, String message){ String storedMessage = lastMessage.get(uuid); - if(storedMessage == message){ + if(storedMessage.equalsIgnoreCase(message) || storedMessage.startsWith(message)) { return true; } lastMessage.put(uuid, message); From 86b7693d6345e4427ee00a64102ade6c58c98eaf Mon Sep 17 00:00:00 2001 From: MochaMilkie Date: Mon, 9 Nov 2026 05:04:31 -0600 Subject: [PATCH 08/10] Add getAntiSpam returns the antispam instance --- .../com/earth2me/essentials/chat/processing/AntiSpam.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/EssentialsChat/src/main/java/com/earth2me/essentials/chat/processing/AntiSpam.java b/EssentialsChat/src/main/java/com/earth2me/essentials/chat/processing/AntiSpam.java index fa952ff2e4..71ffd9e16d 100644 --- a/EssentialsChat/src/main/java/com/earth2me/essentials/chat/processing/AntiSpam.java +++ b/EssentialsChat/src/main/java/com/earth2me/essentials/chat/processing/AntiSpam.java @@ -5,6 +5,10 @@ import java.util.UUID; public class AntiSpam { + static AntiSpam antiSpam; + public AntiSpam() { + antiSpam = this; + } //Needs a listener, last message cache, compare to last message, Map lastMessage = new HashMap<>(); public boolean newMessage(UUID uuid, String message){ @@ -15,4 +19,7 @@ public boolean newMessage(UUID uuid, String message){ lastMessage.put(uuid, message); return false; } + public static AntiSpam getAntiSpam(){ + return antiSpam; + } } From 6acddad54c350d02f077ddb528a88cce5b55ecc0 Mon Sep 17 00:00:00 2001 From: MochaMilkie Date: Mon, 9 Nov 2026 05:05:07 -0600 Subject: [PATCH 09/10] Added antispam to chat events. Added to highest priority where applicable --- .../java/com/earth2me/essentials/chat/EssentialsChat.java | 2 ++ .../com/earth2me/essentials/chat/processing/ChatHandler.java | 3 +++ .../essentials/chat/processing/PaperChatHandler.java | 5 ++--- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/EssentialsChat/src/main/java/com/earth2me/essentials/chat/EssentialsChat.java b/EssentialsChat/src/main/java/com/earth2me/essentials/chat/EssentialsChat.java index f7db3796d2..d734310353 100644 --- a/EssentialsChat/src/main/java/com/earth2me/essentials/chat/EssentialsChat.java +++ b/EssentialsChat/src/main/java/com/earth2me/essentials/chat/EssentialsChat.java @@ -2,6 +2,7 @@ import com.earth2me.essentials.Essentials; import com.earth2me.essentials.EssentialsLogger; +import com.earth2me.essentials.chat.processing.AntiSpam; import com.earth2me.essentials.chat.processing.ChatHandler; import com.earth2me.essentials.chat.processing.PaperChatHandler; import com.earth2me.essentials.metrics.MetricsWrapper; @@ -19,6 +20,7 @@ public class EssentialsChat extends JavaPlugin { private transient IEssentials ess; private transient MetricsWrapper metrics = null; + public transient AntiSpam antiSpam = new AntiSpam(); @Override public void onEnable() { diff --git a/EssentialsChat/src/main/java/com/earth2me/essentials/chat/processing/ChatHandler.java b/EssentialsChat/src/main/java/com/earth2me/essentials/chat/processing/ChatHandler.java index eb53654525..ba2a6faaee 100644 --- a/EssentialsChat/src/main/java/com/earth2me/essentials/chat/processing/ChatHandler.java +++ b/EssentialsChat/src/main/java/com/earth2me/essentials/chat/processing/ChatHandler.java @@ -47,6 +47,9 @@ public void onPlayerChat(AsyncPlayerChatEvent event) { final AbstractChatEvent absEvent = wrap(event); handleChatPostFormat(absEvent); handleChatSubmit(absEvent); + + event.setCancelled(AntiSpam.getAntiSpam().newMessage(event.getPlayer().getUniqueId(), + event.getMessage())); } } } diff --git a/EssentialsChat/src/main/java/com/earth2me/essentials/chat/processing/PaperChatHandler.java b/EssentialsChat/src/main/java/com/earth2me/essentials/chat/processing/PaperChatHandler.java index daae111fe6..a2b155bd85 100644 --- a/EssentialsChat/src/main/java/com/earth2me/essentials/chat/processing/PaperChatHandler.java +++ b/EssentialsChat/src/main/java/com/earth2me/essentials/chat/processing/PaperChatHandler.java @@ -10,11 +10,9 @@ public class PaperChatHandler extends AbstractChatHandler { public PaperChatHandler(Essentials ess, EssentialsChat essChat) { super(ess, essChat); } - AntiSpam antiSpam = new AntiSpam(); public void registerListeners() { final PluginManager pm = essChat.getServer().getPluginManager(); pm.registerEvents(new ChatListener(), essChat); - antiSpam = new AntiSpam(); } public final class ChatListener extends PaperChatListenerProvider { @@ -32,7 +30,8 @@ public void onChatNormal(AbstractChatEvent event) { public void onChatHighest(AbstractChatEvent event) { handleChatPostFormat(event); handleChatSubmit(event); - antiSpam.newMessage(event.getPlayer().getUniqueId(), event.getMessage()); + event.setCancelled(AntiSpam.getAntiSpam().newMessage(event.getPlayer().getUniqueId(), + event.getMessage())); } } } From cff99f181e852d74faea9b664e4d48592d77df4b Mon Sep 17 00:00:00 2001 From: MochaMilkie Date: Mon, 9 Nov 2026 05:05:10 -0600 Subject: [PATCH 10/10] Added antispam to chat events. Added to highest priority where applicable --- .../earth2me/essentials/chat/processing/SpigotChatEvent.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/EssentialsChat/src/main/java/com/earth2me/essentials/chat/processing/SpigotChatEvent.java b/EssentialsChat/src/main/java/com/earth2me/essentials/chat/processing/SpigotChatEvent.java index 04ee58f376..6006710703 100644 --- a/EssentialsChat/src/main/java/com/earth2me/essentials/chat/processing/SpigotChatEvent.java +++ b/EssentialsChat/src/main/java/com/earth2me/essentials/chat/processing/SpigotChatEvent.java @@ -13,6 +13,8 @@ public class SpigotChatEvent implements AbstractChatEvent { public SpigotChatEvent(AsyncPlayerChatEvent event) { this.event = event; + event.setCancelled(AntiSpam.getAntiSpam().newMessage(event.getPlayer().getUniqueId(), + event.getMessage())); } @Override @@ -69,4 +71,5 @@ public void removeRecipients(Predicate predicate) { public void addRecipient(Player player) { event.getRecipients().add(player); } + }