antigrief false positives tweaks
This commit is contained in:
@@ -19,6 +19,7 @@ class KickCommand extends ApplianceCommand<Kick> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) throws Exception {
|
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(
|
this.getAppliance().kick(
|
||||||
args[0],
|
args[0],
|
||||||
Arrays.stream(args).skip(1).collect(Collectors.joining(" "))
|
Arrays.stream(args).skip(1).collect(Collectors.joining(" "))
|
||||||
|
@@ -96,6 +96,11 @@ public class AntiGrief extends Appliance {
|
|||||||
this.chunkZ = chunkZ;
|
this.chunkZ = chunkZ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
long getInhabitedTime() {
|
||||||
|
return Objects.requireNonNull(Bukkit.getWorld(this.worldId))
|
||||||
|
.getChunkAt(this.chunkX, this.chunkX).getInhabitedTime();
|
||||||
|
}
|
||||||
|
|
||||||
void addIncident(GriefIncident incident) {
|
void addIncident(GriefIncident incident) {
|
||||||
long b = incident.timestamp / BUCKET_DURATION_MS;
|
long b = incident.timestamp / BUCKET_DURATION_MS;
|
||||||
this.scores.merge(b, (double) incident.severity.weight, Double::sum);
|
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. */
|
/** Maximum retention time for individual incidents in milliseconds. */
|
||||||
private static final long INCIDENT_RETAIN_MS = 60 * 60 * 1000;
|
private static final long INCIDENT_RETAIN_MS = 60 * 60 * 1000;
|
||||||
/** Spike factor against EMA baseline. Triggers if current score >= baseline * FACTOR_SPIKE. */
|
/** 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. */
|
/** 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. */
|
/** Cooldown time in ms to suppress repeated alerts for the same area. */
|
||||||
private static final long ALERT_COOLDOWN_MS = 2 * 60 * 1000;
|
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 */
|
/** 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. */
|
/** Stores direct incidents mapped by player UUID. */
|
||||||
private final Map<UUID, Set<GriefIncident>> directGriefRegistry = new ConcurrentHashMap<>();
|
private final Map<UUID, Set<GriefIncident>> directGriefRegistry = new ConcurrentHashMap<>();
|
||||||
@@ -159,8 +164,6 @@ public class AntiGrief extends Appliance {
|
|||||||
this.areas
|
this.areas
|
||||||
.computeIfAbsent(areaKey, key -> new AreaState(incident.worldId, chunk.getX(), chunk.getZ()))
|
.computeIfAbsent(areaKey, key -> new AreaState(incident.worldId, chunk.getX(), chunk.getZ()))
|
||||||
.addIncident(incident);
|
.addIncident(incident);
|
||||||
|
|
||||||
Bukkit.broadcast(text(String.format("%d, %d: %s - %s: {%s}", chunk.getX(), chunk.getZ(), incident.severity, incident.event, incident.data)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -169,17 +172,17 @@ public class AntiGrief extends Appliance {
|
|||||||
final long now = System.currentTimeMillis();
|
final long now = System.currentTimeMillis();
|
||||||
final long bucketIdx = this.bucketIdx(now);
|
final long bucketIdx = this.bucketIdx(now);
|
||||||
|
|
||||||
this.areas.forEach((areaKey, st) -> {
|
this.areas.forEach((areaKey, state) -> {
|
||||||
final double currentScore = st.currentScore(bucketIdx);
|
final double currentScore = state.currentScore(bucketIdx);
|
||||||
if (currentScore <= 0.0) return;
|
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;
|
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;
|
boolean spike = currentScore >= HARD_THRESHOLD || currentScore >= base * FACTOR_SPIKE;
|
||||||
if (spike && (now - st.lastAlertAt) >= ALERT_COOLDOWN_MS) {
|
if (spike && (now - state.lastAlertAt) >= ALERT_COOLDOWN_MS) {
|
||||||
st.lastAlertAt = now;
|
state.lastAlertAt = now;
|
||||||
Bukkit.getScheduler().runTask(Main.instance(), () ->
|
Bukkit.getScheduler().runTask(Main.instance(), () ->
|
||||||
this.alertAdmins(areaKey, bucketIdx, currentScore, newBase)
|
this.alertAdmins(areaKey, bucketIdx, currentScore, newBase)
|
||||||
);
|
);
|
||||||
|
@@ -4,6 +4,7 @@ import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener;
|
|||||||
import eu.mhsl.craftattack.spawn.craftattack.appliances.security.antiGrief.AntiGrief;
|
import eu.mhsl.craftattack.spawn.craftattack.appliances.security.antiGrief.AntiGrief;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.block.Block;
|
import org.bukkit.block.Block;
|
||||||
|
import org.bukkit.block.PistonMoveReaction;
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.block.*;
|
import org.bukkit.event.block.*;
|
||||||
|
|
||||||
@@ -57,6 +58,17 @@ public class FireRelatedGriefListener extends ApplianceListener<AntiGrief> {
|
|||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void blockBurned(BlockBurnEvent event) {
|
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(
|
this.getAppliance().trackPassive(
|
||||||
event.getBlock().getChunk(),
|
event.getBlock().getChunk(),
|
||||||
new AntiGrief.GriefIncident(
|
new AntiGrief.GriefIncident(
|
||||||
|
@@ -10,12 +10,14 @@ import org.bukkit.event.block.BlockFromToEvent;
|
|||||||
import org.bukkit.event.player.PlayerBucketEmptyEvent;
|
import org.bukkit.event.player.PlayerBucketEmptyEvent;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.ThreadLocalRandom;
|
||||||
|
|
||||||
public class LiquidRelatedGriefListener extends ApplianceListener<AntiGrief> {
|
public class LiquidRelatedGriefListener extends ApplianceListener<AntiGrief> {
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void liquidFlow(BlockFromToEvent event) {
|
public void liquidFlow(BlockFromToEvent event) {
|
||||||
if(event.getToBlock().isEmpty()) return;
|
if(event.getToBlock().isEmpty()) return;
|
||||||
if(event.getToBlock().isSolid()) return;
|
if(event.getToBlock().isSolid()) return;
|
||||||
|
if(ThreadLocalRandom.current().nextDouble() < 0.95) return;
|
||||||
|
|
||||||
this.getAppliance().trackPassive(
|
this.getAppliance().trackPassive(
|
||||||
event.getToBlock().getChunk(),
|
event.getToBlock().getChunk(),
|
||||||
|
Reference in New Issue
Block a user