[BLEEDING] Change message sending queue from per-player to global.

Adds an API to queue messages (thread safe).
This commit is contained in:
asofold 2013-07-14 22:04:42 +02:00
parent 821dc34d7a
commit cf1c89d646
6 changed files with 89 additions and 37 deletions

View File

@ -11,7 +11,7 @@ public class NCPAPIProvider {
private static NoCheatPlusAPI noCheatPlusAPI = null;
/**
* Get the registered API instance. This will work after the plugin has loaded (onLoad).
* Get the registered API instance. This will work after the plugin has loaded (onLoad), asynchronous calls should be possible, however calls after plugin disable or before it is loaded should fail.
*/
public static NoCheatPlusAPI getNoCheatPlusAPI(){
return noCheatPlusAPI;

View File

@ -5,7 +5,6 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import fr.neatmonster.nocheatplus.NCPAPIProvider;
@ -272,13 +271,7 @@ public class Text extends AsyncCheck implements INotifyReload{
}
}
else if (cc.chatWarningCheck && time - data.chatWarningTime > cc.chatWarningTimeout && (100f * accumulated / cc.textFreqNormLevel > cc.chatWarningLevel || 100f * shortTermAccumulated / cc.textFreqShortTermLevel > cc.chatWarningLevel)){
// TODO: In case this gets used more often, use the player tasks, at least once PlayerData can be used for async calls.
Bukkit.getScheduler().scheduleSyncDelayedTask(Bukkit.getPluginManager().getPlugin("NoCheatPlus"), new Runnable(){
@Override
public void run() {
player.sendMessage(ColorUtil.replaceColors(cc.chatWarningMessage));
};
});
NCPAPIProvider.getNoCheatPlusAPI().sendMessageOnTick(player.getName(), ColorUtil.replaceColors(cc.chatWarningMessage));
data.chatWarningTime = time;
}
else {

View File

@ -35,6 +35,13 @@ public interface NoCheatPlusAPI extends ComponentRegistry<Object>, ComponentRegi
*/
public int sendAdminNotifyMessage(final String message);
/**
* Thread-safe method to send a message to a player in a scheduled task. The scheduling preserves order of messages.
* @param playerName
* @param message
*/
public void sendMessageOnTick(final String playerName, final String message);
/**
* Allow login (remove from deny login map).

View File

@ -0,0 +1,66 @@
package fr.neatmonster.nocheatplus.players;
import java.util.LinkedList;
import java.util.List;
import org.bukkit.entity.Player;
import fr.neatmonster.nocheatplus.utilities.OnDemandTickListener;
/**
* Unregisters during delegateTick to achieve thread-safety.
* @author mc_dev
*
*/
public class PlayerMessageSender extends OnDemandTickListener {
private final class MessageEntry{
public final String playerName;
public final String message;
public MessageEntry(final String playerName, final String message){
this.playerName = playerName;
this.message = message;
}
}
/** Queued entries, also used as lock. */
private final List<MessageEntry> messageEntries = new LinkedList<MessageEntry>();
@Override
public boolean delegateTick(int tick, long timeLast) {
// Copy entries.
final MessageEntry[] entries;
synchronized (messageEntries) {
entries = new MessageEntry[messageEntries.size()];
messageEntries.toArray(entries);
messageEntries.clear();
}
// Do messaging.
for (int i = 0; i < entries.length; i++){
final MessageEntry entry = entries[i];
final Player player = DataManager.getPlayerExact(entry.playerName);
if (player != null && player.isOnline()){
player.sendMessage(entry.message);
}
}
// Unregister if no further entries are there.
synchronized (messageEntries) {
if (messageEntries.isEmpty()){
// Force unregister.
unRegister(true);
}
}
// Always continue here to never use external setRegistered.
return true;
}
public void sendMessageThreadSafe(final String playerName, final String message){
final MessageEntry entry = new MessageEntry(playerName.toLowerCase(), message);
synchronized (messageEntries) {
messageEntries.add(entry);
// Called register asynchronously, potentially.
register();
}
}
}

View File

@ -1,8 +1,5 @@
package fr.neatmonster.nocheatplus.players;
import java.util.LinkedList;
import java.util.List;
import org.bukkit.entity.Player;
import fr.neatmonster.nocheatplus.utilities.OnDemandTickListener;
@ -17,9 +14,6 @@ public class PlayerTask extends OnDemandTickListener {
public final String lcName;
protected boolean updateInventory = false;
/** Messages scheduled for sending. */
protected final List<String> messages = new LinkedList<String>();
/**
*
@ -39,31 +33,11 @@ public class PlayerTask extends OnDemandTickListener {
player.updateInventory();
updateInventory = false;
}
if (!messages.isEmpty()){
final String[] message = new String[messages.size()];
messages.toArray(message);
player.sendMessage(message);
}
}
}
// Cleanup.
if (!messages.isEmpty()){
messages.clear();
}
// TODO: Consider setting updateInventory to false here.
// No re-scheduling (run once each time).
return false;
}
/**
* Add a message to be sent once the task is running. This method is NOT thread-safe.
* @param message
*/
public void sendMessage(String message){
messages.add(message);
register();
}
public void updateInventory(){
// TODO: Might not allow registering every tick.
updateInventory = true;

View File

@ -79,6 +79,7 @@ import fr.neatmonster.nocheatplus.permissions.PermissionUtil.CommandProtectionEn
import fr.neatmonster.nocheatplus.permissions.Permissions;
import fr.neatmonster.nocheatplus.players.DataManager;
import fr.neatmonster.nocheatplus.players.PlayerData;
import fr.neatmonster.nocheatplus.players.PlayerMessageSender;
import fr.neatmonster.nocheatplus.updates.Updates;
import fr.neatmonster.nocheatplus.utilities.BlockProperties;
import fr.neatmonster.nocheatplus.utilities.OnDemandTickListener;
@ -192,6 +193,9 @@ public class NoCheatPlus extends JavaPlugin implements NoCheatPlusAPI {
return false;
}
};
/** Access point for thread safe message queuing. */
private final PlayerMessageSender playerMessageSender = new PlayerMessageSender();
/**
* Remove expired entries.
@ -340,6 +344,14 @@ public class NoCheatPlus extends JavaPlugin implements NoCheatPlusAPI {
return done.size();
}
/* (non-Javadoc)
* @see fr.neatmonster.nocheatplus.components.NoCheatPlusAPI#sendMessageDelayed(java.lang.String, java.lang.String)
*/
@Override
public void sendMessageOnTick(final String playerName, final String message) {
playerMessageSender.sendMessageThreadSafe(playerName, message);
}
@SuppressWarnings("unchecked")
@Override
public <T> Collection<ComponentRegistry<T>> getComponentRegistries(final Class<ComponentRegistry<T>> clazz) {
@ -1082,11 +1094,11 @@ public class NoCheatPlus extends JavaPlugin implements NoCheatPlusAPI {
// Inconsistent config version.
if (configProblems != null && ConfigManager.getConfigFile().getBoolean(ConfPaths.CONFIGVERSION_NOTIFY)) {
// Could use custom prefix from logging, however ncp should be mentioned then.
data.task.sendMessage(ChatColor.RED + "NCP: " + ChatColor.WHITE + configProblems);
sendMessageOnTick(playerName, ChatColor.RED + "NCP: " + ChatColor.WHITE + configProblems);
}
// Message if notify is turned off.
if (data.getNotifyOff()) {
data.task.sendMessage(MSG_NOTIFY_OFF);
sendMessageOnTick(playerName, MSG_NOTIFY_OFF);
}
}
// JoinLeaveListenerS: Do update comment in NoCheatPlusAPI with changing event priority.