New check "chat.spam" with configuration and permissions

This commit is contained in:
Evenprime 2011-09-11 19:07:38 +02:00
parent af2242844f
commit 1480ebb3db
14 changed files with 252 additions and 23 deletions

View File

@ -20,6 +20,7 @@ permissions:
nocheat.checks.blockbreak.*: true
nocheat.checks.blockplace.*: true
nocheat.checks.interact.*: true
nocheat.checks.chat.*: true
nocheat.checks.moving.*:
description: Allow the player to bypass all moving checks
@ -47,6 +48,11 @@ permissions:
description: Allow the player to bypass all interact checks
children:
nocheat.checks.interact.durability: true
nocheat.checks.chat.*:
description: Allow the player to bypass all chat checks
children:
nocheat.checks.chat.spam: true
nocheat.checks.moving.flying:
description: Allow a player to move free through the air (if given, the "running" check will be ignored anyway)
@ -84,6 +90,10 @@ permissions:
nocheat.checks.interact.durability:
description: Allow a player to use an infinite durability item hack
default: op
nocheat.checks.chat.spam:
description: Allow a player to send an infinite amount of chat messages
default: op
nocheat.admin.*:
description: Give a player all admin rights

View File

@ -231,6 +231,30 @@ public class DefaultConfiguration {
actions.add(0, "durabilityLog interactCancel");
}
}
/****** CHAT ******/
{
ParentOption chatNode = new ParentOption("chat");
root.add(chatNode);
chatNode.add(new BooleanOption("check", true, true));
/**** CHAT.SPAM ****/
{
ParentOption spamNode = new ParentOption("spam");
chatNode.add(spamNode);
spamNode.add(new BooleanOption("check", false, true));
spamNode.add(new IntegerOption("timeframe", 5));
spamNode.add(new IntegerOption("limit", 5));
ActionListOption actions = new ActionListOption("actions");
spamNode.add(actions);
actions.add(0, "spamLog spamCancel");
}
}
return root;
}
@ -312,6 +336,7 @@ public class DefaultConfiguration {
w(w, "log directionLog 2 1 med NC: [player] failed [check]: tried to destroy a block out of line of sight.");
w(w, "log durabilityLog 0 1 med NC: [player] failed [check]: tried to use infinity durability hack.");
w(w, "log onliquidLog 2 1 med NC: [player] failed [check]: tried to place a block on liquids.");
w(w, "log spamLog 0 4 med NC: [player] failed [check]: Last sent message \"[text]\".");
w(w, "");
w(w, "# SPECIAL Actions: They will do something check dependant, usually cancel an event.");
w(w, "# - They start with the word 'special'");
@ -325,6 +350,7 @@ public class DefaultConfiguration {
w(w, "special blockbreakCancel 0 0");
w(w, "special blockplaceCancel 0 0");
w(w, "special interactCancel 0 0");
w(w, "special spamCancel 0 0");
w(w, "");
w(w, "# CONSOLECOMMAND Actions: They will execute a command as if it were typed into the console.");
w(w, "# - They start with the word 'consolecommand'");

View File

@ -66,6 +66,14 @@ public class Explainations {
set("interact.durability.check", "If true, check if a player is using a hack that provides infinite durability items.");
set("interact.durability.actions", "What should be done if a player is trying to use the hack.\nUnit is number of uses or attempts to use the hack.");
set("chat.check", "If true, do various checks on PlayerChat events.");
set("chat.spam.check", "If true, check if a player is spamming the chat.");
set("interact.spam.timeframe", "Over what timeframe (in seconds) should the messages be counted?");
set("interact.spam.limit", "How many messages per timeframe may the player send?");
set("interact.spam.actions", "What should be done if a player is trying to spam the chat.\nUnit is number of chat messages above the given limit.");
}
private static void set(String id, String text) {

View File

@ -9,6 +9,7 @@ import cc.co.evenprime.bukkit.nocheat.data.DataManager;
import cc.co.evenprime.bukkit.nocheat.events.BlockPlaceEventManager;
import cc.co.evenprime.bukkit.nocheat.events.BlockBreakEventManager;
import cc.co.evenprime.bukkit.nocheat.events.PlayerChatEventManager;
import cc.co.evenprime.bukkit.nocheat.events.PlayerItemDropEventManager;
import cc.co.evenprime.bukkit.nocheat.events.PlayerInteractEventManager;
import cc.co.evenprime.bukkit.nocheat.events.PlayerMoveEventManager;
@ -37,6 +38,7 @@ public class NoCheat extends JavaPlugin {
private BlockPlaceEventManager eventBlockPlaceManager;
private PlayerInteractEventManager eventPlayerInteractManager;
private PlayerItemDropEventManager eventPlayerItemDropManager;
private PlayerChatEventManager eventPlayerChatManager;
private int taskId = -1;
private int ingameseconds = 0;
@ -81,6 +83,7 @@ public class NoCheat extends JavaPlugin {
eventBlockPlaceManager = new BlockPlaceEventManager(this);
eventPlayerItemDropManager = new PlayerItemDropEventManager(this);
eventPlayerInteractManager = new PlayerInteractEventManager(this);
eventPlayerChatManager = new PlayerChatEventManager(this);
PluginDescriptionFile pdfFile = this.getDescription();

View File

@ -15,6 +15,7 @@ public class Permissions {
private final static String _BLOCKBREAK = _CHECKS + ".blockbreak";
private final static String _BLOCKPLACE = _CHECKS + ".blockplace";
private final static String _INTERACT = _CHECKS + ".interact";
private final static String _CHAT = _CHECKS + ".chat";
public final static String MOVE = _CHECKS + ".moving.*";
public final static String MOVE_FLY = _MOVE + ".flying";
@ -33,9 +34,11 @@ public class Permissions {
public final static String BLOCKPLACE = _CHECKS + ".blockplace.*";
public final static String BLOCKPLACE_ONLIQUID = _BLOCKPLACE + ".onliquid";
public static final String BLOCKPLACE_REACH = _BLOCKPLACE + ".reach";
public final static String BLOCKPLACE_REACH = _BLOCKPLACE + ".reach";
public final static String CHAT = _CHECKS + ".chat.*";
public final static String CHAT_SPAM = _CHAT + ".spam";
public final static String ADMIN_CHATLOG = _ADMIN + ".chatlog";
private Permissions() {}

View File

@ -23,7 +23,8 @@ public class LogAction extends Action {
public static final String DISTANCE = "\\[distance\\]";
public static final String LOCATION_TO = "\\[locationto\\]";
public static final String CHECK = "\\[check\\]";
public static final String PACKETS = "\\[packets\\]"; ;
public static final String PACKETS = "\\[packets\\]";
public static final String TEXT = "\\[text\\]";
public final LogLevel level;
private final String message;

View File

@ -0,0 +1,62 @@
package cc.co.evenprime.bukkit.nocheat.checks.chat;
import java.util.HashMap;
import org.bukkit.entity.Player;
import cc.co.evenprime.bukkit.nocheat.NoCheat;
import cc.co.evenprime.bukkit.nocheat.Permissions;
import cc.co.evenprime.bukkit.nocheat.actions.ActionExecutor;
import cc.co.evenprime.bukkit.nocheat.actions.ActionExecutorWithHistory;
import cc.co.evenprime.bukkit.nocheat.actions.types.LogAction;
import cc.co.evenprime.bukkit.nocheat.config.cache.ConfigurationCache;
import cc.co.evenprime.bukkit.nocheat.data.ChatData;
/**
*
* @author Evenprime
*
*/
public class ChatCheck {
private final ActionExecutor action;
private final NoCheat plugin;
public ChatCheck(NoCheat plugin) {
action = new ActionExecutorWithHistory(plugin);
this.plugin = plugin;
}
public boolean check(Player player, String message, ChatData data, ConfigurationCache cc) {
boolean cancel = false;
boolean spamCheck = cc.chat.spamCheck && !player.hasPermission(Permissions.CHAT_SPAM);
if(spamCheck) {
int time = plugin.getIngameSeconds();
if(data.spamLasttime + cc.chat.spamTimeframe <= plugin.getIngameSeconds()) {
data.spamLasttime = time;
data.messageCount = 0;
}
data.messageCount++;
if(data.messageCount > cc.chat.spamLimit) {
// Prepare some event-specific values for logging and custom
// actions
HashMap<String, String> params = new HashMap<String, String>();
params.put(LogAction.CHECK, "chat.spam");
params.put(LogAction.TEXT, message);
cancel = action.executeActions(player, cc.chat.spamActions, data.messageCount - cc.chat.spamLimit, params, cc);
}
}
return cancel;
}
}

View File

@ -0,0 +1,23 @@
package cc.co.evenprime.bukkit.nocheat.config.cache;
import cc.co.evenprime.bukkit.nocheat.actions.ActionList;
import cc.co.evenprime.bukkit.nocheat.config.Configuration;
public class CCChat {
public final boolean check;
public final boolean spamCheck;
public final int spamTimeframe;
public final int spamLimit;
public final ActionList spamActions;
public CCChat(Configuration data) {
check = data.getBoolean("chat.check");
spamCheck = data.getBoolean("chat.spam.check");
spamTimeframe = data.getInteger("chat.spam.timeframe");
spamLimit = data.getInteger("chat.spam.limit");
spamActions = data.getActionList("chat.spam.actions");
}
}

View File

@ -13,11 +13,12 @@ import cc.co.evenprime.bukkit.nocheat.config.Configuration;
*/
public class ConfigurationCache {
public final CCMoving moving;
public final CCLogging logging;
public final CCMoving moving;
public final CCLogging logging;
public final CCBlockBreak blockbreak;
public final CCInteract interact;
public final CCBlockPlace blockplace;
public final CCInteract interact;
public final CCBlockPlace blockplace;
public final CCChat chat;
/**
* Instantiate a config cache and populate it with the data of a
@ -31,6 +32,7 @@ public class ConfigurationCache {
blockbreak = new CCBlockBreak(data);
blockplace = new CCBlockPlace(data);
interact = new CCInteract(data);
chat = new CCChat(data);
logging = new CCLogging(data, worldSpecificFileLogger);
}

View File

@ -0,0 +1,13 @@
package cc.co.evenprime.bukkit.nocheat.data;
/**
*
* @author Evenprime
*
*/
public class ChatData {
public int messageCount = 0;
public int spamLasttime = 0;
}

View File

@ -5,7 +5,6 @@ import java.util.Map;
import org.bukkit.entity.Player;
/**
* Provide secure access to player-specific data objects for various checks or
* check groups
@ -19,7 +18,8 @@ public class DataManager {
private final Map<Player, MovingData> movingData = new HashMap<Player, MovingData>();
private final Map<Player, BlockBreakData> blockbreakData = new HashMap<Player, BlockBreakData>();
private final Map<Player, InteractData> interactData = new HashMap<Player, InteractData>();
private final Map<Player, BlockPlaceData> blockPlaceData = new HashMap<Player, BlockPlaceData>();
private final Map<Player, BlockPlaceData> blockPlaceData = new HashMap<Player, BlockPlaceData>();
private final Map<Player, ChatData> chatData = new HashMap<Player, ChatData>();
public DataManager() {
@ -86,4 +86,20 @@ public class DataManager {
return data;
}
public ChatData getChatData(Player player) {
ChatData data;
// intentionally not thread-safe, because bukkit events are handled
// in sequence anyway, so zero chance of two events of the same
// player being handled at the same time
// And if it still happens by accident, it's no real loss anyway
data = chatData.get(player);
if(data == null) {
data = new ChatData();
chatData.put(player, data);
}
return data;
}
}

View File

@ -0,0 +1,62 @@
package cc.co.evenprime.bukkit.nocheat.events;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.Event;
import org.bukkit.event.Event.Priority;
import org.bukkit.event.player.PlayerChatEvent;
import org.bukkit.event.player.PlayerListener;
import org.bukkit.plugin.PluginManager;
import cc.co.evenprime.bukkit.nocheat.NoCheat;
import cc.co.evenprime.bukkit.nocheat.Permissions;
import cc.co.evenprime.bukkit.nocheat.checks.chat.ChatCheck;
import cc.co.evenprime.bukkit.nocheat.config.ConfigurationManager;
import cc.co.evenprime.bukkit.nocheat.config.cache.ConfigurationCache;
import cc.co.evenprime.bukkit.nocheat.data.ChatData;
import cc.co.evenprime.bukkit.nocheat.data.DataManager;
public class PlayerChatEventManager extends PlayerListener {
private final ChatCheck chatCheck;
private final DataManager data;
private final ConfigurationManager config;
public PlayerChatEventManager(NoCheat plugin) {
this.data = plugin.getDataManager();
this.config = plugin.getConfigurationManager();
this.chatCheck = new ChatCheck(plugin);
PluginManager pm = Bukkit.getServer().getPluginManager();
pm.registerEvent(Event.Type.PLAYER_CHAT, this, Priority.High, plugin);
}
@Override
public void onPlayerChat(PlayerChatEvent event) {
if(event.isCancelled()) {
return;
}
final Player player = event.getPlayer();
final ConfigurationCache cc = config.getConfigurationCacheForWorld(player.getWorld().getName());
// Find out if checks need to be done for that player
if(cc.chat.check && !player.hasPermission(Permissions.CHAT)) {
boolean cancel = false;
// Get the player-specific stored data that applies here
final ChatData data = this.data.getChatData(player);
cancel = chatCheck.check(player, event.getMessage(), data, cc);
if(cancel) {
event.setCancelled(true);
}
}
}
}

View File

@ -19,7 +19,7 @@ import cc.co.evenprime.bukkit.nocheat.data.InteractData;
/**
*
* @author Evenprime
*
*
*/
public class PlayerInteractEventManager extends PlayerListener {
@ -29,18 +29,18 @@ public class PlayerInteractEventManager extends PlayerListener {
public PlayerInteractEventManager(NoCheat plugin) {
this.data = plugin.getDataManager();
this.config = plugin.getConfigurationManager();
this.interactCheck = new InteractCheck(plugin);
PluginManager pm = Bukkit.getServer().getPluginManager();
this.data = plugin.getDataManager();
this.config = plugin.getConfigurationManager();
this.interactCheck = new InteractCheck(plugin);
pm.registerEvent(Event.Type.PLAYER_INTERACT, this, Priority.Lowest, plugin);
PluginManager pm = Bukkit.getServer().getPluginManager();
pm.registerEvent(Event.Type.PLAYER_INTERACT, this, Priority.Lowest, plugin);
}
@Override
public void onPlayerInteract(PlayerInteractEvent event) {
if(event.isCancelled()) {
return;
}
@ -52,17 +52,17 @@ public class PlayerInteractEventManager extends PlayerListener {
if(cc.interact.check && !player.hasPermission(Permissions.INTERACT)) {
boolean cancel = false;
// Get the player-specific stored data that applies here
final InteractData data = this.data.getInteractData(player);
cancel = interactCheck.check(player, data, cc);
if(cancel) {
event.setCancelled(true);
}
}
}
}

View File

@ -22,7 +22,7 @@ public class FlatConfigGenerator {
String s = "# Want to know what these options do? Read the descriptions.txt file.\r\n\r\n";
for(Option option : o.getChildOptions()) {
s += optionToFlatString(option);
s += optionToFlatString(option) + "\r\n";
}
return s;