src/main/java/fr/xephi/authme/service/AntiBotService.java
package fr.xephi.authme.service;
import fr.xephi.authme.initialization.SettingsDependent;
import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.message.Messages;
import fr.xephi.authme.permission.AdminPermission;
import fr.xephi.authme.permission.PermissionsManager;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.ProtectionSettings;
import fr.xephi.authme.util.AtomicIntervalCounter;
import org.bukkit.scheduler.BukkitTask;
import javax.inject.Inject;
import java.util.Locale;
import java.util.concurrent.CopyOnWriteArrayList;
import static fr.xephi.authme.service.BukkitService.TICKS_PER_MINUTE;
import static fr.xephi.authme.service.BukkitService.TICKS_PER_SECOND;
/**
* The AntiBot Service Management class.
*/
public class AntiBotService implements SettingsDependent {
// Instances
private final Messages messages;
private final PermissionsManager permissionsManager;
private final BukkitService bukkitService;
private final CopyOnWriteArrayList<String> antibotKicked = new CopyOnWriteArrayList<>();
// Settings
private int duration;
// Service status
private AntiBotStatus antiBotStatus;
private boolean startup;
private BukkitTask disableTask;
private AtomicIntervalCounter flaggedCounter;
@Inject
AntiBotService(Settings settings, Messages messages, PermissionsManager permissionsManager,
BukkitService bukkitService) {
// Instances
this.messages = messages;
this.permissionsManager = permissionsManager;
this.bukkitService = bukkitService;
// Initial status
disableTask = null;
antiBotStatus = AntiBotStatus.DISABLED;
startup = true;
// Load settings and start if required
reload(settings);
}
@Override
public void reload(Settings settings) {
// Load settings
duration = settings.getProperty(ProtectionSettings.ANTIBOT_DURATION);
int sensibility = settings.getProperty(ProtectionSettings.ANTIBOT_SENSIBILITY);
int interval = settings.getProperty(ProtectionSettings.ANTIBOT_INTERVAL);
flaggedCounter = new AtomicIntervalCounter(sensibility, interval * 1000);
// Stop existing protection
stopProtection();
antiBotStatus = AntiBotStatus.DISABLED;
// If antibot is disabled, just stop
if (!settings.getProperty(ProtectionSettings.ENABLE_ANTIBOT)) {
return;
}
// Bot activation task
Runnable enableTask = () -> antiBotStatus = AntiBotStatus.LISTENING;
// Delay the schedule on first start
if (startup) {
int delay = settings.getProperty(ProtectionSettings.ANTIBOT_DELAY);
bukkitService.scheduleSyncDelayedTask(enableTask, delay * TICKS_PER_SECOND);
startup = false;
} else {
enableTask.run();
}
}
/**
* Transitions the anti bot service to an active status.
*/
private void startProtection() {
if (antiBotStatus == AntiBotStatus.ACTIVE) {
return; // Already activating/active
}
if (disableTask != null) {
disableTask.cancel();
}
// Schedule auto-disable
disableTask = bukkitService.runTaskLater(this::stopProtection, duration * TICKS_PER_MINUTE);
antiBotStatus = AntiBotStatus.ACTIVE;
bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(() -> {
// Inform admins
bukkitService.getOnlinePlayers().stream()
.filter(player -> permissionsManager.hasPermission(player, AdminPermission.ANTIBOT_MESSAGES))
.forEach(player -> messages.send(player, MessageKey.ANTIBOT_AUTO_ENABLED_MESSAGE));
});
}
/**
* Transitions the anti bot service from active status back to listening.
*/
private void stopProtection() {
if (antiBotStatus != AntiBotStatus.ACTIVE) {
return;
}
// Change status
antiBotStatus = AntiBotStatus.LISTENING;
flaggedCounter.reset();
antibotKicked.clear();
// Cancel auto-disable task
disableTask.cancel();
disableTask = null;
// Inform admins
String durationString = Integer.toString(duration);
bukkitService.getOnlinePlayers().stream()
.filter(player -> permissionsManager.hasPermission(player, AdminPermission.ANTIBOT_MESSAGES))
.forEach(player -> messages.send(player, MessageKey.ANTIBOT_AUTO_DISABLED_MESSAGE, durationString));
}
/**
* Returns the status of the AntiBot service.
*
* @return status of the antibot service
*/
public AntiBotStatus getAntiBotStatus() {
return antiBotStatus;
}
/**
* Allows to override the status of the protection.
*
* @param started the new protection status
*/
public void overrideAntiBotStatus(boolean started) {
if (antiBotStatus != AntiBotStatus.DISABLED) {
if (started) {
startProtection();
} else {
stopProtection();
}
}
}
/**
* Returns if a player should be kicked due to antibot service.
*
* @return if the player should be kicked
*/
public boolean shouldKick() {
if (antiBotStatus == AntiBotStatus.DISABLED) {
return false;
} else if (antiBotStatus == AntiBotStatus.ACTIVE) {
return true;
}
if (flaggedCounter.handle()) {
startProtection();
return true;
}
return false;
}
/**
* Returns whether the player was kicked because of activated antibot. The list is reset
* when antibot is deactivated.
*
* @param name the name to check
*
* @return true if the given name has been kicked because of Antibot
*/
public boolean wasPlayerKicked(String name) {
return antibotKicked.contains(name.toLowerCase(Locale.ROOT));
}
/**
* Adds a name to the list of players kicked by antibot. Should only be used when a player
* is determined to be kicked because of failed antibot verification.
*
* @param name the name to add
*/
public void addPlayerKick(String name) {
antibotKicked.addIfAbsent(name.toLowerCase(Locale.ROOT));
}
public enum AntiBotStatus {
LISTENING,
DISABLED,
ACTIVE
}
}