antigrief false positives tweaks

This commit is contained in:
2025-09-27 07:30:23 +02:00
parent fdf3b5c73f
commit 16d7347fd0
4 changed files with 29 additions and 11 deletions

View File

@@ -19,6 +19,7 @@ class KickCommand extends ApplianceCommand<Kick> {
@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(" "))

View File

@@ -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<UUID, Set<GriefIncident>> 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)
);

View File

@@ -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<AntiGrief> {
@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<Material> 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(

View File

@@ -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<AntiGrief> {
@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(),