From 16d7347fd058dffdc12c976b29713d82d76e2056 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20M=C3=BCller?= Date: Sat, 27 Sep 2025 07:30:23 +0200 Subject: [PATCH] antigrief false positives tweaks --- .../appliances/tooling/kick/KickCommand.java | 1 + .../security/antiGrief/AntiGrief.java | 25 +++++++++++-------- .../listener/FireRelatedGriefListener.java | 12 +++++++++ .../listener/LiquidRelatedGriefListener.java | 2 ++ 4 files changed, 29 insertions(+), 11 deletions(-) diff --git a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/tooling/kick/KickCommand.java b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/tooling/kick/KickCommand.java index c9edaac..1122db6 100644 --- a/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/tooling/kick/KickCommand.java +++ b/common/src/main/java/eu/mhsl/craftattack/spawn/common/appliances/tooling/kick/KickCommand.java @@ -19,6 +19,7 @@ class KickCommand extends ApplianceCommand { @Override protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) throws Exception { + if(args.length < 1) throw new Error("Es muss ein Spielername angegeben werden!"); this.getAppliance().kick( args[0], Arrays.stream(args).skip(1).collect(Collectors.joining(" ")) diff --git a/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/security/antiGrief/AntiGrief.java b/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/security/antiGrief/AntiGrief.java index a333d45..b9aacf8 100644 --- a/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/security/antiGrief/AntiGrief.java +++ b/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/security/antiGrief/AntiGrief.java @@ -96,6 +96,11 @@ public class AntiGrief extends Appliance { this.chunkZ = chunkZ; } + long getInhabitedTime() { + return Objects.requireNonNull(Bukkit.getWorld(this.worldId)) + .getChunkAt(this.chunkX, this.chunkX).getInhabitedTime(); + } + void addIncident(GriefIncident incident) { long b = incident.timestamp / BUCKET_DURATION_MS; this.scores.merge(b, (double) incident.severity.weight, Double::sum); @@ -126,13 +131,13 @@ public class AntiGrief extends Appliance { /** Maximum retention time for individual incidents in milliseconds. */ private static final long INCIDENT_RETAIN_MS = 60 * 60 * 1000; /** Spike factor against EMA baseline. Triggers if current score >= baseline * FACTOR_SPIKE. */ - private static final double FACTOR_SPIKE = 3.0; + private static final double FACTOR_SPIKE = 5.0; /** Absolute threshold for spike detection. Triggers if current score exceeds this value. */ - private static final double HARD_THRESHOLD = 20.0; + private static final double HARD_THRESHOLD = 50.0; /** Cooldown time in ms to suppress repeated alerts for the same area. */ private static final long ALERT_COOLDOWN_MS = 2 * 60 * 1000; /** Smoothing factor for EMA baseline. Lower = smoother, higher = more reactive. 0.0 < EMA_ALPHA <= 1.0 */ - private static final double EMA_ALPHA = 0.3; + private static final double EMA_ALPHA = 0.2; /** Stores direct incidents mapped by player UUID. */ private final Map> directGriefRegistry = new ConcurrentHashMap<>(); @@ -159,8 +164,6 @@ public class AntiGrief extends Appliance { this.areas .computeIfAbsent(areaKey, key -> new AreaState(incident.worldId, chunk.getX(), chunk.getZ())) .addIncident(incident); - - Bukkit.broadcast(text(String.format("%d, %d: %s - %s: {%s}", chunk.getX(), chunk.getZ(), incident.severity, incident.event, incident.data))); } @Override @@ -169,17 +172,17 @@ public class AntiGrief extends Appliance { final long now = System.currentTimeMillis(); final long bucketIdx = this.bucketIdx(now); - this.areas.forEach((areaKey, st) -> { - final double currentScore = st.currentScore(bucketIdx); + this.areas.forEach((areaKey, state) -> { + final double currentScore = state.currentScore(bucketIdx); if (currentScore <= 0.0) return; - final double base = (st.ema == 0.0) ? currentScore : st.ema; + final double base = (state.ema == 0.0) ? currentScore : state.ema; final double newBase = EMA_ALPHA * currentScore + (1 - EMA_ALPHA) * base; - st.ema = newBase; + state.ema = Math.max(3, newBase); boolean spike = currentScore >= HARD_THRESHOLD || currentScore >= base * FACTOR_SPIKE; - if (spike && (now - st.lastAlertAt) >= ALERT_COOLDOWN_MS) { - st.lastAlertAt = now; + if (spike && (now - state.lastAlertAt) >= ALERT_COOLDOWN_MS) { + state.lastAlertAt = now; Bukkit.getScheduler().runTask(Main.instance(), () -> this.alertAdmins(areaKey, bucketIdx, currentScore, newBase) ); diff --git a/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/security/antiGrief/listener/FireRelatedGriefListener.java b/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/security/antiGrief/listener/FireRelatedGriefListener.java index 06d05de..f084428 100644 --- a/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/security/antiGrief/listener/FireRelatedGriefListener.java +++ b/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/security/antiGrief/listener/FireRelatedGriefListener.java @@ -4,6 +4,7 @@ import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener; import eu.mhsl.craftattack.spawn.craftattack.appliances.security.antiGrief.AntiGrief; import org.bukkit.Material; import org.bukkit.block.Block; +import org.bukkit.block.PistonMoveReaction; import org.bukkit.event.EventHandler; import org.bukkit.event.block.*; @@ -57,6 +58,17 @@ public class FireRelatedGriefListener extends ApplianceListener { @EventHandler public void blockBurned(BlockBurnEvent event) { + if(event.getBlock().isReplaceable()) return; + if(event.getBlock().isPassable()) return; + if(event.getBlock().getPistonMoveReaction().equals(PistonMoveReaction.BREAK)) return; + if(event.getBlock().getType().name().endsWith("_LEAVES")) return; + if(event.getBlock().getType().name().endsWith("_LOG")) return; + List allowed = List.of( + Material.MOSS_BLOCK, + Material.MOSS_CARPET + ); + if(allowed.contains(event.getBlock().getType())) return; + this.getAppliance().trackPassive( event.getBlock().getChunk(), new AntiGrief.GriefIncident( diff --git a/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/security/antiGrief/listener/LiquidRelatedGriefListener.java b/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/security/antiGrief/listener/LiquidRelatedGriefListener.java index 78de348..fafa54e 100644 --- a/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/security/antiGrief/listener/LiquidRelatedGriefListener.java +++ b/craftattack/src/main/java/eu/mhsl/craftattack/spawn/craftattack/appliances/security/antiGrief/listener/LiquidRelatedGriefListener.java @@ -10,12 +10,14 @@ import org.bukkit.event.block.BlockFromToEvent; import org.bukkit.event.player.PlayerBucketEmptyEvent; import java.util.List; +import java.util.concurrent.ThreadLocalRandom; public class LiquidRelatedGriefListener extends ApplianceListener { @EventHandler public void liquidFlow(BlockFromToEvent event) { if(event.getToBlock().isEmpty()) return; if(event.getToBlock().isSolid()) return; + if(ThreadLocalRandom.current().nextDouble() < 0.95) return; this.getAppliance().trackPassive( event.getToBlock().getChunk(),