RAW CODING: completely asynchronous chat (executeActions is synched

into main thread).
This commit is contained in:
asofold 2012-08-08 20:07:13 +02:00
parent 7f9d2d2c11
commit 889930fd9b
6 changed files with 177 additions and 52 deletions

View File

@ -12,6 +12,7 @@ import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.plugin.PluginDescriptionFile; import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.java.JavaPlugin;
import fr.neatmonster.nocheatplus.checks.ExecuteActionsEvent;
import fr.neatmonster.nocheatplus.checks.Workarounds; import fr.neatmonster.nocheatplus.checks.Workarounds;
import fr.neatmonster.nocheatplus.checks.blockbreak.BlockBreakListener; import fr.neatmonster.nocheatplus.checks.blockbreak.BlockBreakListener;
import fr.neatmonster.nocheatplus.checks.blockinteract.BlockInteractListener; import fr.neatmonster.nocheatplus.checks.blockinteract.BlockInteractListener;
@ -188,4 +189,9 @@ public class NoCheatPlus extends JavaPlugin implements Listener {
player.sendMessage(message); player.sendMessage(message);
} }
@EventHandler(priority=EventPriority.LOWEST)
final void onExecuteActions(final ExecuteActionsEvent event){
}
} }

View File

@ -0,0 +1,52 @@
package fr.neatmonster.nocheatplus.checks;
import org.bukkit.entity.Player;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
/**
* This event is to be fired to execute actions in the main thread.
* @author mc_dev
*
*/
public class ExecuteActionsEvent extends Event {
private static final HandlerList handlers = new HandlerList();
private final Check check;
final Player player;
/**
* If the actions have been executed already.
*/
private boolean actionsExecuted = false;
private boolean cancel = false;
public ExecuteActionsEvent(final Check check, final Player player){
this.check = check;
this.player = player;
}
@Override
public HandlerList getHandlers() {
return handlers;
}
/**
* Must have :_) ...
* @return
*/
public static HandlerList getHandlerList() {
return handlers;
}
public void executeActions(){
if (actionsExecuted) return;
cancel = check.executeActions(player);
actionsExecuted = true;
}
public boolean getCancel(){
return cancel;
}
}

View File

