-
-
Notifications
You must be signed in to change notification settings - Fork 102
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add anti-AFK system for preventing AFK XP gain (#305)
- Loading branch information
Showing
36 changed files
with
1,120 additions
and
77 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
124 changes: 124 additions & 0 deletions
124
bukkit/src/main/java/dev/aurelium/auraskills/bukkit/antiafk/AntiAfkManager.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
package dev.aurelium.auraskills.bukkit.antiafk; | ||
|
||
import com.ezylang.evalex.Expression; | ||
import com.ezylang.evalex.parser.ParseException; | ||
import dev.aurelium.auraskills.bukkit.AuraSkills; | ||
import dev.aurelium.auraskills.bukkit.user.BukkitUser; | ||
import dev.aurelium.auraskills.common.config.Option; | ||
import dev.aurelium.auraskills.common.message.type.CommandMessage; | ||
import dev.aurelium.auraskills.common.region.BlockPosition; | ||
import dev.aurelium.auraskills.common.user.AntiAfkLog; | ||
import dev.aurelium.auraskills.common.user.User; | ||
import dev.aurelium.auraskills.common.util.text.TextUtil; | ||
import org.bukkit.Bukkit; | ||
import org.bukkit.Location; | ||
import org.bukkit.World; | ||
import org.bukkit.entity.Player; | ||
import org.bukkit.event.HandlerList; | ||
import org.jetbrains.annotations.Nullable; | ||
|
||
import java.lang.reflect.Constructor; | ||
import java.lang.reflect.InvocationTargetException; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
|
||
public class AntiAfkManager { | ||
|
||
private final AuraSkills plugin; | ||
private final Map<CheckType, Check> checkMap = new HashMap<>(); | ||
private Expression logThresholdExpression; | ||
|
||
public AntiAfkManager(AuraSkills plugin) { | ||
this.plugin = plugin; | ||
|
||
if (!plugin.configBoolean(Option.ANTI_AFK_ENABLED)) return; | ||
|
||
loadLogThresholdExpression(); | ||
registerChecks(); | ||
} | ||
|
||
public void reload() { | ||
loadLogThresholdExpression(); | ||
// Unregister checks | ||
for (Check existing : checkMap.values()) { | ||
HandlerList.unregisterAll(existing); | ||
} | ||
checkMap.clear(); | ||
// Register checks again to account for changed config values | ||
registerChecks(); | ||
} | ||
|
||
private void loadLogThresholdExpression() { | ||
this.logThresholdExpression = new Expression(plugin.configString(Option.ANTI_AFK_LOG_THRESHOLD)); | ||
try { | ||
this.logThresholdExpression.validate(); | ||
} catch (ParseException e) { | ||
plugin.logger().warn("Failed to parse anti_afk.log_threshold expression: " + e.getMessage()); | ||
e.printStackTrace(); | ||
} | ||
} | ||
|
||
private void registerChecks() { | ||
for (CheckType type : CheckType.values()) { | ||
registerCheck(type); | ||
} | ||
} | ||
|
||
public AuraSkills getPlugin() { | ||
return plugin; | ||
} | ||
|
||
@Nullable | ||
public Check getCheck(CheckType type) { | ||
return checkMap.get(type); | ||
} | ||
|
||
public CheckData getCheckData(Player player, CheckType type) { | ||
return ((BukkitUser) plugin.getUser(player)).getCheckData(type); | ||
} | ||
|
||
public Expression getLogThresholdExpression() { | ||
return logThresholdExpression; | ||
} | ||
|
||
public void logAndNotifyFail(Player player, CheckType type, CheckData checkData) { | ||
String message = TextUtil.replace(plugin.getMsg(CommandMessage.ANTIAFK_FAILED, plugin.getDefaultLanguage()), | ||
"{player}", player.getName(), | ||
"{check}", type.name(), | ||
"{count}", String.valueOf(checkData.getCount())); | ||
|
||
Location loc = player.getLocation(); | ||
BlockPosition coords = new BlockPosition(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()); | ||
@Nullable World world = loc.getWorld(); | ||
String worldName = world != null ? world.getName() : ""; | ||
|
||
// Log message | ||
User user = plugin.getUser(player); | ||
var log = new AntiAfkLog(System.currentTimeMillis(), message, coords, worldName); | ||
user.getSessionAntiAfkLogs().add(log); | ||
|
||
// Send to online players with notify permission | ||
for (Player notified : Bukkit.getOnlinePlayers()) { | ||
if (!notified.hasPermission("auraskills.antiafk.notify")) { | ||
continue; | ||
} | ||
|
||
notified.sendMessage(message); | ||
} | ||
} | ||
|
||
private void registerCheck(CheckType type) { | ||
Class<? extends Check> checkClass = type.getCheckClass(); | ||
try { | ||
Constructor<?> constructor = checkClass.getDeclaredConstructor(CheckType.class, AntiAfkManager.class); | ||
Object checkObj = constructor.newInstance(type, this); | ||
if (checkObj instanceof Check check) { | ||
plugin.getServer().getPluginManager().registerEvents(check, plugin); | ||
checkMap.put(type, check); | ||
} | ||
} catch (NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException e) { | ||
plugin.logger().warn("Failed to register check of type " + type); | ||
e.printStackTrace(); | ||
} | ||
} | ||
} |
81 changes: 81 additions & 0 deletions
81
bukkit/src/main/java/dev/aurelium/auraskills/bukkit/antiafk/Check.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
package dev.aurelium.auraskills.bukkit.antiafk; | ||
|
||
import com.ezylang.evalex.EvaluationException; | ||
import com.ezylang.evalex.parser.ParseException; | ||
import dev.aurelium.auraskills.bukkit.AuraSkills; | ||
import dev.aurelium.auraskills.common.config.Option; | ||
import org.bukkit.entity.Player; | ||
import org.bukkit.event.Listener; | ||
|
||
import java.util.Locale; | ||
|
||
public class Check implements Listener { | ||
|
||
private final CheckType type; | ||
private final AntiAfkManager manager; | ||
private final AuraSkills plugin; | ||
private final String CONFIG_PREFIX; | ||
private final Option ENABLED_OPTION; | ||
private final int LOG_THRESHOLD; | ||
|
||
public Check(CheckType type, AntiAfkManager manager) { | ||
this.type = type; | ||
this.manager = manager; | ||
this.plugin = manager.getPlugin(); | ||
this.CONFIG_PREFIX = "ANTI_AFK_CHECKS_" + type.toString() + "_"; | ||
this.ENABLED_OPTION = Option.valueOf(CONFIG_PREFIX + "ENABLED"); | ||
int minCount = optionInt("min_count"); | ||
int logThresholdParsed; | ||
try { | ||
logThresholdParsed = manager.getLogThresholdExpression() | ||
.with("min_count", minCount) | ||
.evaluate() | ||
.getNumberValue() | ||
.intValue(); | ||
} catch (EvaluationException | ParseException e) { | ||
plugin.logger().warn("Failed to evaluate anti_afk.log_threshold expression: " + e.getMessage()); | ||
e.printStackTrace(); | ||
logThresholdParsed = minCount; // Fallback value | ||
} | ||
this.LOG_THRESHOLD = logThresholdParsed; | ||
} | ||
|
||
public int getLogThreshold() { | ||
return LOG_THRESHOLD; | ||
} | ||
|
||
protected CheckData getCheckData(Player player) { | ||
return manager.getCheckData(player, type); | ||
} | ||
|
||
protected void logFail(Player player) { | ||
if (!plugin.configBoolean(Option.ANTI_AFK_LOGGING_ENABLED)) return; | ||
|
||
CheckData checkData = getCheckData(player); | ||
if (checkData.getLogCount() >= LOG_THRESHOLD) { | ||
manager.logAndNotifyFail(player, type, checkData); | ||
checkData.resetLogCount(); | ||
} | ||
} | ||
|
||
protected boolean isDisabled() { | ||
return !plugin.configBoolean(ENABLED_OPTION); | ||
} | ||
|
||
protected int optionInt(String option) { | ||
return plugin.configInt(Option.valueOf(CONFIG_PREFIX + option.toUpperCase(Locale.ROOT))); | ||
} | ||
|
||
protected double optionDouble(String option) { | ||
return plugin.configDouble(Option.valueOf(CONFIG_PREFIX + option.toUpperCase(Locale.ROOT))); | ||
} | ||
|
||
protected String optionString(String option) { | ||
return plugin.configString(Option.valueOf(CONFIG_PREFIX + option.toUpperCase(Locale.ROOT))); | ||
} | ||
|
||
protected boolean optionBoolean(String option) { | ||
return plugin.configBoolean(Option.valueOf(CONFIG_PREFIX + option.toUpperCase(Locale.ROOT))); | ||
} | ||
|
||
} |
45 changes: 45 additions & 0 deletions
45
bukkit/src/main/java/dev/aurelium/auraskills/bukkit/antiafk/CheckData.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
package dev.aurelium.auraskills.bukkit.antiafk; | ||
|
||
import java.util.HashMap; | ||
import java.util.Map; | ||
|
||
public class CheckData { | ||
|
||
private final Map<String, Object> cache = new HashMap<>(); | ||
|
||
public <T> T getCache(String key, Class<T> type, T def) { | ||
Object val = cache.get(key); | ||
if (val != null) { | ||
return type.cast(val); | ||
} else { | ||
return def; | ||
} | ||
} | ||
|
||
public void setCache(String key, Object value) { | ||
cache.put(key, value); | ||
} | ||
|
||
public int getCount() { | ||
return getCache("count", Integer.class, 0); | ||
} | ||
|
||
public void incrementCount() { | ||
setCache("count", getCache("count", Integer.class, 0) + 1); | ||
setCache("log_count", getCache("log_count", Integer.class, 0) + 1); | ||
} | ||
|
||
public void resetCount() { | ||
setCache("count", 0); | ||
setCache("log_count", 0); | ||
} | ||
|
||
public int getLogCount() { | ||
return getCache("log_count", Integer.class, 0); | ||
} | ||
|
||
public void resetLogCount() { | ||
setCache("log_count", 0); | ||
} | ||
|
||
} |
25 changes: 25 additions & 0 deletions
25
bukkit/src/main/java/dev/aurelium/auraskills/bukkit/antiafk/CheckType.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package dev.aurelium.auraskills.bukkit.antiafk; | ||
|
||
import dev.aurelium.auraskills.bukkit.antiafk.checks.*; | ||
|
||
public enum CheckType { | ||
|
||
BLOCK_A(BlockA.class), | ||
DAMAGE_A(DamageA.class), | ||
DAMAGE_B(DamageB.class), | ||
DAMAGE_C(DamageC.class), | ||
ENTITY_A(EntityA.class), | ||
ENTITY_B(EntityB.class), | ||
ENTITY_C(EntityC.class), | ||
FISHING_A(FishingA.class); | ||
|
||
private final Class<? extends Check> checkClass; | ||
|
||
CheckType(Class<? extends Check> checkClass) { | ||
this.checkClass = checkClass; | ||
} | ||
|
||
public Class<? extends Check> getCheckClass() { | ||
return checkClass; | ||
} | ||
} |
33 changes: 33 additions & 0 deletions
33
bukkit/src/main/java/dev/aurelium/auraskills/bukkit/antiafk/checks/BlockA.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package dev.aurelium.auraskills.bukkit.antiafk.checks; | ||
|
||
import dev.aurelium.auraskills.api.event.skill.XpGainEvent; | ||
import dev.aurelium.auraskills.api.source.type.BlockXpSource; | ||
import dev.aurelium.auraskills.bukkit.antiafk.AntiAfkManager; | ||
import dev.aurelium.auraskills.bukkit.antiafk.Check; | ||
import dev.aurelium.auraskills.bukkit.antiafk.CheckType; | ||
import dev.aurelium.auraskills.bukkit.antiafk.handler.FacingHandler; | ||
import org.bukkit.entity.Player; | ||
import org.bukkit.event.EventHandler; | ||
import org.bukkit.event.EventPriority; | ||
|
||
public class BlockA extends Check { | ||
|
||
private final FacingHandler handler; | ||
|
||
public BlockA(CheckType type, AntiAfkManager manager) { | ||
super(type, manager); | ||
this.handler = new FacingHandler(optionInt("min_count")); | ||
} | ||
|
||
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) | ||
public void onXpGain(XpGainEvent event) { | ||
if (isDisabled() || !(event.getSource() instanceof BlockXpSource)) return; | ||
|
||
Player player = event.getPlayer(); | ||
if (handler.failsCheck(getCheckData(player), player)) { | ||
event.setCancelled(true); | ||
logFail(player); | ||
} | ||
} | ||
|
||
} |
33 changes: 33 additions & 0 deletions
33
bukkit/src/main/java/dev/aurelium/auraskills/bukkit/antiafk/checks/DamageA.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package dev.aurelium.auraskills.bukkit.antiafk.checks; | ||
|
||
import dev.aurelium.auraskills.api.event.skill.XpGainEvent; | ||
import dev.aurelium.auraskills.api.source.type.DamageXpSource; | ||
import dev.aurelium.auraskills.bukkit.antiafk.AntiAfkManager; | ||
import dev.aurelium.auraskills.bukkit.antiafk.Check; | ||
import dev.aurelium.auraskills.bukkit.antiafk.CheckType; | ||
import dev.aurelium.auraskills.bukkit.antiafk.handler.PositionHandler; | ||
import org.bukkit.entity.Player; | ||
import org.bukkit.event.EventHandler; | ||
import org.bukkit.event.EventPriority; | ||
|
||
public class DamageA extends Check { | ||
|
||
private final PositionHandler handler; | ||
|
||
public DamageA(CheckType type, AntiAfkManager manager) { | ||
super(type, manager); | ||
this.handler = new PositionHandler(optionDouble("max_distance"), optionInt("min_count")); | ||
} | ||
|
||
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) | ||
public void onXpGain(XpGainEvent event) { | ||
if (isDisabled() || !(event.getSource() instanceof DamageXpSource)) return; | ||
|
||
Player player = event.getPlayer(); | ||
if (handler.failsCheck(getCheckData(player), player)) { | ||
event.setCancelled(true); | ||
logFail(player); | ||
} | ||
} | ||
|
||
} |
Oops, something went wrong.