From 1ccd3d9586a703e28b3edf9f9f8293edcd3119af Mon Sep 17 00:00:00 2001 From: Xelara Networks Date: Mon, 8 Jun 2026 00:02:34 -0400 Subject: [PATCH] first commit --- README.md | 0 pom.xml | 47 ++ .../com/bitnix/farmguard/FarmGuardPlugin.java | 536 ++++++++++++++++++ src/main/resources/config.yml | 44 ++ src/main/resources/plugin.yml | 16 + target/FarmGuard.jar | Bin 0 -> 16449 bytes .../FarmGuardPlugin$ActionDefinition.class | Bin 0 -> 1895 bytes .../FarmGuardPlugin$CameraSample.class | Bin 0 -> 555 bytes .../FarmGuardPlugin$DetectionContext.class | Bin 0 -> 2368 bytes .../FarmGuardPlugin$MoveSample.class | Bin 0 -> 549 bytes .../FarmGuardPlugin$PlayerData.class | Bin 0 -> 1226 bytes .../bitnix/farmguard/FarmGuardPlugin.class | Bin 0 -> 20272 bytes target/classes/config.yml | 44 ++ target/classes/plugin.yml | 16 + target/maven-archiver/pom.properties | 5 + .../compile/default-compile/createdFiles.lst | 6 + .../compile/default-compile/inputFiles.lst | 1 + 17 files changed, 715 insertions(+) create mode 100644 README.md create mode 100644 pom.xml create mode 100644 src/main/java/com/bitnix/farmguard/FarmGuardPlugin.java create mode 100644 src/main/resources/config.yml create mode 100644 src/main/resources/plugin.yml create mode 100644 target/FarmGuard.jar create mode 100644 target/classes/com/bitnix/farmguard/FarmGuardPlugin$ActionDefinition.class create mode 100644 target/classes/com/bitnix/farmguard/FarmGuardPlugin$CameraSample.class create mode 100644 target/classes/com/bitnix/farmguard/FarmGuardPlugin$DetectionContext.class create mode 100644 target/classes/com/bitnix/farmguard/FarmGuardPlugin$MoveSample.class create mode 100644 target/classes/com/bitnix/farmguard/FarmGuardPlugin$PlayerData.class create mode 100644 target/classes/com/bitnix/farmguard/FarmGuardPlugin.class create mode 100644 target/classes/config.yml create mode 100644 target/classes/plugin.yml create mode 100644 target/maven-archiver/pom.properties create mode 100644 target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst create mode 100644 target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..450e1fa --- /dev/null +++ b/pom.xml @@ -0,0 +1,47 @@ + + 4.0.0 + + com.bitnix + FarmGuard + 1.0 + jar + + FarmGuard + + + 21 + UTF-8 + + + + + papermc + https://repo.papermc.io/repository/maven-public/ + + + + + + io.papermc.paper + paper-api + 1.21.8-R0.1-SNAPSHOT + provided + + + + + FarmGuard + + + org.apache.maven.plugins + maven-compiler-plugin + 3.13.0 + + 21 + + + + + diff --git a/src/main/java/com/bitnix/farmguard/FarmGuardPlugin.java b/src/main/java/com/bitnix/farmguard/FarmGuardPlugin.java new file mode 100644 index 0000000..a7f952c --- /dev/null +++ b/src/main/java/com/bitnix/farmguard/FarmGuardPlugin.java @@ -0,0 +1,536 @@ +package com.bitnix.farmguard; + +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.GameMode; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.player.PlayerAnimationEvent; +import org.bukkit.event.player.PlayerAnimationType; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.player.PlayerItemConsumeEvent; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerMoveEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.scheduler.BukkitTask; + +import java.text.DecimalFormat; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Deque; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +public final class FarmGuardPlugin extends JavaPlugin implements Listener { + + private final Map playerDataMap = new HashMap<>(); + private BukkitTask analysisTask; + + private boolean opBypass; + private String bypassPermission; + private boolean alertConsole; + private boolean alertOps; + + private long checkPeriodTicks; + private long windowMillis; + private int minActions; + private double maxCameraMovement; + private double maxDistanceMoved; + private long maxIntervalDeviationMs; + private int suspicionIncrease; + private int suspicionDecrease; + private int alertThreshold; + private int actionThreshold; + private long alertCooldownMillis; + private long actionCooldownMillis; + + private boolean checkAttack; + private boolean checkInteract; + private boolean checkBlockBreak; + private boolean checkBlockPlace; + private boolean checkItemConsume; + private boolean checkAnimation; + + private String msgOpAlert; + private String msgConsoleLog; + private String msgKickMessage; + + private List onAlertActions = new ArrayList<>(); + private List onActionActions = new ArrayList<>(); + + private final DecimalFormat decimalFormat = new DecimalFormat("0.00"); + + @Override + public void onEnable() { + saveDefaultConfig(); + loadSettings(); + + getServer().getPluginManager().registerEvents(this, this); + startAnalysisTask(); + + getLogger().info("FarmGuard enabled."); + } + + @Override + public void onDisable() { + if (analysisTask != null) { + analysisTask.cancel(); + analysisTask = null; + } + + playerDataMap.clear(); + onAlertActions.clear(); + onActionActions.clear(); + } + + private void loadSettings() { + reloadConfig(); + FileConfiguration config = getConfig(); + + this.opBypass = config.getBoolean("op-bypass", true); + this.bypassPermission = config.getString("permission-bypass", "farmguard.bypass"); + this.alertConsole = config.getBoolean("alert-console", true); + this.alertOps = config.getBoolean("alert-ops", true); + + this.checkPeriodTicks = Math.max(20L, config.getLong("detection.check-period-ticks", 100L)); + this.windowMillis = Math.max(30L, config.getLong("detection.window-seconds", 180L)) * 1000L; + this.minActions = Math.max(10, config.getInt("detection.min-actions", 80)); + this.maxCameraMovement = Math.max(0.1, config.getDouble("detection.max-camera-movement", 12.0)); + this.maxDistanceMoved = Math.max(0.1, config.getDouble("detection.max-distance-moved", 6.0)); + this.maxIntervalDeviationMs = Math.max(1L, config.getLong("detection.max-interval-deviation-ms", 90L)); + this.suspicionIncrease = Math.max(1, config.getInt("detection.suspicion-increase", 15)); + this.suspicionDecrease = Math.max(1, config.getInt("detection.suspicion-decrease", 5)); + this.alertThreshold = Math.max(1, config.getInt("detection.alert-threshold", 70)); + this.actionThreshold = Math.max(this.alertThreshold, config.getInt("detection.action-threshold", 90)); + this.alertCooldownMillis = Math.max(1L, config.getLong("detection.alert-cooldown-seconds", 300L)) * 1000L; + this.actionCooldownMillis = Math.max(1L, config.getLong("detection.action-cooldown-seconds", 300L)) * 1000L; + + this.checkAttack = config.getBoolean("checks.attack", true); + this.checkInteract = config.getBoolean("checks.interact", true); + this.checkBlockBreak = config.getBoolean("checks.block-break", true); + this.checkBlockPlace = config.getBoolean("checks.block-place", false); + this.checkItemConsume = config.getBoolean("checks.item-consume", false); + this.checkAnimation = config.getBoolean("checks.animation", true); + + this.msgOpAlert = color(config.getString("messages.op-alert", + "&c[FarmGuard] &e%player% &cmay be using unattended repetitive actions. Score=&e%score%&c Actions=&e%actions%&c Camera=&e%camera%&c Move=&e%move%&c Deviation=&e%deviation%")); + + this.msgConsoleLog = color(config.getString("messages.console-log", + "[FarmGuard] %player% flagged. Score=%score% Actions=%actions% Camera=%camera% Move=%move% Deviation=%deviation%")); + + this.msgKickMessage = color(config.getString("messages.kick-message", + "&cSuspicious unattended repetitive activity detected.")); + + this.onAlertActions = loadActions(config.getMapList("on-alert")); + this.onActionActions = loadActions(config.getMapList("on-action")); + } + + private List loadActions(List> rawList) { + List actions = new ArrayList<>(); + + for (Map map : rawList) { + Object typeObj = map.get("type"); + if (typeObj == null) { + continue; + } + + String type = String.valueOf(typeObj).trim().toUpperCase(); + String command = map.containsKey("command") ? String.valueOf(map.get("command")) : ""; + String message = map.containsKey("message") ? String.valueOf(map.get("message")) : ""; + + actions.add(new ActionDefinition(type, command, color(message))); + } + + return actions; + } + + private void startAnalysisTask() { + if (analysisTask != null) { + analysisTask.cancel(); + } + + analysisTask = Bukkit.getScheduler().runTaskTimer(this, this::runAnalysis, checkPeriodTicks, checkPeriodTicks); + } + + private void runAnalysis() { + long now = System.currentTimeMillis(); + + for (Player player : Bukkit.getOnlinePlayers()) { + if (shouldBypass(player)) { + continue; + } + + PlayerData data = playerDataMap.computeIfAbsent(player.getUniqueId(), uuid -> new PlayerData()); + + pruneOldData(data, now); + + int actions = data.actionTimes.size(); + if (actions < minActions) { + data.suspicion = Math.max(0, data.suspicion - suspicionDecrease); + continue; + } + + double cameraMovement = data.cameraMovement; + double distanceMoved = data.distanceMoved; + long intervalDeviation = calculateIntervalDeviation(data.actionTimes); + + boolean suspicious = + cameraMovement <= maxCameraMovement && + distanceMoved <= maxDistanceMoved && + intervalDeviation <= maxIntervalDeviationMs; + + if (suspicious) { + data.suspicion += suspicionIncrease; + } else { + data.suspicion = Math.max(0, data.suspicion - suspicionDecrease); + } + + DetectionContext context = new DetectionContext( + player, + data.suspicion, + actions, + cameraMovement, + distanceMoved, + intervalDeviation + ); + + if (data.suspicion >= alertThreshold && (now - data.lastAlertTime) >= alertCooldownMillis) { + data.lastAlertTime = now; + executeActions(onAlertActions, context); + + if (onAlertActions.isEmpty()) { + if (alertOps) { + sendOpAlert(context); + } + if (alertConsole) { + getLogger().info(stripColors(formatMessage(msgConsoleLog, context))); + } + } + } + + if (data.suspicion >= actionThreshold && (now - data.lastActionTime) >= actionCooldownMillis) { + data.lastActionTime = now; + executeActions(onActionActions, context); + } + } + } + + private void pruneOldData(PlayerData data, long now) { + while (!data.actionTimes.isEmpty() && now - data.actionTimes.peekFirst() > windowMillis) { + data.actionTimes.pollFirst(); + } + + while (!data.cameraEvents.isEmpty() && now - data.cameraEvents.peekFirst().time > windowMillis) { + CameraSample removed = data.cameraEvents.pollFirst(); + data.cameraMovement = Math.max(0.0, data.cameraMovement - removed.amount); + } + + while (!data.moveEvents.isEmpty() && now - data.moveEvents.peekFirst().time > windowMillis) { + MoveSample removed = data.moveEvents.pollFirst(); + data.distanceMoved = Math.max(0.0, data.distanceMoved - removed.amount); + } + } + + private long calculateIntervalDeviation(Deque actionTimes) { + if (actionTimes.size() < 3) { + return Long.MAX_VALUE; + } + + List intervals = new ArrayList<>(); + Long previous = null; + + for (Long time : actionTimes) { + if (previous != null) { + intervals.add(time - previous); + } + previous = time; + } + + if (intervals.size() < 2) { + return Long.MAX_VALUE; + } + + double average = 0.0; + for (Long interval : intervals) { + average += interval; + } + average /= intervals.size(); + + double totalDeviation = 0.0; + for (Long interval : intervals) { + totalDeviation += Math.abs(interval - average); + } + + return Math.round(totalDeviation / intervals.size()); + } + + private boolean shouldBypass(Player player) { + if (player.getGameMode() == GameMode.CREATIVE || player.getGameMode() == GameMode.SPECTATOR) { + return true; + } + + if (opBypass && player.isOp()) { + return true; + } + + return bypassPermission != null && !bypassPermission.isBlank() && player.hasPermission(bypassPermission); + } + + private void recordAction(Player player) { + if (shouldBypass(player)) { + return; + } + + PlayerData data = playerDataMap.computeIfAbsent(player.getUniqueId(), uuid -> new PlayerData()); + data.actionTimes.addLast(System.currentTimeMillis()); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onAttack(EntityDamageByEntityEvent event) { + if (!checkAttack) { + return; + } + + if (event.getDamager() instanceof Player player) { + recordAction(player); + } + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onInteract(PlayerInteractEvent event) { + if (!checkInteract) { + return; + } + + recordAction(event.getPlayer()); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onBlockBreak(BlockBreakEvent event) { + if (!checkBlockBreak) { + return; + } + + recordAction(event.getPlayer()); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onBlockPlace(BlockPlaceEvent event) { + if (!checkBlockPlace) { + return; + } + + recordAction(event.getPlayer()); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onConsume(PlayerItemConsumeEvent event) { + if (!checkItemConsume) { + return; + } + + recordAction(event.getPlayer()); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onAnimation(PlayerAnimationEvent event) { + if (!checkAnimation) { + return; + } + + if (event.getAnimationType() == PlayerAnimationType.ARM_SWING) { + recordAction(event.getPlayer()); + } + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onMove(PlayerMoveEvent event) { + Player player = event.getPlayer(); + + if (shouldBypass(player)) { + return; + } + + PlayerData data = playerDataMap.computeIfAbsent(player.getUniqueId(), uuid -> new PlayerData()); + long now = System.currentTimeMillis(); + + float fromYaw = event.getFrom().getYaw(); + float toYaw = event.getTo().getYaw(); + float fromPitch = event.getFrom().getPitch(); + float toPitch = event.getTo().getPitch(); + + double yawDelta = angleDistance(fromYaw, toYaw); + double pitchDelta = Math.abs(toPitch - fromPitch); + double cameraDelta = yawDelta + pitchDelta; + + if (cameraDelta > 0.0) { + data.cameraEvents.addLast(new CameraSample(now, cameraDelta)); + data.cameraMovement += cameraDelta; + } + + double distance = 0.0; + if (event.getFrom().getWorld().equals(event.getTo().getWorld())) { + distance = event.getFrom().distance(event.getTo()); + } + + if (distance > 0.0) { + data.moveEvents.addLast(new MoveSample(now, distance)); + data.distanceMoved += distance; + } + } + + @EventHandler + public void onJoin(PlayerJoinEvent event) { + playerDataMap.putIfAbsent(event.getPlayer().getUniqueId(), new PlayerData()); + } + + @EventHandler + public void onQuit(PlayerQuitEvent event) { + playerDataMap.remove(event.getPlayer().getUniqueId()); + } + + private double angleDistance(float from, float to) { + double diff = Math.abs(to - from) % 360.0; + return diff > 180.0 ? 360.0 - diff : diff; + } + + private void executeActions(List actions, DetectionContext context) { + for (ActionDefinition action : actions) { + switch (action.type) { + case "OP_ALERT" -> sendOpAlert(context); + case "CONSOLE_LOG" -> getLogger().info(stripColors(formatMessage(msgConsoleLog, context))); + case "CONSOLE_COMMAND" -> { + if (action.command != null && !action.command.isBlank()) { + Bukkit.dispatchCommand(Bukkit.getConsoleSender(), formatMessage(action.command, context)); + } + } + case "PLAYER_COMMAND" -> { + if (action.command != null && !action.command.isBlank()) { + context.player.performCommand(stripLeadingSlash(formatMessage(action.command, context))); + } + } + case "KICK" -> { + String kickMessage = (action.message != null && !action.message.isBlank()) ? action.message : msgKickMessage; + context.player.kickPlayer(formatMessage(kickMessage, context)); + } + default -> getLogger().warning("Unknown action type in config: " + action.type); + } + } + } + + private void sendOpAlert(DetectionContext context) { + String message = formatMessage(msgOpAlert, context); + + for (Player online : Bukkit.getOnlinePlayers()) { + if (online.isOp()) { + online.sendMessage(message); + } + } + } + + private String formatMessage(String input, DetectionContext context) { + return color(input) + .replace("%player%", context.player.getName()) + .replace("%score%", String.valueOf(context.score)) + .replace("%actions%", String.valueOf(context.actions)) + .replace("%camera%", decimalFormat.format(context.cameraMovement)) + .replace("%move%", decimalFormat.format(context.distanceMoved)) + .replace("%deviation%", String.valueOf(context.intervalDeviation)); + } + + private String color(String input) { + return input == null ? "" : ChatColor.translateAlternateColorCodes('&', input); + } + + private String stripColors(String input) { + return ChatColor.stripColor(input); + } + + private String stripLeadingSlash(String input) { + if (input == null) { + return ""; + } + return input.startsWith("/") ? input.substring(1) : input; + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (args.length == 1 && args[0].equalsIgnoreCase("reload")) { + if (!sender.hasPermission("farmguard.admin")) { + sender.sendMessage(color("&cYou do not have permission.")); + return true; + } + + if (analysisTask != null) { + analysisTask.cancel(); + analysisTask = null; + } + + loadSettings(); + startAnalysisTask(); + sender.sendMessage(color("&aFarmGuard config reloaded.")); + return true; + } + + sender.sendMessage(color("&eUsage: /farmguard reload")); + return true; + } + + private static final class PlayerData { + private final Deque actionTimes = new ArrayDeque<>(); + private final Deque cameraEvents = new ArrayDeque<>(); + private final Deque moveEvents = new ArrayDeque<>(); + private double cameraMovement = 0.0; + private double distanceMoved = 0.0; + private int suspicion = 0; + private long lastAlertTime = 0L; + private long lastActionTime = 0L; + } + + private static final class CameraSample { + private final long time; + private final double amount; + + private CameraSample(long time, double amount) { + this.time = time; + this.amount = amount; + } + } + + private static final class MoveSample { + private final long time; + private final double amount; + + private MoveSample(long time, double amount) { + this.time = time; + this.amount = amount; + } + } + + private record DetectionContext( + Player player, + int score, + int actions, + double cameraMovement, + double distanceMoved, + long intervalDeviation + ) { } + + private record ActionDefinition( + String type, + String command, + String message + ) { } +} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml new file mode 100644 index 0000000..182196b --- /dev/null +++ b/src/main/resources/config.yml @@ -0,0 +1,44 @@ +op-bypass: true +permission-bypass: "farmguard.bypass" + +alert-console: true +alert-ops: true + +detection: + check-period-ticks: 100 + window-seconds: 180 + min-actions: 80 + max-camera-movement: 12.0 + max-distance-moved: 6.0 + max-interval-deviation-ms: 90 + suspicion-increase: 15 + suspicion-decrease: 5 + alert-threshold: 70 + action-threshold: 90 + alert-cooldown-seconds: 300 + action-cooldown-seconds: 300 + +checks: + attack: true + interact: true + block-break: true + block-place: false + item-consume: false + animation: true + +messages: + op-alert: "&c[FarmGuard] &e%player% &cmay be using unattended repetitive actions. Score=&e%score%&c Actions=&e%actions%&c Camera=&e%camera%&c Move=&e%move%&c Deviation=&e%deviation%" + console-log: "[FarmGuard] %player% flagged. Score=%score% Actions=%actions% Camera=%camera% Move=%move% Deviation=%deviation%" + kick-message: "&cSuspicious unattended repetitive activity detected." + +on-alert: + - type: OP_ALERT + - type: CONSOLE_LOG + +on-action: [] +# Example: +# on-action: +# - type: CONSOLE_COMMAND +# command: "cmi warp %player% checkroom" +# - type: KICK +# message: "&cPlease contact staff." diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml new file mode 100644 index 0000000..072734a --- /dev/null +++ b/src/main/resources/plugin.yml @@ -0,0 +1,16 @@ +name: FarmGuard +version: 1.0 +main: com.bitnix.farmguard.FarmGuardPlugin +api-version: '1.21' +author: bitnix +description: Detects suspicious repetitive unattended behavior and triggers configurable actions. +commands: + farmguard: + description: FarmGuard admin command + usage: /farmguard reload + permission: farmguard.admin +permissions: + farmguard.admin: + default: op + farmguard.bypass: + default: false diff --git a/target/FarmGuard.jar b/target/FarmGuard.jar new file mode 100644 index 0000000000000000000000000000000000000000..7857fb7381b7b132062def1f5b8995daf38eca58 GIT binary patch literal 16449 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 Sr=mnLiI*6_D=c9J{r>>%NUEa% literal 0 HcmV?d00001 diff --git a/target/classes/com/bitnix/farmguard/FarmGuardPlugin$CameraSample.class b/target/classes/com/bitnix/farmguard/FarmGuardPlugin$CameraSample.class new file mode 100644 index 0000000000000000000000000000000000000000..5d78cfd0014b2aac8ffa1c43b44e80d85149bad0 GIT binary patch literal 555 zcmb7B$w~u35PdZ>G08ZZIL0lm2M?&9;z?=f_o}F>SN;0=1Z zRlq<;de*pu63Q$hf+!1Fjar0U?Wk4naAh;P?7skFWfCz?wFNP&k%JdJrXP9`l6NzsL}r!!VR_(-%o1 zc{}H)7g%Qp4E_fLuJ)6etL6a3i;23e$|5X&L%rTFB%x!;yhm%za@M3ye0`6i~$?s-H*_d&U3& literal 0 HcmV?d00001 diff --git a/target/classes/com/bitnix/farmguard/FarmGuardPlugin$DetectionContext.class b/target/classes/com/bitnix/farmguard/FarmGuardPlugin$DetectionContext.class new file mode 100644 index 0000000000000000000000000000000000000000..6ae433588fac1354686b7599bd2d7990f0dbdc21 GIT binary patch literal 2368 zcmbVO>vGyg6#mu*gsB`GLMXl^ZQ@Xn+o)-qrj49l0&$Fk9XEE}nfBKLTdY_}R1!@5 z4t6w&vt!~S-S1ZC74c~O^vSa(=wNExX7C#m)!(z>GTY9~FbY%Lvuzk}%)^~$5 zL%O=AVh-~RagVSF23D2385P&%uEGPWp4_{kB8PVwt~GdDxP05`i8k3Im5MxGP_ZbF zXPc(ybGsqrg{C}NRZ2>Ws7i}P6(wktY4J;!TOLF0s`iEuzo@^W zLK>wX8EzLZuUxfSsce-F7-C^4b2ZZzJKc6&xcj_rQE-`>)8N(tcTM>nZN~gVlYl*w}pS`G;uo;%g+NJ#(Sgi!_Er!&d z({&qS!<6A>-(ti)X`8COBRqf8@q7hDuhmI9iVAwl%lRjc<9oi#J7L&f5>FV)A+wBt zFh)cW<2)&hb0jb>H2xe>i=J6}()48M$Z+IiJ@zX^k!`{!4^Q`7-)wYEjm=x4d^KTC`1$zv42Wc1+mV)mYZjQf71rBq7 zk7+nf5t9?h$YD!TMh?SZrSTW6H1dL#W?Qh*B%@W%^f=b&{ULq*ifP&b3fkXb+FBoq zw%o_8w$evh%g6f2YWYMTc`cXhW9bduoWe)+PJoe^!ab5Vp-2nR36=a60z`C9lEfr& z7UWPSUs95|Vu2))B?3ueOX5Ds3kgyODS}i$mqEiBG=_mRjoMGhu>^jfR_Q{~PH+qO zlf;Y2)bL7#EKOtd(-G=-fZrxkFX6LDeVv@iu#(!^2|lT#6*>N!pl&_CePEV$s!xv|;r$`E6Xa5+Jy@BV&k;?hW zlvnWO2+vEuPvAL@N5L%v>%saMU(;Hk#tVrP(k3KLNSTl@Azi+qyE$@QrS?qHf1)xf cc!~`?qqg6rvkcWZiO2Yky7VRXae#&Y0nG{TOaK4? literal 0 HcmV?d00001 diff --git a/target/classes/com/bitnix/farmguard/FarmGuardPlugin$MoveSample.class b/target/classes/com/bitnix/farmguard/FarmGuardPlugin$MoveSample.class new file mode 100644 index 0000000000000000000000000000000000000000..9a8c928c681f6e62470c9cf0c50afd09fb8a763c GIT binary patch literal 549 zcmb7B%T5A85Uklu@Ie+<)=}gONy| zfOS--f{h|dWWoI~4akeN7&5g^t3II2W;mwIVpj&Dm-<5y-SMGn;dH|h_Xa$YN`D89 z6>K-~NpxAf?YwXWqHtKUMzfefSD%}nYq6Q2t vgS?hSK{HXN_yHAC8P>2)sF<>03#N>8Ivb=kY+`GH73>gNl#@dRyQq8t{uy=F literal 0 HcmV?d00001 diff --git a/target/classes/com/bitnix/farmguard/FarmGuardPlugin$PlayerData.class b/target/classes/com/bitnix/farmguard/FarmGuardPlugin$PlayerData.class new file mode 100644 index 0000000000000000000000000000000000000000..72bfb31ba352106cf30b51767923da26f3319c56 GIT binary patch literal 1226 zcmbVL+fEcg5IxPV47&`=MZB$dSrA5LMDe-^A*?87MUo{XKK9PGYm>R)OwUF>!O!u5 zkZ9ru_$%J^-B{fihTzM_m#(Tlr>m-~Pk;RV?gM~D%oovvoQ1rNBj{xq+TD zO=xN4oQ?Ciz)(oiWJ`L~7oAzr$R!(NsL&aoCu-3bvCHZE(lWYGN!QKE)y zhg_vGQO6G7ZEkPF%)TCl?dp7$VfkNHS2JBQqiOJ9%NHh5?LQOgVH`-4Uy54P{9s*% zVl@q#B3|cBpLRy-k;nZF9!nkn-OQ_(lAdv<-c`?;4(Q^=MwG^$Se82Z=po5WYgsV# zt%^iFjuK_z7DJ_*BBYv?Fck3;Jx!t|R?MnexJSKqlcjQSyp-}_Xko5f;b*g@KC%GU zNkcvKJ^M)K^pC(WNl_1`C?@6a#|}j#`2F|_aY*TgOOiI01Z*tH)mRcHSw>9y?_{r1 z^aeTdfCXn4%y|#%`7R1OC}!?Z<{ouQJ2=u4^aLCEr~Wq literal 0 HcmV?d00001 diff --git a/target/classes/com/bitnix/farmguard/FarmGuardPlugin.class b/target/classes/com/bitnix/farmguard/FarmGuardPlugin.class new file mode 100644 index 0000000000000000000000000000000000000000..8e067141b73622f6354e279b98d3e8e742fb318e GIT binary patch literal 20272 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 N8`l{(7&lV!{{nW~XzTz0 literal 0 HcmV?d00001 diff --git a/target/classes/config.yml b/target/classes/config.yml new file mode 100644 index 0000000..182196b --- /dev/null +++ b/target/classes/config.yml @@ -0,0 +1,44 @@ +op-bypass: true +permission-bypass: "farmguard.bypass" + +alert-console: true +alert-ops: true + +detection: + check-period-ticks: 100 + window-seconds: 180 + min-actions: 80 + max-camera-movement: 12.0 + max-distance-moved: 6.0 + max-interval-deviation-ms: 90 + suspicion-increase: 15 + suspicion-decrease: 5 + alert-threshold: 70 + action-threshold: 90 + alert-cooldown-seconds: 300 + action-cooldown-seconds: 300 + +checks: + attack: true + interact: true + block-break: true + block-place: false + item-consume: false + animation: true + +messages: + op-alert: "&c[FarmGuard] &e%player% &cmay be using unattended repetitive actions. Score=&e%score%&c Actions=&e%actions%&c Camera=&e%camera%&c Move=&e%move%&c Deviation=&e%deviation%" + console-log: "[FarmGuard] %player% flagged. Score=%score% Actions=%actions% Camera=%camera% Move=%move% Deviation=%deviation%" + kick-message: "&cSuspicious unattended repetitive activity detected." + +on-alert: + - type: OP_ALERT + - type: CONSOLE_LOG + +on-action: [] +# Example: +# on-action: +# - type: CONSOLE_COMMAND +# command: "cmi warp %player% checkroom" +# - type: KICK +# message: "&cPlease contact staff." diff --git a/target/classes/plugin.yml b/target/classes/plugin.yml new file mode 100644 index 0000000..072734a --- /dev/null +++ b/target/classes/plugin.yml @@ -0,0 +1,16 @@ +name: FarmGuard +version: 1.0 +main: com.bitnix.farmguard.FarmGuardPlugin +api-version: '1.21' +author: bitnix +description: Detects suspicious repetitive unattended behavior and triggers configurable actions. +commands: + farmguard: + description: FarmGuard admin command + usage: /farmguard reload + permission: farmguard.admin +permissions: + farmguard.admin: + default: op + farmguard.bypass: + default: false diff --git a/target/maven-archiver/pom.properties b/target/maven-archiver/pom.properties new file mode 100644 index 0000000..4389cef --- /dev/null +++ b/target/maven-archiver/pom.properties @@ -0,0 +1,5 @@ +#Generated by Maven +#Sun Jun 07 21:02:20 EDT 2026 +artifactId=FarmGuard +groupId=com.bitnix +version=1.0 diff --git a/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst new file mode 100644 index 0000000..28a91f2 --- /dev/null +++ b/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst @@ -0,0 +1,6 @@ +com/bitnix/farmguard/FarmGuardPlugin$PlayerData.class +com/bitnix/farmguard/FarmGuardPlugin$ActionDefinition.class +com/bitnix/farmguard/FarmGuardPlugin$CameraSample.class +com/bitnix/farmguard/FarmGuardPlugin$DetectionContext.class +com/bitnix/farmguard/FarmGuardPlugin$MoveSample.class +com/bitnix/farmguard/FarmGuardPlugin.class diff --git a/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst new file mode 100644 index 0000000..0671954 --- /dev/null +++ b/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst @@ -0,0 +1 @@ +/home/bitnix/Desktop/FarmGuard/src/main/java/com/bitnix/farmguard/FarmGuardPlugin.java