@ -34,8 +34,10 @@ public class ChatConfig {
* Clear all the configurations. * Clear all the configurations.
*/ */
public static void clear() { public static void clear() {
synchronized (worldsMap) {
worldsMap.clear(); worldsMap.clear();
} }
}
/** /**
* Gets the configuration for a specified player. * Gets the configuration for a specified player.
@ -45,11 +47,13 @@ public class ChatConfig {
* @return the configuration * @return the configuration
*/ */
public static ChatConfig getConfig(final Player player) { public static ChatConfig getConfig(final Player player) {
synchronized (worldsMap) {
if (!worldsMap.containsKey(player.getWorld().getName())) if (!worldsMap.containsKey(player.getWorld().getName()))
worldsMap.put(player.getWorld().getName(), worldsMap.put(player.getWorld().getName(),
new ChatConfig(ConfigManager.getConfigFile(player.getWorld().getName()))); new ChatConfig(ConfigManager.getConfigFile(player.getWorld().getName())));
return worldsMap.get(player.getWorld().getName()); return worldsMap.get(player.getWorld().getName());
} }
}
public final boolean arrivalsCheck; public final boolean arrivalsCheck;
public final int arrivalsJoinsLimit; public final int arrivalsJoinsLimit;

View File

@ -30,7 +30,7 @@ public class ChatData {
* the player * the player
* @return the data * @return the data
*/ */
public static ChatData getData(final Player player) { public synchronized static ChatData getData(final Player player) {
if (!playersMap.containsKey(player.getName())) if (!playersMap.containsKey(player.getName()))
playersMap.put(player.getName(), new ChatData()); playersMap.put(player.getName(), new ChatData());
return playersMap.get(player.getName()); return playersMap.get(player.getName());
@ -59,7 +59,7 @@ public class ChatData {
/** /**
* Clear the data of the no pwnage check. * Clear the data of the no pwnage check.
*/ */
public void clearNoPwnageData() { public synchronized void clearNoPwnageData() {
noPwnageCaptchTries = noPwnageReloginWarnings = 0; noPwnageCaptchTries = noPwnageReloginWarnings = 0;
noPwnageJoinTime = noPwnageLastMessageTime = noPwnageLastMovedTime = noPwnageLastWarningTime = noPwnageLeaveTime = noPwnageReloginWarningTime = 0L; noPwnageJoinTime = noPwnageLastMessageTime = noPwnageLastMovedTime = noPwnageLastWarningTime = noPwnageLeaveTime = noPwnageReloginWarningTime = 0L;
noPwnageGeneratedCaptcha = noPwnageLastMessage = ""; noPwnageGeneratedCaptcha = noPwnageLastMessage = "";

View File

@ -59,7 +59,7 @@ public class ChatListener implements Listener {
event.setMessage(color.check(player, event.getMessage())); event.setMessage(color.check(player, event.getMessage()));
// Then the no pwnage check. // Then the no pwnage check.
if (noPwnage.isEnabled(player) && noPwnage.check(player)) if (noPwnage.check(player, event, false))
player.kickPlayer(Check.removeColors(ChatConfig.getConfig(player).noPwnageKickMessage)); player.kickPlayer(Check.removeColors(ChatConfig.getConfig(player).noPwnageKickMessage));
} }
@ -105,7 +105,7 @@ public class ChatListener implements Listener {
event.setMessage(color.check(player, event.getMessage())); event.setMessage(color.check(player, event.getMessage()));
// Then the no pwnage check. // Then the no pwnage check.
if (noPwnage.isEnabled(player) && noPwnage.check(player)) if (noPwnage.check(player, event, true))
player.kickPlayer(Check.removeColors(ChatConfig.getConfig(player).noPwnageKickMessage)); player.kickPlayer(Check.removeColors(ChatConfig.getConfig(player).noPwnageKickMessage));
} }
@ -127,7 +127,7 @@ public class ChatListener implements Listener {
* |___/ * |___/
*/ */
final Player player = event.getPlayer(); final Player player = event.getPlayer();
final ChatConfig cc = ChatConfig.getConfig(player); final ChatConfig cc = ChatConfig.getConfig(player); // Non critical use (concurrency).
// First the arrivals check, if enabled of course. // First the arrivals check, if enabled of course.
if (arrivals.isEnabled(player) && arrivals.check(player)) if (arrivals.isEnabled(player) && arrivals.check(player))
@ -135,7 +135,7 @@ public class ChatListener implements Listener {
event.disallow(Result.KICK_OTHER, cc.arrivalsMessage); event.disallow(Result.KICK_OTHER, cc.arrivalsMessage);
// Then the no pwnage check, if the login isn't already disallowed. // Then the no pwnage check, if the login isn't already disallowed.
if (event.getResult() != Result.KICK_OTHER && noPwnage.isEnabled(player) && noPwnage.check(player)) if (event.getResult() != Result.KICK_OTHER && noPwnage.check(player))
event.disallow(Result.KICK_OTHER, cc.noPwnageReloginKickMessage); event.disallow(Result.KICK_OTHER, cc.noPwnageReloginKickMessage);
} }
} }

View File

@ -11,6 +11,7 @@ import org.bukkit.event.player.PlayerEvent;
import fr.neatmonster.nocheatplus.actions.ParameterName; import fr.neatmonster.nocheatplus.actions.ParameterName;
import fr.neatmonster.nocheatplus.checks.Check; import fr.neatmonster.nocheatplus.checks.Check;
import fr.neatmonster.nocheatplus.checks.CheckType; import fr.neatmonster.nocheatplus.checks.CheckType;
import fr.neatmonster.nocheatplus.checks.ExecuteActionsEvent;
import fr.neatmonster.nocheatplus.utilities.CheckUtils; import fr.neatmonster.nocheatplus.utilities.CheckUtils;
/* /*
@ -49,21 +50,43 @@ public class NoPwnage extends Check {
public NoPwnage() { public NoPwnage() {
super(CheckType.CHAT_NOPWNAGE); super(CheckType.CHAT_NOPWNAGE);
for (final Player player : Bukkit.getOnlinePlayers()) for (final Player player : Bukkit.getOnlinePlayers()){
ChatData.getData(player).noPwnageLastLocation = player.getLocation(); final ChatData data = ChatData.getData(player);
synchronized(data){
data.noPwnageLastLocation = player.getLocation();
}
}
} }
/** /**
* Checks a player (join). * Checks a player (join).
* *
* Only called from the main thread.
*
* @param player * @param player
* the player * the player
* @return true, if successful * @return true, if successful
*/ */
public boolean check(final Player player) { public boolean check(final Player player) {
if (!isEnabled(player)) return false;
final ChatConfig cc = ChatConfig.getConfig(player); final ChatConfig cc = ChatConfig.getConfig(player);
final ChatData data = ChatData.getData(player); final ChatData data = ChatData.getData(player);
synchronized(data){
return unsafeCheck(player, cc, data);
}
}
/**
* Check (Join), only call from synchronized code.
* @param player
* @param cc
* @param data
* @return
*/
private boolean unsafeCheck(final Player player, final ChatConfig cc, final ChatData data) {
boolean cancel = false; boolean cancel = false;
final long now = System.currentTimeMillis(); final long now = System.currentTimeMillis();
@ -80,7 +103,7 @@ public class NoPwnage extends Check {
data.noPwnageReloginWarnings++; data.noPwnageReloginWarnings++;
} else if (now - data.noPwnageReloginWarningTime < cc.noPwnageReloginWarningTimeout) } else if (now - data.noPwnageReloginWarningTime < cc.noPwnageReloginWarningTimeout)
// Find out if we need to ban the player or not. // Find out if we need to ban the player or not.
cancel = executeActions_(player); cancel = executeActionsThreadSafe(player, true);
} }
// Store his location and some other data. // Store his location and some other data.
@ -99,9 +122,28 @@ public class NoPwnage extends Check {
* the event * the event
* @return true, if successful * @return true, if successful
*/ */
public boolean check(final Player player, final PlayerEvent event) { public boolean check(final Player player, final PlayerEvent event, final boolean isMainThread) {
if (isMainThread && !isEnabled(player)) return false;
final ChatConfig cc = ChatConfig.getConfig(player); final ChatConfig cc = ChatConfig.getConfig(player);
final ChatData data = ChatData.getData(player); final ChatData data = ChatData.getData(player);
synchronized(data){
return unsafeCheck(player, event, isMainThread, cc, data);
}
}
/**
* Only to be called form synchronized code.
* @param player
* @param event
* @param isMainThread
* @param cc
* @param data
* @return
*/
private boolean unsafeCheck(final Player player, final PlayerEvent event, final boolean isMainThread, final ChatConfig cc, final ChatData data) {
data.noPwnageVL = 0D; data.noPwnageVL = 0D;
boolean cancel = false; boolean cancel = false;
@ -126,7 +168,7 @@ public class NoPwnage extends Check {
// Does he failed too much times? // Does he failed too much times?
if (data.noPwnageCaptchTries > cc.noPwnageCaptchaTries) if (data.noPwnageCaptchTries > cc.noPwnageCaptchaTries)
// Find out if we need to ban the player or not. // Find out if we need to ban the player or not.
cancel = executeActions_(player); cancel = executeActionsThreadSafe(player, isMainThread);
// Increment his tries number counter. // Increment his tries number counter.
data.noPwnageCaptchTries++; data.noPwnageCaptchTries++;
@ -223,7 +265,7 @@ public class NoPwnage extends Check {
((PlayerCommandPreprocessEvent) event).setCancelled(true); ((PlayerCommandPreprocessEvent) event).setCancelled(true);
// Find out if we need to ban the player or not. // Find out if we need to ban the player or not.
cancel = executeActions_(player); cancel = executeActionsThreadSafe(player, isMainThread);
} }
// Store the message and some other data. // Store the message and some other data.
@ -236,20 +278,40 @@ public class NoPwnage extends Check {
return cancel; return cancel;
} }
@Override
public final boolean executeActions(final Player player){
// To be called from synchronized code (ChatData).
// Late check of bypass permissions:
// (One might use a bypass flag, set if its already been checked and then reset.)
if (!isEnabled(player)) return false;
return super.executeActions(player);
}
/** /**
* Execute actions. * Execute actions from another thread (not the main thread).<br>
* * This does not use extra synchronization.
* @param player * @param player
* the player * @return
* @return true, if successful
*/ */
private boolean executeActions_(final Player player) { public final boolean executeActionsThreadSafe(final Player player, boolean isMainThread){
if (super.executeActions(player)) { if (isMainThread){
// Just execute.
if (executeActions(player)){
ChatData.getData(player).clearNoPwnageData(); ChatData.getData(player).clearNoPwnageData();
return true; return true;
} }
else
return false; return false;
} }
else {
// Sync it into the main thread by using an event.
final ExecuteActionsEvent event = new ExecuteActionsEvent(this, player);
Bukkit.getPluginManager().callEvent(event);
final boolean cancel = event.getCancel();
if (cancel) ChatData.getData(player).clearNoPwnageData();
return cancel;
}
}
/* (non-Javadoc) /* (non-Javadoc)
* @see fr.neatmonster.nocheatplus.checks.Check#getParameter(fr.neatmonster.nocheatplus.actions.ParameterName, org.bukkit.entity.Player) * @see fr.neatmonster.nocheatplus.checks.Check#getParameter(fr.neatmonster.nocheatplus.actions.ParameterName, org.bukkit.entity.Player)
@ -261,4 +323,5 @@ public class NoPwnage extends Check {
else else
return super.getParameter(wildcard, player); return super.getParameter(wildcard, player);
} }
} }