summaryrefslogtreecommitdiffstats
path: root/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/chat
diff options
context:
space:
mode:
Diffstat (limited to 'EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/chat')
-rw-r--r--EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/chat/ChatCheck.java79
-rw-r--r--EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/chat/ChatCheckListener.java108
-rw-r--r--EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/chat/ChatConfig.java64
-rw-r--r--EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/chat/ChatData.java22
-rw-r--r--EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/chat/ColorCheck.java50
-rw-r--r--EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/chat/SpamCheck.java96
6 files changed, 419 insertions, 0 deletions
diff --git a/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/chat/ChatCheck.java b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/chat/ChatCheck.java
new file mode 100644
index 000000000..b1f14deec
--- /dev/null
+++ b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/chat/ChatCheck.java
@@ -0,0 +1,79 @@
+package com.earth2me.essentials.anticheat.checks.chat;
+
+import com.earth2me.essentials.anticheat.NoCheat;
+import com.earth2me.essentials.anticheat.NoCheatPlayer;
+import com.earth2me.essentials.anticheat.actions.ParameterName;
+import com.earth2me.essentials.anticheat.checks.Check;
+import com.earth2me.essentials.anticheat.config.ConfigurationCacheStore;
+import com.earth2me.essentials.anticheat.data.DataStore;
+
+
+/**
+ * Abstract base class for Chat checks, provides some convenience methods for access to data and config that's relevant
+ * to this checktype
+ */
+public abstract class ChatCheck extends Check
+{
+ private static final String id = "chat";
+
+ public ChatCheck(NoCheat plugin, String name)
+ {
+ super(plugin, id, name);
+ }
+
+ @Override
+ public String getParameter(ParameterName wildcard, NoCheatPlayer player)
+ {
+
+ if (wildcard == ParameterName.TEXT)
+ // Filter colors from the players message when logging
+ {
+ return getData(player).message.replaceAll("\302\247.", "").replaceAll("\247.", "");
+ }
+ else
+ {
+ return super.getParameter(wildcard, player);
+ }
+ }
+
+ /**
+ * Get the "ChatData" object that belongs to the player. Will ensure that such a object exists and if not, create
+ * one
+ *
+ * @param player
+ * @return
+ */
+ public static ChatData getData(NoCheatPlayer player)
+ {
+ DataStore base = player.getDataStore();
+ ChatData data = base.get(id);
+ if (data == null)
+ {
+ data = new ChatData();
+ base.set(id, data);
+ }
+ return data;
+ }
+
+ /**
+ * Get the ChatConfig object that belongs to the world that the player currently resides in.
+ *
+ * @param player
+ * @return
+ */
+ public static ChatConfig getConfig(NoCheatPlayer player)
+ {
+ return getConfig(player.getConfigurationStore());
+ }
+
+ public static ChatConfig getConfig(ConfigurationCacheStore cache)
+ {
+ ChatConfig config = cache.get(id);
+ if (config == null)
+ {
+ config = new ChatConfig(cache.getConfiguration());
+ cache.set(id, config);
+ }
+ return config;
+ }
+}
diff --git a/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/chat/ChatCheckListener.java b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/chat/ChatCheckListener.java
new file mode 100644
index 000000000..965a374aa
--- /dev/null
+++ b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/chat/ChatCheckListener.java
@@ -0,0 +1,108 @@
+package com.earth2me.essentials.anticheat.checks.chat;
+
+import com.earth2me.essentials.anticheat.EventManager;
+import com.earth2me.essentials.anticheat.NoCheat;
+import com.earth2me.essentials.anticheat.NoCheatPlayer;
+import com.earth2me.essentials.anticheat.config.ConfigurationCacheStore;
+import com.earth2me.essentials.anticheat.config.Permissions;
+import java.util.LinkedList;
+import java.util.List;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.Listener;
+import org.bukkit.event.player.PlayerChatEvent;
+import org.bukkit.event.player.PlayerCommandPreprocessEvent;
+
+
+/**
+ * Central location to listen to events that are relevant for the chat checks
+ *
+ */
+public class ChatCheckListener implements Listener, EventManager
+{
+ private final SpamCheck spamCheck;
+ private final ColorCheck colorCheck;
+ private final NoCheat plugin;
+
+ public ChatCheckListener(NoCheat plugin)
+ {
+
+ this.plugin = plugin;
+
+ spamCheck = new SpamCheck(plugin);
+ colorCheck = new ColorCheck(plugin);
+ }
+
+ /**
+ * We listen to PlayerCommandPreprocess events because commands can be used for spamming too.
+ *
+ * @param event The PlayerCommandPreprocess Event
+ */
+ @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
+ public void commandPreprocess(final PlayerCommandPreprocessEvent event)
+ {
+ // This type of event is derived from PlayerChatEvent, therefore
+ // just treat it like that
+ chat(event);
+ }
+
+ /**
+ * We listen to PlayerChat events for obvious reasons
+ *
+ * @param event The PlayerChat event
+ */
+ @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
+ public void chat(final PlayerChatEvent event)
+ {
+ boolean cancelled = false;
+
+ final NoCheatPlayer player = plugin.getPlayer(event.getPlayer());
+ final ChatConfig cc = ChatCheck.getConfig(player);
+ final ChatData data = ChatCheck.getData(player);
+
+ // Remember the original message
+ data.message = event.getMessage();
+
+ // Now do the actual checks
+
+ // First the spam check
+ if (cc.spamCheck && !player.hasPermission(Permissions.CHAT_SPAM))
+ {
+ cancelled = spamCheck.check(player, data, cc);
+ }
+
+ // Second the color check
+ if (!cancelled && cc.colorCheck && !player.hasPermission(Permissions.CHAT_COLOR))
+ {
+ cancelled = colorCheck.check(player, data, cc);
+ }
+
+ // If one of the checks requested the event to be cancelled, do it
+ if (cancelled)
+ {
+ event.setCancelled(cancelled);
+ }
+ else
+ {
+ // In case one of the events modified the message, make sure that
+ // the new message gets used
+ event.setMessage(data.message);
+ }
+ }
+
+ public List<String> getActiveChecks(ConfigurationCacheStore cc)
+ {
+ LinkedList<String> s = new LinkedList<String>();
+
+ ChatConfig c = ChatCheck.getConfig(cc);
+ if (c.spamCheck)
+ {
+ s.add("chat.spam");
+ }
+ if (c.colorCheck)
+ {
+ s.add("chat.color");
+ }
+ return s;
+ }
+}
diff --git a/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/chat/ChatConfig.java b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/chat/ChatConfig.java
new file mode 100644
index 000000000..06ad5c9fc
--- /dev/null
+++ b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/chat/ChatConfig.java
@@ -0,0 +1,64 @@
+package com.earth2me.essentials.anticheat.checks.chat;
+
+import java.util.LinkedList;
+import java.util.List;
+import com.earth2me.essentials.anticheat.ConfigItem;
+import com.earth2me.essentials.anticheat.actions.types.ActionList;
+import com.earth2me.essentials.anticheat.config.ConfPaths;
+import com.earth2me.essentials.anticheat.config.NoCheatConfiguration;
+import com.earth2me.essentials.anticheat.config.Permissions;
+
+
+/**
+ * Configurations specific for the "Chat" checks Every world gets one of these assigned to it, or if a world doesn't get
+ * it's own, it will use the "global" version
+ *
+ */
+public class ChatConfig implements ConfigItem
+{
+ public final boolean spamCheck;
+ public final String[] spamWhitelist;
+ public final long spamTimeframe;
+ public final int spamMessageLimit;
+ public final int spamCommandLimit;
+ public final ActionList spamActions;
+ public final boolean colorCheck;
+ public final ActionList colorActions;
+
+ public ChatConfig(NoCheatConfiguration data)
+ {
+
+ spamCheck = data.getBoolean(ConfPaths.CHAT_SPAM_CHECK);
+ spamWhitelist = splitWhitelist(data.getString(ConfPaths.CHAT_SPAM_WHITELIST));
+ spamTimeframe = data.getInt(ConfPaths.CHAT_SPAM_TIMEFRAME) * 1000L;
+ spamMessageLimit = data.getInt(ConfPaths.CHAT_SPAM_MESSAGELIMIT);
+ spamCommandLimit = data.getInt(ConfPaths.CHAT_SPAM_COMMANDLIMIT);
+ spamActions = data.getActionList(ConfPaths.CHAT_SPAM_ACTIONS, Permissions.CHAT_SPAM);
+ colorCheck = data.getBoolean(ConfPaths.CHAT_COLOR_CHECK);
+ colorActions = data.getActionList(ConfPaths.CHAT_COLOR_ACTIONS, Permissions.CHAT_COLOR);
+ }
+
+ /**
+ * Convenience method to split a string into an array on every occurance of the "," character, removing all
+ * whitespaces before and after it too.
+ *
+ * @param string The string containing text seperated by ","
+ * @return An array of the seperate texts
+ */
+ private String[] splitWhitelist(String string)
+ {
+
+ List<String> strings = new LinkedList<String>();
+ string = string.trim();
+
+ for (String s : string.split(","))
+ {
+ if (s != null && s.trim().length() > 0)
+ {
+ strings.add(s.trim());
+ }
+ }
+
+ return strings.toArray(new String[strings.size()]);
+ }
+}
diff --git a/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/chat/ChatData.java b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/chat/ChatData.java
new file mode 100644
index 000000000..b05cb2579
--- /dev/null
+++ b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/chat/ChatData.java
@@ -0,0 +1,22 @@
+package com.earth2me.essentials.anticheat.checks.chat;
+
+import com.earth2me.essentials.anticheat.DataItem;
+
+
+/**
+ * Player specific data for the chat checks
+ *
+ */
+public class ChatData implements DataItem
+{
+ // Keep track of the violation levels for the two checks
+ public int spamVL;
+ public int colorVL;
+ // Count messages and commands
+ public int messageCount = 0;
+ public int commandCount = 0;
+ // Remember when the last check time period started
+ public long spamLastTime = 0;
+ // Remember the last chat message or command for logging purposes
+ public String message = "";
+}
diff --git a/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/chat/ColorCheck.java b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/chat/ColorCheck.java
new file mode 100644
index 000000000..2468c7065
--- /dev/null
+++ b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/chat/ColorCheck.java
@@ -0,0 +1,50 @@
+package com.earth2me.essentials.anticheat.checks.chat;
+
+import java.util.Locale;
+import com.earth2me.essentials.anticheat.NoCheat;
+import com.earth2me.essentials.anticheat.NoCheatPlayer;
+import com.earth2me.essentials.anticheat.actions.ParameterName;
+import com.earth2me.essentials.anticheat.data.Statistics.Id;
+
+
+public class ColorCheck extends ChatCheck
+{
+ public ColorCheck(NoCheat plugin)
+ {
+ super(plugin, "chat.color");
+ }
+
+ public boolean check(NoCheatPlayer player, ChatData data, ChatConfig cc)
+ {
+
+ if (data.message.contains("\247"))
+ {
+
+ data.colorVL += 1;
+ incrementStatistics(player, Id.CHAT_COLOR, 1);
+
+ boolean filter = executeActions(player, cc.colorActions, data.colorVL);
+
+ if (filter)
+ {
+ // Remove color codes
+ data.message = data.message.replaceAll("\302\247.", "").replaceAll("\247.", "");
+ }
+ }
+
+ return false;
+ }
+
+ public String getParameter(ParameterName wildcard, NoCheatPlayer player)
+ {
+
+ if (wildcard == ParameterName.VIOLATIONS)
+ {
+ return String.format(Locale.US, "%d", getData(player).colorVL);
+ }
+ else
+ {
+ return super.getParameter(wildcard, player);
+ }
+ }
+}
diff --git a/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/chat/SpamCheck.java b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/chat/SpamCheck.java
new file mode 100644
index 000000000..8bf893091
--- /dev/null
+++ b/EssentialsAntiCheat/src/com/earth2me/essentials/anticheat/checks/chat/SpamCheck.java
@@ -0,0 +1,96 @@
+package com.earth2me.essentials.anticheat.checks.chat;
+
+import com.earth2me.essentials.anticheat.NoCheat;
+import com.earth2me.essentials.anticheat.NoCheatPlayer;
+import com.earth2me.essentials.anticheat.actions.ParameterName;
+import com.earth2me.essentials.anticheat.data.Statistics.Id;
+import java.util.Locale;
+
+
+/**
+ * The SpamCheck will count messages and commands over a short timeframe to see if the player tried to send too many of
+ * them
+ *
+ */
+public class SpamCheck extends ChatCheck
+{
+ public SpamCheck(NoCheat plugin)
+ {
+ super(plugin, "chat.spam");
+ }
+
+ public boolean check(NoCheatPlayer player, ChatData data, ChatConfig cc)
+ {
+
+ boolean cancel = false;
+ // Maybe it's a command and on the whitelist
+ for (String s : cc.spamWhitelist)
+ {
+ if (data.message.startsWith(s))
+ {
+ // It is
+ return false;
+ }
+ }
+
+ int commandLimit = cc.spamCommandLimit;
+ int messageLimit = cc.spamMessageLimit;
+ long timeframe = cc.spamTimeframe;
+
+ final long time = System.currentTimeMillis();
+
+ // Has enough time passed? Then reset the counters
+ if (data.spamLastTime + timeframe <= time)
+ {
+ data.spamLastTime = time;
+ data.messageCount = 0;
+ data.commandCount = 0;
+ }
+ // Security check, if the system time changes
+ else if (data.spamLastTime > time)
+ {
+ data.spamLastTime = Integer.MIN_VALUE;
+ }
+
+ // Increment appropriate counter
+ if (data.message.startsWith("/"))
+ {
+ data.commandCount++;
+ }
+ else
+ {
+ data.messageCount++;
+ }
+
+ // Did the player go over the limit on at least one of the counters?
+ if (data.messageCount > messageLimit || data.commandCount > commandLimit)
+ {
+
+ // Set the vl as the number of messages above the limit and
+ // increment statistics
+ data.spamVL = Math.max(0, data.messageCount - messageLimit);
+ data.spamVL += Math.max(0, data.commandCount - commandLimit);
+ incrementStatistics(player, Id.CHAT_SPAM, 1);
+
+ // Execute whatever actions are associated with this check and the
+ // violation level and find out if we should cancel the event
+ cancel = executeActions(player, cc.spamActions, data.spamVL);
+ }
+
+ return cancel;
+ }
+
+ @Override
+ public String getParameter(ParameterName wildcard, NoCheatPlayer player)
+ {
+
+ if (wildcard == ParameterName.VIOLATIONS)
+ {
+ return String.format(Locale.US, "%d", getData(player).spamVL);
+ }
+ else
+ {
+ return super.getParameter(wildcard, player);
+ }
+ }
+}