From 1ac19014c1e35996895430ae7dc44029f0d05ca4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20M=C3=BCller?= Date: Sat, 13 Dec 2025 13:45:37 +0100 Subject: [PATCH] introduced `AntiInventoryMove` appliance with related listeners and optimized admin notification logic in `AcInform` --- .../antiBoatFreecam/AntiBoatFreecam.java | 13 ++----- .../antiInventoryMove/AntiInventoryMove.java | 38 +++++++++++++++++++ .../InInventoryMoveListener.java | 21 ++++++++++ .../InventoryTrackerListener.java | 21 ++++++++++ .../appliances/tooling/acInform/AcInform.java | 25 ++++++++++++ 5 files changed, 108 insertions(+), 10 deletions(-) create mode 100644 common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/security/antiInventoryMove/AntiInventoryMove.java create mode 100644 common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/security/antiInventoryMove/InInventoryMoveListener.java create mode 100644 common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/security/antiInventoryMove/InventoryTrackerListener.java diff --git a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/security/antiBoatFreecam/AntiBoatFreecam.java b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/security/antiBoatFreecam/AntiBoatFreecam.java index 8313799..3830920 100644 --- a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/security/antiBoatFreecam/AntiBoatFreecam.java +++ b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/security/antiBoatFreecam/AntiBoatFreecam.java @@ -5,15 +5,10 @@ import eu.mhsl.craftattack.spawn.core.Main; import eu.mhsl.craftattack.spawn.core.appliance.Appliance; import org.bukkit.Bukkit; import org.bukkit.entity.Boat; -import org.bukkit.entity.Player; - -import java.util.HashMap; -import java.util.Map; @SuppressWarnings("unused") public class AntiBoatFreecam extends Appliance { private static final float MAX_YAW_OFFSET = 106.0f; - private final Map violatedPlayers = new HashMap<>(); public AntiBoatFreecam() { Bukkit.getScheduler().runTaskTimerAsynchronously( @@ -27,14 +22,12 @@ public class AntiBoatFreecam extends Appliance { float yawDelta = wrapDegrees(playerYaw - boatYaw); if(Math.abs(yawDelta) <= MAX_YAW_OFFSET) return; - this.violatedPlayers.merge(player, 1f, Float::sum); - float violationCount = this.violatedPlayers.get(player); - if(violationCount != 1 && violationCount % 100 != 0) return; - Main.instance().getAppliance(AcInform.class).notifyAdmins( + Main.instance().getAppliance(AcInform.class).slowedNotifyAdmins( "internal", player.getName(), "illegalBoatLookYaw", - violationCount + yawDelta, + 3000 ); }), 1L, diff --git a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/security/antiInventoryMove/AntiInventoryMove.java b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/security/antiInventoryMove/AntiInventoryMove.java new file mode 100644 index 0000000..2b7a21d --- /dev/null +++ b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/security/antiInventoryMove/AntiInventoryMove.java @@ -0,0 +1,38 @@ +package eu.mhsl.craftattack.spawn.common.appliances.security.antiInventoryMove; + +import eu.mhsl.craftattack.spawn.core.appliance.Appliance; +import net.kyori.adventure.util.Ticks; +import org.bukkit.entity.Player; +import org.bukkit.event.Listener; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +public class AntiInventoryMove extends Appliance { + private static final long errorTimeMargin = Ticks.SINGLE_TICK_DURATION_MS * 2; + + private final Map invOpen = new ConcurrentHashMap<>(); + + public void setInvOpen(Player player, boolean open) { + if(open) + this.invOpen.put(player.getUniqueId(), System.currentTimeMillis()); + else + this.invOpen.remove(player.getUniqueId()); + } + + public boolean hasInventoryOpen(Player player) { + if(!this.invOpen.containsKey(player.getUniqueId())) return false; + return this.invOpen.get(player.getUniqueId()) < System.currentTimeMillis() - errorTimeMargin; + } + + @Override + protected @NotNull List listeners() { + return List.of( + new InventoryTrackerListener(), + new InInventoryMoveListener() + ); + } +} diff --git a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/security/antiInventoryMove/InInventoryMoveListener.java b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/security/antiInventoryMove/InInventoryMoveListener.java new file mode 100644 index 0000000..0fd4b28 --- /dev/null +++ b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/security/antiInventoryMove/InInventoryMoveListener.java @@ -0,0 +1,21 @@ +package eu.mhsl.craftattack.spawn.common.appliances.security.antiInventoryMove; + +import eu.mhsl.craftattack.spawn.common.appliances.tooling.acInform.AcInform; +import eu.mhsl.craftattack.spawn.core.Main; +import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener; +import org.bukkit.event.EventHandler; +import org.bukkit.event.player.PlayerInputEvent; + +class InInventoryMoveListener extends ApplianceListener { + @EventHandler + public void onInput(PlayerInputEvent event) { + if(!this.getAppliance().hasInventoryOpen(event.getPlayer())) return; + Main.instance().getAppliance(AcInform.class).slowedNotifyAdmins( + "internal", + event.getPlayer().getName(), + "inInventoryMove", + -1f, + 3000 + ); + } +} diff --git a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/security/antiInventoryMove/InventoryTrackerListener.java b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/security/antiInventoryMove/InventoryTrackerListener.java new file mode 100644 index 0000000..0c5c89f --- /dev/null +++ b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/security/antiInventoryMove/InventoryTrackerListener.java @@ -0,0 +1,21 @@ +package eu.mhsl.craftattack.spawn.common.appliances.security.antiInventoryMove; + +import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.event.inventory.InventoryOpenEvent; + +class InventoryTrackerListener extends ApplianceListener { + @EventHandler + public void onOpen(InventoryOpenEvent event) { + if(!(event.getPlayer() instanceof Player player)) return; + this.getAppliance().setInvOpen(player, true); + } + + @EventHandler + public void onClose(InventoryCloseEvent event) { + if(!(event.getPlayer() instanceof Player player)) return; + this.getAppliance().setInvOpen(player, false); + } +} diff --git a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/tooling/acInform/AcInform.java b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/tooling/acInform/AcInform.java index efd5c05..a721ad3 100644 --- a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/tooling/acInform/AcInform.java +++ b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/tooling/acInform/AcInform.java @@ -11,14 +11,20 @@ import org.bukkit.Bukkit; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; public class AcInform extends Appliance { + private final Map> violationSlowdowns = new ConcurrentHashMap<>(); + public void processCommand(@NotNull String[] args) { String anticheatName = null; String playerName = null; String checkName = null; Float violationCount = null; + int notifyEvery = 0; for(int i = 0; i < args.length; i++) { if(!args[i].startsWith("--")) continue; @@ -36,13 +42,32 @@ public class AcInform extends Appliance { case "--playerName" -> playerName = value; case "--check" -> checkName = value; case "--violationCount" -> violationCount = value.isEmpty() ? null : Float.valueOf(value); + case "--notifyEvery" -> notifyEvery = Integer.parseInt(value); } } + if(notifyEvery == 0) { + this.notifyAdmins(anticheatName, playerName, checkName, violationCount); + } else { + this.slowedNotifyAdmins(anticheatName, playerName, checkName, violationCount, notifyEvery); + } + } + + public void slowedNotifyAdmins(@Nullable String anticheatName, @Nullable String playerName, @Nullable String checkName, @Nullable Float violationCount, int notifyEvery) { + this.violationSlowdowns.putIfAbsent(playerName, new HashMap<>()); + + var slowdowns = this.violationSlowdowns.get(playerName); + if(slowdowns.containsKey(checkName)) { + if(slowdowns.get(checkName) > System.currentTimeMillis() - notifyEvery) return; + } + this.notifyAdmins(anticheatName, playerName, checkName, violationCount); } public void notifyAdmins(@Nullable String anticheatName, @Nullable String playerName, @Nullable String checkName, @Nullable Float violationCount) { + this.violationSlowdowns.putIfAbsent(playerName, new HashMap<>()); + this.violationSlowdowns.get(playerName).put(checkName, System.currentTimeMillis()); + ComponentBuilder component = Component.text(); NamedTextColor textColor = NamedTextColor.GRAY;