/* * This file is part of NucleusFramework for Bukkit, licensed under the MIT License (MIT). * * Copyright (c) JCThePants (www.jcwhatever.com) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package com.jcwhatever.nucleus.internal.managed.messenger; import com.jcwhatever.nucleus.Nucleus; import com.jcwhatever.nucleus.NucleusPlugin; import com.jcwhatever.nucleus.collections.players.PlayerMap; import com.jcwhatever.nucleus.collections.timed.TimedHashSet; import com.jcwhatever.nucleus.managed.messaging.IMessenger; import com.jcwhatever.nucleus.storage.IDataNode; import com.jcwhatever.nucleus.utils.PreCon; import com.jcwhatever.nucleus.utils.TimeScale; import com.jcwhatever.nucleus.utils.nms.INmsChatHandler; import com.jcwhatever.nucleus.utils.nms.NmsUtils; import com.jcwhatever.nucleus.utils.player.PlayerUtils; import com.jcwhatever.nucleus.utils.text.TextUtils; import com.jcwhatever.nucleus.utils.text.components.IChatMessage; import com.jcwhatever.nucleus.utils.text.components.SimpleChatMessage; import com.jcwhatever.nucleus.utils.text.format.ITextFormatterResult; import com.jcwhatever.nucleus.utils.text.format.TextFormatter; import com.jcwhatever.nucleus.utils.text.format.TextFormatterSettings; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; import org.bukkit.command.ConsoleCommandSender; import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; import javax.annotation.Nullable; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.logging.Logger; /** * Provide chat and console message utilities. */ class Messenger implements IMessenger { private static Map<UUID, TimedHashSet<String>> _noSpamCache = new PlayerMap<TimedHashSet<String>>(Nucleus.getPlugin()); private TextFormatterSettings TEXT_SETTING = new TextFormatterSettings(); private TextFormatter TEXT_FORMATTER = new TextFormatter(TEXT_SETTING); private final Plugin _plugin; private final String _consolePrefix; private final IDataNode _importantData; private final Logger _logger; private INmsChatHandler _chatHandler; private int _maxLineLen = 60; private int _spamDelay = 140; private LineWrapping _lineWrap = LineWrapping.ENABLED; private final Object _prefixSource; private IChatMessage _chatPrefix; protected Messenger(InternalMessengerFactory factory, Plugin plugin, @Nullable Object prefixSource) { _plugin = plugin; _prefixSource = prefixSource; _consolePrefix = InternalMessengerFactory.getConsolePrefix(prefixSource); _logger = InternalMessengerFactory.LOGGER; _importantData = factory.getImportantData(plugin); } @Override public Plugin getPlugin() { return _plugin; } @Override public int getLineWrapCount() { return _maxLineLen; } @Override public void setLineWrapCount(int characterCount) { _maxLineLen = characterCount; } @Override public LineWrapping getDefaultLineWrap() { return _lineWrap; } @Override public void setDefaultLineWrap(LineWrapping lineWrapping) { _lineWrap = lineWrapping; } @Override public int getDefaultNoSpamDelay() { return _spamDelay; } @Override public void setDefaultNoSpamDelay(int delay) { _spamDelay = delay; } @Override public boolean tellNoSpam(CommandSender sender, Object message, Object... params) { return tellNoSpam(sender, _spamDelay, _lineWrap, message, params); } @Override public boolean tellNoSpam(Collection<? extends Player> players, Object message, Object... params) { return tellNoSpam(players, _spamDelay, _lineWrap, message, params); } @Override public boolean tellNoSpam(CommandSender sender, Integer ticks, Object message, Object... params) { return tellNoSpam(sender, ticks, _lineWrap, message, params); } @Override public boolean tellNoSpam(Collection<? extends Player> players, Integer ticks, Object message, Object... params) { return tellNoSpam(players, ticks, _lineWrap, message, params); } @Override public boolean tellNoSpam(CommandSender sender, Integer ticks, LineWrapping lineWrapping, Object message, Object... params) { PreCon.notNull(sender); PreCon.positiveNumber(ticks); PreCon.notNull(lineWrapping); PreCon.notNull(message); PreCon.notNull(params); if (!(sender instanceof Player)) { // no spam not applied to console return tell(sender, message, params); } Player player = (Player)sender; IChatMessage msg = TextUtils.format(message, params); String str = msg.toString(); TimedHashSet<String> recent = _noSpamCache.get(player.getUniqueId()); if (recent == null) { recent = new TimedHashSet<String>(_plugin, 20, 140); _noSpamCache.put(player.getUniqueId(), recent); } if (recent.contains(str, ticks, TimeScale.TICKS)) return false; recent.add(str, ticks, TimeScale.TICKS); return tell(player, lineWrapping, msg); } @Override public boolean tellNoSpam(Collection<? extends Player> players, Integer ticks, LineWrapping lineWrapping, Object message, Object... params) { PreCon.notNull(players); PreCon.positiveNumber(ticks); PreCon.notNull(lineWrapping); PreCon.notNull(message); PreCon.notNull(params); IChatMessage msg = TextUtils.format(message, params); String str = msg.toString(); List<Player> recipients = new ArrayList<>(players.size()); for (Player player : players) { TimedHashSet<String> recent = _noSpamCache.get(player.getUniqueId()); if (recent == null) { recent = new TimedHashSet<String>(_plugin, 20, 140); _noSpamCache.put(player.getUniqueId(), recent); } if (recent.contains(str, ticks, TimeScale.TICKS)) continue; recent.add(str, ticks, TimeScale.TICKS); recipients.add(player); } if (recipients.isEmpty()) return false; sendMessage(recipients, msg); return true; } @Override public boolean tell(CommandSender sender, Object message, Object... params) { return tell(sender, _lineWrap, message, params); } @Override public boolean tell(Collection<? extends Player> players, Object message, Object... params) { return tell(players, _lineWrap, message, params); } @Override public boolean tell(CommandSender sender, LineWrapping lineWrapping, Object messageObject, Object... params) { PreCon.notNull(sender); PreCon.notNull(lineWrapping); PreCon.notNull(messageObject); PreCon.notNull(params); loadChatPrefix(); boolean cutLines = lineWrapping == LineWrapping.ENABLED && sender instanceof Player; TEXT_SETTING.setMaxLineLen(cutLines ? _maxLineLen : -1); if (messageObject instanceof ITextFormatterResult) { ITextFormatterResult result = ((ITextFormatterResult) messageObject); result.rebuild(TEXT_SETTING); sendMessage(sender, result); return true; } ITextFormatterResult result = messageObject instanceof CharSequence ? TEXT_FORMATTER.format(TEXT_SETTING, (CharSequence)messageObject, params) : TEXT_FORMATTER.format(TEXT_SETTING, messageObject.toString(), params); sendMessage(sender, result); return true; } @Override public boolean tell(Collection<? extends Player> players, LineWrapping lineWrapping, Object messageObject, Object... params) { PreCon.notNull(players); PreCon.notNull(lineWrapping); PreCon.notNull(messageObject); PreCon.notNull(params); loadChatPrefix(); boolean cutLines = lineWrapping == LineWrapping.ENABLED; TEXT_SETTING.setMaxLineLen(cutLines ? _maxLineLen : -1); if (messageObject instanceof ITextFormatterResult) { ITextFormatterResult result = ((ITextFormatterResult) messageObject); result.rebuild(TEXT_SETTING); sendMessage(players, result); return true; } IChatMessage result = messageObject instanceof CharSequence ? TEXT_FORMATTER.format(TEXT_SETTING, (CharSequence)messageObject, params) : TEXT_FORMATTER.format(TEXT_SETTING, messageObject.toString(), params); sendMessage(players, result); return true; } @Override public void tellImportant(Player player, String context, Object message, Object... params) { tellImportant(player.getUniqueId(), context, message, params); } @Override public void tellImportant(UUID playerId, String context, Object message, Object... params) { PreCon.notNull(playerId); PreCon.notNullOrEmpty(context); PreCon.notNull(message); PreCon.notNull(params); if (!TextUtils.isValidName(context, 64)) { throw new IllegalArgumentException( "Illegal characters in context argument or argument is too long. (Max 64)"); } Player player = PlayerUtils.getPlayer(playerId); if (player != null && player.isOnline()) { tell(player, message, params); return; } IDataNode data = _importantData; data.set(playerId.toString() + '.' + context + ".message", TextUtils.format(message, params)); data.set(playerId.toString() + '.' + context + ".prefix", loadChatPrefix().toString()); data.set(playerId.toString() + '.' + context + ".lineLen", _maxLineLen); data.save(); } @Override public void broadcast(Object message, Object... params) { broadcast(_lineWrap, message, params); } @Override public void broadcast(Collection<? extends Player> exclude, Object message, Object... params) { broadcast(exclude, _lineWrap, message, params); } @Override public void broadcast(LineWrapping lineWrapping, Object message, Object... params) { PreCon.notNull(message); PreCon.notNull(params); IChatMessage formatted = TextUtils.format(message, params); sendMessage(Bukkit.getOnlinePlayers(), formatted); } @Override public void broadcast(Collection<? extends Player> exclude, LineWrapping lineWrapping, Object message, Object... params) { PreCon.notNull(lineWrapping); PreCon.notNull(exclude); PreCon.notNull(message); PreCon.notNull(params); IChatMessage formatted = TextUtils.format(message, params); Set<Player> recipients = new HashSet<>(Bukkit.getOnlinePlayers()); recipients.removeAll(exclude); sendMessage(recipients, formatted); } @Override public void info(Object message, Object... params) { PreCon.notNull(message); PreCon.notNull(params); _logger.info(_consolePrefix + TextUtils.format(message, params)); } @Override public void debug(Object message, Object... params) { PreCon.notNull(message); PreCon.notNull(params); if (_plugin instanceof NucleusPlugin && !((NucleusPlugin) _plugin).isDebugging()) return; ConsoleCommandSender e = Bukkit.getConsoleSender(); if (e != null) { tell(e, LineWrapping.DISABLED, ChatColor.GOLD + "[debug] " + TextUtils.format(message, params)); } else { info("[debug] " + TextUtils.format(message, params)); } } @Override public void warning(Object message, Object... params) { PreCon.notNull(message); PreCon.notNull(params); _logger.warning(_consolePrefix + TextUtils.format(message, params)); } @Override public void severe(Object message, Object... params) { PreCon.notNull(message); PreCon.notNull(params); _logger.severe(_consolePrefix + TextUtils.format(message, params)); } private IChatMessage loadChatPrefix() { if (_chatPrefix == null) { _chatPrefix = _prefixSource != null ? TEXT_FORMATTER.format(InternalMessengerFactory.getChatPrefix(_prefixSource)) : new SimpleChatMessage(); TEXT_SETTING.setLinePrefix(_prefixSource == null ? null : _chatPrefix); } return _chatPrefix; } private INmsChatHandler chatHandler() { if (_chatHandler == null) _chatHandler = NmsUtils.getChatHandler(); return _chatHandler; } private void sendMessage(CommandSender sender, ITextFormatterResult result) { if (sender instanceof Player && chatHandler() != null) { chatHandler().send((Player)sender, result); } else { sender.sendMessage(result.toString()); } } private void sendMessage(Collection<? extends Player> players, IChatMessage message) { if (chatHandler() != null) { chatHandler().send(players, message); } else { for (Player player : players) { player.sendMessage(message.toString()); } } } }