mirror of
https://github.com/EssentialsX/Essentials.git
synced 2024-12-22 17:18:37 +01:00
Implement chat message signing for 1.19.1+ (#5030)
**Known issue: this inadvertently reformats `/minecraft:tell`. There's not much we can do about this in 2.19.x.** This commit refactors EssentialsX Chat in order to support chat previews and signed chat messages in 1.19.1+.
This commit is contained in:
parent
a394760677
commit
6dfa18ca53
@ -52,8 +52,8 @@ public class LocalChatSpyEvent extends Event implements Cancellable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the format to use to display this chat message. When this event finishes execution, the first format
|
* Gets the format to use to display this chat message to spy recipients. When this event finishes execution, the
|
||||||
* parameter is the {@link Player#getDisplayName()} and the second parameter is {@link #getMessage()}
|
* first format parameter is the {@link Player#getDisplayName()} and the second parameter is {@link #getMessage()}
|
||||||
*
|
*
|
||||||
* @return {@link String#format(String, Object...)} compatible format string
|
* @return {@link String#format(String, Object...)} compatible format string
|
||||||
*/
|
*/
|
||||||
@ -62,8 +62,8 @@ public class LocalChatSpyEvent extends Event implements Cancellable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the format to use to display this chat message. When this event finishes execution, the first format
|
* Sets the format to use to display this chat message to spy recipients. When this event finishes execution, the
|
||||||
* parameter is the {@link Player#getDisplayName()} and the second parameter is {@link #getMessage()}
|
* first format parameter is the {@link Player#getDisplayName()} and the second parameter is {@link #getMessage()}
|
||||||
*
|
*
|
||||||
* @param format {@link String#format(String, Object...)} compatible format string
|
* @param format {@link String#format(String, Object...)} compatible format string
|
||||||
* @throws IllegalFormatException if the underlying API throws the exception
|
* @throws IllegalFormatException if the underlying API throws the exception
|
||||||
|
@ -314,6 +314,7 @@ enderchestCommandUsage2=/<command> <player>
|
|||||||
enderchestCommandUsage2Description=Opens the ender chest of the target player
|
enderchestCommandUsage2Description=Opens the ender chest of the target player
|
||||||
errorCallingCommand=Error calling the command /{0}
|
errorCallingCommand=Error calling the command /{0}
|
||||||
errorWithMessage=\u00a7cError\:\u00a74 {0}
|
errorWithMessage=\u00a7cError\:\u00a74 {0}
|
||||||
|
essChatNoSecureMsg=EssentialsX Chat version {0} does not support secure chat on this server software. Update EssentialsX, and if this issue persists, inform the developers.
|
||||||
essentialsCommandDescription=Reloads essentials.
|
essentialsCommandDescription=Reloads essentials.
|
||||||
essentialsCommandUsage=/<command>
|
essentialsCommandUsage=/<command>
|
||||||
essentialsCommandUsage1=/<command> reload
|
essentialsCommandUsage1=/<command> reload
|
||||||
|
@ -1,42 +0,0 @@
|
|||||||
package com.earth2me.essentials.chat;
|
|
||||||
|
|
||||||
import com.earth2me.essentials.Trade;
|
|
||||||
import com.earth2me.essentials.User;
|
|
||||||
import net.ess3.api.IEssentials;
|
|
||||||
|
|
||||||
class ChatStore {
|
|
||||||
private final User user;
|
|
||||||
private final String type;
|
|
||||||
private final Trade charge;
|
|
||||||
private long radius;
|
|
||||||
|
|
||||||
ChatStore(final IEssentials ess, final User user, final String type) {
|
|
||||||
this.user = user;
|
|
||||||
this.type = type;
|
|
||||||
this.charge = new Trade(getLongType(), ess);
|
|
||||||
}
|
|
||||||
|
|
||||||
User getUser() {
|
|
||||||
return user;
|
|
||||||
}
|
|
||||||
|
|
||||||
Trade getCharge() {
|
|
||||||
return charge;
|
|
||||||
}
|
|
||||||
|
|
||||||
String getType() {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
final String getLongType() {
|
|
||||||
return type.length() == 0 ? "chat" : "chat-" + type;
|
|
||||||
}
|
|
||||||
|
|
||||||
long getRadius() {
|
|
||||||
return radius;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setRadius(final long radius) {
|
|
||||||
this.radius = radius;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,17 +1,16 @@
|
|||||||
package com.earth2me.essentials.chat;
|
package com.earth2me.essentials.chat;
|
||||||
|
|
||||||
|
import com.earth2me.essentials.Essentials;
|
||||||
import com.earth2me.essentials.EssentialsLogger;
|
import com.earth2me.essentials.EssentialsLogger;
|
||||||
|
import com.earth2me.essentials.chat.processing.LegacyChatHandler;
|
||||||
|
import com.earth2me.essentials.chat.processing.SignedChatHandler;
|
||||||
import com.earth2me.essentials.metrics.MetricsWrapper;
|
import com.earth2me.essentials.metrics.MetricsWrapper;
|
||||||
import net.ess3.api.IEssentials;
|
import net.ess3.api.IEssentials;
|
||||||
import org.bukkit.command.Command;
|
import org.bukkit.command.Command;
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
import org.bukkit.event.player.AsyncPlayerChatEvent;
|
|
||||||
import org.bukkit.plugin.PluginManager;
|
import org.bukkit.plugin.PluginManager;
|
||||||
import org.bukkit.plugin.java.JavaPlugin;
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
import static com.earth2me.essentials.I18n.tl;
|
import static com.earth2me.essentials.I18n.tl;
|
||||||
@ -33,14 +32,13 @@ public class EssentialsChat extends JavaPlugin {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final Map<AsyncPlayerChatEvent, ChatStore> chatStore = Collections.synchronizedMap(new HashMap<>());
|
final SignedChatHandler signedHandler = new SignedChatHandler((Essentials) ess, this);
|
||||||
|
if (signedHandler.tryRegisterListeners()) {
|
||||||
final EssentialsChatPlayerListenerLowest playerListenerLowest = new EssentialsChatPlayerListenerLowest(getServer(), ess, this, chatStore);
|
getLogger().info("Secure signed chat and previews are enabled.");
|
||||||
final EssentialsChatPlayerListenerNormal playerListenerNormal = new EssentialsChatPlayerListenerNormal(getServer(), ess, this, chatStore);
|
} else {
|
||||||
final EssentialsChatPlayerListenerHighest playerListenerHighest = new EssentialsChatPlayerListenerHighest(getServer(), ess, this, chatStore);
|
final LegacyChatHandler legacyHandler = new LegacyChatHandler((Essentials) ess, this);
|
||||||
pluginManager.registerEvents(playerListenerLowest, this);
|
legacyHandler.registerListeners();
|
||||||
pluginManager.registerEvents(playerListenerNormal, this);
|
}
|
||||||
pluginManager.registerEvents(playerListenerHighest, this);
|
|
||||||
|
|
||||||
if (metrics == null) {
|
if (metrics == null) {
|
||||||
metrics = new MetricsWrapper(this, 3814, false);
|
metrics = new MetricsWrapper(this, 3814, false);
|
||||||
|
@ -1,79 +0,0 @@
|
|||||||
package com.earth2me.essentials.chat;
|
|
||||||
|
|
||||||
import com.earth2me.essentials.ChargeException;
|
|
||||||
import com.earth2me.essentials.Trade;
|
|
||||||
import com.earth2me.essentials.User;
|
|
||||||
import net.ess3.api.IEssentials;
|
|
||||||
import org.bukkit.Server;
|
|
||||||
import org.bukkit.event.Listener;
|
|
||||||
import org.bukkit.event.player.AsyncPlayerChatEvent;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public abstract class EssentialsChatPlayer implements Listener {
|
|
||||||
final transient IEssentials ess;
|
|
||||||
final transient EssentialsChat essChat;
|
|
||||||
final transient Server server;
|
|
||||||
private final transient Map<AsyncPlayerChatEvent, ChatStore> chatStorage;
|
|
||||||
|
|
||||||
EssentialsChatPlayer(final Server server, final IEssentials ess, final EssentialsChat essChat, final Map<AsyncPlayerChatEvent, ChatStore> chatStorage) {
|
|
||||||
this.ess = ess;
|
|
||||||
this.essChat = essChat;
|
|
||||||
this.server = server;
|
|
||||||
this.chatStorage = chatStorage;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract void onPlayerChat(final AsyncPlayerChatEvent event);
|
|
||||||
|
|
||||||
boolean isAborted(final AsyncPlayerChatEvent event) {
|
|
||||||
return event.isCancelled();
|
|
||||||
}
|
|
||||||
|
|
||||||
String getChatType(final User user, final String message) {
|
|
||||||
if (message.length() == 0) {
|
|
||||||
//Ignore empty chat events generated by plugins
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
final char prefix = message.charAt(0);
|
|
||||||
if (prefix == ess.getSettings().getChatShout()) {
|
|
||||||
if (user.isToggleShout()) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
return message.length() > 1 ? "shout" : "";
|
|
||||||
} else if (ess.getSettings().isChatQuestionEnabled() && prefix == ess.getSettings().getChatQuestion()) {
|
|
||||||
return message.length() > 1 ? "question" : "";
|
|
||||||
} else if (user.isToggleShout()) {
|
|
||||||
return message.length() > 1 ? "shout" : "";
|
|
||||||
} else {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ChatStore getChatStore(final AsyncPlayerChatEvent event) {
|
|
||||||
return chatStorage.get(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setChatStore(final AsyncPlayerChatEvent event, final ChatStore chatStore) {
|
|
||||||
chatStorage.put(event, chatStore);
|
|
||||||
}
|
|
||||||
|
|
||||||
ChatStore delChatStore(final AsyncPlayerChatEvent event) {
|
|
||||||
return chatStorage.remove(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void charge(final User user, final Trade charge) throws ChargeException {
|
|
||||||
charge.charge(user);
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean charge(final AsyncPlayerChatEvent event, final ChatStore chatStore) {
|
|
||||||
try {
|
|
||||||
charge(chatStore.getUser(), chatStore.getCharge());
|
|
||||||
} catch (final ChargeException e) {
|
|
||||||
ess.showError(chatStore.getUser().getSource(), e, "\\ chat " + chatStore.getLongType());
|
|
||||||
event.setCancelled(true);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,27 +0,0 @@
|
|||||||
package com.earth2me.essentials.chat;
|
|
||||||
|
|
||||||
import net.ess3.api.IEssentials;
|
|
||||||
import org.bukkit.Server;
|
|
||||||
import org.bukkit.event.EventHandler;
|
|
||||||
import org.bukkit.event.EventPriority;
|
|
||||||
import org.bukkit.event.player.AsyncPlayerChatEvent;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class EssentialsChatPlayerListenerHighest extends EssentialsChatPlayer {
|
|
||||||
EssentialsChatPlayerListenerHighest(final Server server, final IEssentials ess, final EssentialsChat essChat, final Map<AsyncPlayerChatEvent, ChatStore> chatStorage) {
|
|
||||||
super(server, ess, essChat, chatStorage);
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.HIGHEST)
|
|
||||||
@Override
|
|
||||||
public void onPlayerChat(final AsyncPlayerChatEvent event) {
|
|
||||||
final ChatStore chatStore = delChatStore(event);
|
|
||||||
if (isAborted(event) || chatStore == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This file should handle charging the user for the action before returning control back
|
|
||||||
charge(event, chatStore);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,72 +0,0 @@
|
|||||||
package com.earth2me.essentials.chat;
|
|
||||||
|
|
||||||
import com.earth2me.essentials.User;
|
|
||||||
import com.earth2me.essentials.utils.FormatUtil;
|
|
||||||
import net.ess3.api.IEssentials;
|
|
||||||
import org.bukkit.ChatColor;
|
|
||||||
import org.bukkit.Server;
|
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
import org.bukkit.event.EventHandler;
|
|
||||||
import org.bukkit.event.EventPriority;
|
|
||||||
import org.bukkit.event.player.AsyncPlayerChatEvent;
|
|
||||||
import org.bukkit.scoreboard.Team;
|
|
||||||
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class EssentialsChatPlayerListenerLowest extends EssentialsChatPlayer {
|
|
||||||
EssentialsChatPlayerListenerLowest(final Server server, final IEssentials ess, final EssentialsChat essChat, final Map<AsyncPlayerChatEvent, ChatStore> chatStorage) {
|
|
||||||
super(server, ess, essChat, chatStorage);
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.LOWEST)
|
|
||||||
@Override
|
|
||||||
public void onPlayerChat(final AsyncPlayerChatEvent event) {
|
|
||||||
if (isAborted(event)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final User user = ess.getUser(event.getPlayer());
|
|
||||||
|
|
||||||
if (user == null) {
|
|
||||||
event.setCancelled(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final ChatStore chatStore = new ChatStore(ess, user, getChatType(user, event.getMessage()));
|
|
||||||
setChatStore(event, chatStore);
|
|
||||||
|
|
||||||
// This listener should apply the general chat formatting only...then return control back the event handler
|
|
||||||
event.setMessage(FormatUtil.formatMessage(user, "essentials.chat", event.getMessage()));
|
|
||||||
|
|
||||||
if (ChatColor.stripColor(event.getMessage()).length() == 0) {
|
|
||||||
event.setCancelled(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final String group = user.getGroup();
|
|
||||||
final String world = user.getWorld().getName();
|
|
||||||
final String username = user.getName();
|
|
||||||
final String nickname = user.getFormattedNickname();
|
|
||||||
|
|
||||||
final Player player = user.getBase();
|
|
||||||
final String prefix = FormatUtil.replaceFormat(ess.getPermissionsHandler().getPrefix(player));
|
|
||||||
final String suffix = FormatUtil.replaceFormat(ess.getPermissionsHandler().getSuffix(player));
|
|
||||||
final Team team = player.getScoreboard().getPlayerTeam(player);
|
|
||||||
|
|
||||||
String format = ess.getSettings().getChatFormat(group);
|
|
||||||
format = format.replace("{0}", group);
|
|
||||||
format = format.replace("{1}", ess.getSettings().getWorldAlias(world));
|
|
||||||
format = format.replace("{2}", world.substring(0, 1).toUpperCase(Locale.ENGLISH));
|
|
||||||
format = format.replace("{3}", team == null ? "" : team.getPrefix());
|
|
||||||
format = format.replace("{4}", team == null ? "" : team.getSuffix());
|
|
||||||
format = format.replace("{5}", team == null ? "" : team.getDisplayName());
|
|
||||||
format = format.replace("{6}", prefix);
|
|
||||||
format = format.replace("{7}", suffix);
|
|
||||||
format = format.replace("{8}", username);
|
|
||||||
format = format.replace("{9}", nickname == null ? username : nickname);
|
|
||||||
synchronized (format) {
|
|
||||||
event.setFormat(format);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,136 +0,0 @@
|
|||||||
package com.earth2me.essentials.chat;
|
|
||||||
|
|
||||||
import com.earth2me.essentials.User;
|
|
||||||
import net.ess3.api.IEssentials;
|
|
||||||
import net.ess3.api.events.LocalChatSpyEvent;
|
|
||||||
import org.bukkit.Location;
|
|
||||||
import org.bukkit.Server;
|
|
||||||
import org.bukkit.World;
|
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
import org.bukkit.event.EventHandler;
|
|
||||||
import org.bukkit.event.EventPriority;
|
|
||||||
import org.bukkit.event.player.AsyncPlayerChatEvent;
|
|
||||||
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
|
|
||||||
import static com.earth2me.essentials.I18n.tl;
|
|
||||||
|
|
||||||
public class EssentialsChatPlayerListenerNormal extends EssentialsChatPlayer {
|
|
||||||
EssentialsChatPlayerListenerNormal(final Server server, final IEssentials ess, final EssentialsChat essChat, final Map<AsyncPlayerChatEvent, ChatStore> chatStorage) {
|
|
||||||
super(server, ess, essChat, chatStorage);
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.NORMAL)
|
|
||||||
@Override
|
|
||||||
public void onPlayerChat(final AsyncPlayerChatEvent event) {
|
|
||||||
if (isAborted(event)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This file should handle detection of the local chat features; if local chat is enabled, we need to handle it here
|
|
||||||
long radius = ess.getSettings().getChatRadius();
|
|
||||||
if (radius < 1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
radius *= radius;
|
|
||||||
|
|
||||||
final ChatStore chatStore = getChatStore(event);
|
|
||||||
final User user = chatStore.getUser();
|
|
||||||
chatStore.setRadius(radius);
|
|
||||||
|
|
||||||
if (event.getMessage().length() > 1) {
|
|
||||||
if (chatStore.getType().isEmpty()) {
|
|
||||||
if (!user.isAuthorized("essentials.chat.local")) {
|
|
||||||
user.sendMessage(tl("notAllowedToLocal"));
|
|
||||||
event.setCancelled(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (user.isToggleShout() && event.getMessage().length() > 1 && event.getMessage().charAt(0) == ess.getSettings().getChatShout()) {
|
|
||||||
event.setMessage(event.getMessage().substring(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
event.getRecipients().removeIf(player -> !ess.getUser(player).isAuthorized("essentials.chat.receive.local"));
|
|
||||||
} else {
|
|
||||||
final String permission = "essentials.chat." + chatStore.getType();
|
|
||||||
|
|
||||||
if (user.isAuthorized(permission)) {
|
|
||||||
if (event.getMessage().charAt(0) == ess.getSettings().getChatShout() || (event.getMessage().charAt(0) == ess.getSettings().getChatQuestion() && ess.getSettings().isChatQuestionEnabled())) {
|
|
||||||
event.setMessage(event.getMessage().substring(1));
|
|
||||||
}
|
|
||||||
event.setFormat(tl(chatStore.getType() + "Format", event.getFormat()));
|
|
||||||
event.getRecipients().removeIf(player -> !ess.getUser(player).isAuthorized("essentials.chat.receive." + chatStore.getType()));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
user.sendMessage(tl("notAllowedTo" + chatStore.getType().substring(0, 1).toUpperCase(Locale.ENGLISH) + chatStore.getType().substring(1)));
|
|
||||||
event.setCancelled(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final Location loc = user.getLocation();
|
|
||||||
final World world = loc.getWorld();
|
|
||||||
|
|
||||||
if (!charge(event, chatStore)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final Set<Player> outList = event.getRecipients();
|
|
||||||
final Set<Player> spyList = new HashSet<>();
|
|
||||||
|
|
||||||
try {
|
|
||||||
outList.add(event.getPlayer());
|
|
||||||
} catch (final UnsupportedOperationException ex) {
|
|
||||||
if (ess.getSettings().isDebug()) {
|
|
||||||
essChat.getLogger().log(Level.INFO, "Plugin triggered custom chat event, local chat handling aborted.", ex);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final String format = event.getFormat();
|
|
||||||
event.setFormat(tl("chatTypeLocal").concat(event.getFormat()));
|
|
||||||
|
|
||||||
final Iterator<Player> it = outList.iterator();
|
|
||||||
while (it.hasNext()) {
|
|
||||||
final Player onlinePlayer = it.next();
|
|
||||||
final User onlineUser = ess.getUser(onlinePlayer);
|
|
||||||
if (!onlineUser.equals(user)) {
|
|
||||||
boolean abort = false;
|
|
||||||
final Location playerLoc = onlineUser.getLocation();
|
|
||||||
if (playerLoc.getWorld() != world) {
|
|
||||||
abort = true;
|
|
||||||
} else {
|
|
||||||
final double delta = playerLoc.distanceSquared(loc);
|
|
||||||
if (delta > chatStore.getRadius()) {
|
|
||||||
abort = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (abort) {
|
|
||||||
if (onlineUser.isAuthorized("essentials.chat.spy")) {
|
|
||||||
spyList.add(onlinePlayer);
|
|
||||||
}
|
|
||||||
it.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (outList.size() < 2) {
|
|
||||||
user.sendMessage(tl("localNoOne"));
|
|
||||||
}
|
|
||||||
|
|
||||||
final LocalChatSpyEvent spyEvent = new LocalChatSpyEvent(event.isAsynchronous(), event.getPlayer(), format, event.getMessage(), spyList);
|
|
||||||
server.getPluginManager().callEvent(spyEvent);
|
|
||||||
|
|
||||||
if (!spyEvent.isCancelled()) {
|
|
||||||
for (final Player onlinePlayer : spyEvent.getRecipients()) {
|
|
||||||
onlinePlayer.sendMessage(String.format(spyEvent.getFormat(), user.getDisplayName(), spyEvent.getMessage()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,304 @@
|
|||||||
|
package com.earth2me.essentials.chat.processing;
|
||||||
|
|
||||||
|
import com.earth2me.essentials.ChargeException;
|
||||||
|
import com.earth2me.essentials.Essentials;
|
||||||
|
import com.earth2me.essentials.Trade;
|
||||||
|
import com.earth2me.essentials.User;
|
||||||
|
import com.earth2me.essentials.chat.EssentialsChat;
|
||||||
|
import com.earth2me.essentials.utils.FormatUtil;
|
||||||
|
import net.ess3.api.events.LocalChatSpyEvent;
|
||||||
|
import org.bukkit.ChatColor;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.Server;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.player.AsyncPlayerChatEvent;
|
||||||
|
import org.bukkit.scoreboard.Team;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
import static com.earth2me.essentials.I18n.tl;
|
||||||
|
|
||||||
|
public abstract class AbstractChatHandler {
|
||||||
|
|
||||||
|
protected final Essentials ess;
|
||||||
|
protected final EssentialsChat essChat;
|
||||||
|
protected final Server server;
|
||||||
|
protected final ChatProcessingCache cache;
|
||||||
|
|
||||||
|
protected AbstractChatHandler(Essentials ess, EssentialsChat essChat) {
|
||||||
|
this.ess = ess;
|
||||||
|
this.essChat = essChat;
|
||||||
|
this.server = ess.getServer();
|
||||||
|
this.cache = new ChatProcessingCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply chat formatting from config and from translations according to chat type.
|
||||||
|
* <p>
|
||||||
|
* Handled at {@link org.bukkit.event.EventPriority#LOWEST} on both preview and chat events.
|
||||||
|
*/
|
||||||
|
protected void handleChatFormat(AsyncPlayerChatEvent event) {
|
||||||
|
if (isAborted(event)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final User user = ess.getUser(event.getPlayer());
|
||||||
|
|
||||||
|
if (user == null) {
|
||||||
|
event.setCancelled(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reuse cached IntermediateChat if available
|
||||||
|
ChatProcessingCache.IntermediateChat chat = cache.getIntermediateChat(event.getPlayer());
|
||||||
|
if (chat == null) {
|
||||||
|
chat = new ChatProcessingCache.IntermediateChat(user, getChatType(user, event.getMessage()), event.getMessage());
|
||||||
|
cache.setIntermediateChat(event.getPlayer(), chat);
|
||||||
|
}
|
||||||
|
|
||||||
|
final long configRadius = ess.getSettings().getChatRadius();
|
||||||
|
chat.setRadius(Math.max(configRadius, 0));
|
||||||
|
|
||||||
|
// This listener should apply the general chat formatting only...then return control back the event handler
|
||||||
|
event.setMessage(FormatUtil.formatMessage(user, "essentials.chat", event.getMessage()));
|
||||||
|
|
||||||
|
if (ChatColor.stripColor(event.getMessage()).length() == 0) {
|
||||||
|
event.setCancelled(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String group = user.getGroup();
|
||||||
|
final String world = user.getWorld().getName();
|
||||||
|
final String username = user.getName();
|
||||||
|
final String nickname = user.getFormattedNickname();
|
||||||
|
|
||||||
|
final Player player = user.getBase();
|
||||||
|
final String prefix = FormatUtil.replaceFormat(ess.getPermissionsHandler().getPrefix(player));
|
||||||
|
final String suffix = FormatUtil.replaceFormat(ess.getPermissionsHandler().getSuffix(player));
|
||||||
|
final Team team = player.getScoreboard().getPlayerTeam(player);
|
||||||
|
|
||||||
|
String format = ess.getSettings().getChatFormat(group);
|
||||||
|
format = format.replace("{0}", group);
|
||||||
|
format = format.replace("{1}", ess.getSettings().getWorldAlias(world));
|
||||||
|
format = format.replace("{2}", world.substring(0, 1).toUpperCase(Locale.ENGLISH));
|
||||||
|
format = format.replace("{3}", team == null ? "" : team.getPrefix());
|
||||||
|
format = format.replace("{4}", team == null ? "" : team.getSuffix());
|
||||||
|
format = format.replace("{5}", team == null ? "" : team.getDisplayName());
|
||||||
|
format = format.replace("{6}", prefix);
|
||||||
|
format = format.replace("{7}", suffix);
|
||||||
|
format = format.replace("{8}", username);
|
||||||
|
format = format.replace("{9}", nickname == null ? username : nickname);
|
||||||
|
|
||||||
|
// Local, shout and question chat types are only enabled when there's a valid radius
|
||||||
|
if (chat.getRadius() > 0 && event.getMessage().length() > 1) {
|
||||||
|
if (chat.getType().isEmpty()) {
|
||||||
|
if (user.isToggleShout() && event.getMessage().charAt(0) == ess.getSettings().getChatShout()) {
|
||||||
|
event.setMessage(event.getMessage().substring(1));
|
||||||
|
}
|
||||||
|
format = tl("chatTypeLocal").concat(format);
|
||||||
|
} else {
|
||||||
|
if (event.getMessage().charAt(0) == ess.getSettings().getChatShout() || (event.getMessage().charAt(0) == ess.getSettings().getChatQuestion() && ess.getSettings().isChatQuestionEnabled())) {
|
||||||
|
event.setMessage(event.getMessage().substring(1));
|
||||||
|
}
|
||||||
|
format = tl(chat.getType() + "Format", format);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Long live pointless synchronized blocks!
|
||||||
|
synchronized (format) {
|
||||||
|
event.setFormat(format);
|
||||||
|
}
|
||||||
|
|
||||||
|
chat.setFormatResult(event.getFormat());
|
||||||
|
chat.setMessageResult(event.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the recipient filtering and permissions checks for local chat, if enabled.
|
||||||
|
* <p>
|
||||||
|
* Runs at {@link org.bukkit.event.EventPriority#NORMAL} priority on submitted chat events only.
|
||||||
|
*/
|
||||||
|
protected void handleChatRecipients(AsyncPlayerChatEvent event) {
|
||||||
|
if (isAborted(event)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final ChatProcessingCache.Chat chat = cache.getIntermediateOrElseProcessedChat(event.getPlayer());
|
||||||
|
|
||||||
|
// If local chat is enabled, handle the recipients here; else we have nothing to do
|
||||||
|
if (chat.getRadius() < 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final long radiusSquared = chat.getRadius() * chat.getRadius();
|
||||||
|
|
||||||
|
final User user = chat.getUser();
|
||||||
|
|
||||||
|
if (event.getMessage().length() > 1) {
|
||||||
|
if (chat.getType().isEmpty()) {
|
||||||
|
if (!user.isAuthorized("essentials.chat.local")) {
|
||||||
|
user.sendMessage(tl("notAllowedToLocal"));
|
||||||
|
event.setCancelled(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
event.getRecipients().removeIf(player -> !ess.getUser(player).isAuthorized("essentials.chat.receive.local"));
|
||||||
|
} else {
|
||||||
|
final String permission = "essentials.chat." + chat.getType();
|
||||||
|
|
||||||
|
if (user.isAuthorized(permission)) {
|
||||||
|
event.getRecipients().removeIf(player -> !ess.getUser(player).isAuthorized("essentials.chat.receive." + chat.getType()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
user.sendMessage(tl("notAllowedTo" + chat.getType().substring(0, 1).toUpperCase(Locale.ENGLISH) + chat.getType().substring(1)));
|
||||||
|
event.setCancelled(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final Location loc = user.getLocation();
|
||||||
|
final World world = loc.getWorld();
|
||||||
|
|
||||||
|
final Set<Player> outList = event.getRecipients();
|
||||||
|
final Set<Player> spyList = new HashSet<>();
|
||||||
|
|
||||||
|
try {
|
||||||
|
outList.add(event.getPlayer());
|
||||||
|
} catch (final UnsupportedOperationException ex) {
|
||||||
|
if (ess.getSettings().isDebug()) {
|
||||||
|
essChat.getLogger().log(Level.INFO, "Plugin triggered custom chat event, local chat handling aborted.", ex);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Iterator<Player> it = outList.iterator();
|
||||||
|
while (it.hasNext()) {
|
||||||
|
final Player onlinePlayer = it.next();
|
||||||
|
final User onlineUser = ess.getUser(onlinePlayer);
|
||||||
|
if (!onlineUser.equals(user)) {
|
||||||
|
boolean abort = false;
|
||||||
|
final Location playerLoc = onlineUser.getLocation();
|
||||||
|
if (playerLoc.getWorld() != world) {
|
||||||
|
abort = true;
|
||||||
|
} else {
|
||||||
|
final double delta = playerLoc.distanceSquared(loc);
|
||||||
|
if (delta > radiusSquared) {
|
||||||
|
abort = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (abort) {
|
||||||
|
if (onlineUser.isAuthorized("essentials.chat.spy")) {
|
||||||
|
spyList.add(onlinePlayer);
|
||||||
|
}
|
||||||
|
it.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outList.size() < 2) {
|
||||||
|
user.sendMessage(tl("localNoOne"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Strip local chat prefix to preserve API behaviour
|
||||||
|
final String localPrefix = tl("chatTypeLocal");
|
||||||
|
String baseFormat = event.getFormat();
|
||||||
|
if (event.getFormat().startsWith(localPrefix)) {
|
||||||
|
baseFormat = baseFormat.substring(localPrefix.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
final LocalChatSpyEvent spyEvent = new LocalChatSpyEvent(event.isAsynchronous(), event.getPlayer(), baseFormat, event.getMessage(), spyList);
|
||||||
|
server.getPluginManager().callEvent(spyEvent);
|
||||||
|
|
||||||
|
if (!spyEvent.isCancelled()) {
|
||||||
|
for (final Player onlinePlayer : spyEvent.getRecipients()) {
|
||||||
|
onlinePlayer.sendMessage(String.format(spyEvent.getFormat(), user.getDisplayName(), spyEvent.getMessage()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finalise the formatting stage of chat processing.
|
||||||
|
* <p>
|
||||||
|
* Handled at {@link org.bukkit.event.EventPriority#HIGHEST} during previews, and immediately after
|
||||||
|
* {@link #handleChatFormat(AsyncPlayerChatEvent)} when previews are not available.
|
||||||
|
*/
|
||||||
|
protected void handleChatPostFormat(AsyncPlayerChatEvent event) {
|
||||||
|
final ChatProcessingCache.IntermediateChat intermediateChat = cache.clearIntermediateChat(event.getPlayer());
|
||||||
|
if (isAborted(event) || intermediateChat == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// in case of modifications by other plugins during the preview
|
||||||
|
intermediateChat.setFormatResult(event.getFormat());
|
||||||
|
intermediateChat.setMessageResult(event.getMessage());
|
||||||
|
|
||||||
|
final ChatProcessingCache.ProcessedChat processed = new ChatProcessingCache.ProcessedChat(ess, intermediateChat);
|
||||||
|
cache.setProcessedChat(event.getPlayer(), processed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run costs for chat and clean up the cached {@link com.earth2me.essentials.chat.processing.ChatProcessingCache.ProcessedChat}
|
||||||
|
*/
|
||||||
|
protected void handleChatSubmit(AsyncPlayerChatEvent event) {
|
||||||
|
if (isAborted(event)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This file should handle charging the user for the action before returning control back
|
||||||
|
charge(event, cache.getProcessedChat(event.getPlayer()));
|
||||||
|
|
||||||
|
cache.clearProcessedChat(event.getPlayer());
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isAborted(final AsyncPlayerChatEvent event) {
|
||||||
|
return event.isCancelled();
|
||||||
|
}
|
||||||
|
|
||||||
|
String getChatType(final User user, final String message) {
|
||||||
|
if (message.length() == 0) {
|
||||||
|
//Ignore empty chat events generated by plugins
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
final char prefix = message.charAt(0);
|
||||||
|
if (prefix == ess.getSettings().getChatShout()) {
|
||||||
|
if (user.isToggleShout()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return message.length() > 1 ? "shout" : "";
|
||||||
|
} else if (ess.getSettings().isChatQuestionEnabled() && prefix == ess.getSettings().getChatQuestion()) {
|
||||||
|
return message.length() > 1 ? "question" : "";
|
||||||
|
} else if (user.isToggleShout()) {
|
||||||
|
return message.length() > 1 ? "shout" : "";
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void charge(final User user, final Trade charge) throws ChargeException {
|
||||||
|
charge.charge(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean charge(final AsyncPlayerChatEvent event, final ChatProcessingCache.ProcessedChat chat) {
|
||||||
|
try {
|
||||||
|
charge(chat.getUser(), chat.getCharge());
|
||||||
|
} catch (final ChargeException e) {
|
||||||
|
ess.showError(chat.getUser().getSource(), e, "\\ chat " + chat.getLongType());
|
||||||
|
event.setCancelled(true);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected interface ChatListener extends Listener {
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
void onPlayerChat(AsyncPlayerChatEvent event);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,144 @@
|
|||||||
|
package com.earth2me.essentials.chat.processing;
|
||||||
|
|
||||||
|
import com.earth2me.essentials.Trade;
|
||||||
|
import com.earth2me.essentials.User;
|
||||||
|
import com.google.common.cache.Cache;
|
||||||
|
import com.google.common.cache.CacheBuilder;
|
||||||
|
import net.ess3.api.IEssentials;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
public class ChatProcessingCache {
|
||||||
|
|
||||||
|
private final Map<Player, IntermediateChat> intermediateChats = Collections.synchronizedMap(new HashMap<>());
|
||||||
|
|
||||||
|
private final Cache<Player, ProcessedChat> processedChats = CacheBuilder.newBuilder()
|
||||||
|
.expireAfterWrite(5, TimeUnit.MINUTES)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
public IntermediateChat getIntermediateChat(final Player player) {
|
||||||
|
return intermediateChats.get(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIntermediateChat(final Player player, final IntermediateChat intermediateChat) {
|
||||||
|
intermediateChats.put(player, intermediateChat);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IntermediateChat clearIntermediateChat(final Player player) {
|
||||||
|
return intermediateChats.remove(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProcessedChat getProcessedChat(final Player player) {
|
||||||
|
return processedChats.getIfPresent(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProcessedChat(final Player player, final ProcessedChat chat) {
|
||||||
|
processedChats.put(player, chat);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProcessedChat clearProcessedChat(final Player player) {
|
||||||
|
final ProcessedChat chat = processedChats.getIfPresent(player);
|
||||||
|
processedChats.invalidate(player);
|
||||||
|
return chat;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Chat getIntermediateOrElseProcessedChat(final Player player) {
|
||||||
|
final IntermediateChat chat = getIntermediateChat(player);
|
||||||
|
if (chat != null) {
|
||||||
|
return chat;
|
||||||
|
}
|
||||||
|
return getProcessedChat(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static abstract class Chat {
|
||||||
|
private final User user;
|
||||||
|
private final String type;
|
||||||
|
private final String originalMessage;
|
||||||
|
protected long radius;
|
||||||
|
|
||||||
|
protected Chat(User user, String type, String originalMessage) {
|
||||||
|
this.user = user;
|
||||||
|
this.type = type;
|
||||||
|
this.originalMessage = originalMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public User getUser() {
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOriginalMessage() {
|
||||||
|
return originalMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getRadius() {
|
||||||
|
return radius;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final String getLongType() {
|
||||||
|
return type.length() == 0 ? "chat" : "chat-" + type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ProcessedChat extends Chat {
|
||||||
|
private final String message;
|
||||||
|
private final String format;
|
||||||
|
private final Trade charge;
|
||||||
|
|
||||||
|
public ProcessedChat(final IEssentials ess, final IntermediateChat sourceChat) {
|
||||||
|
super(sourceChat.getUser(), sourceChat.getType(), sourceChat.getOriginalMessage());
|
||||||
|
this.message = sourceChat.messageResult;
|
||||||
|
this.format = sourceChat.formatResult;
|
||||||
|
this.charge = new Trade(getLongType(), ess);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFormat() {
|
||||||
|
return format;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Trade getCharge() {
|
||||||
|
return charge;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class IntermediateChat extends Chat {
|
||||||
|
private String messageResult;
|
||||||
|
private String formatResult;
|
||||||
|
|
||||||
|
public IntermediateChat(final User user, final String type, final String originalMessage) {
|
||||||
|
super(user, type, originalMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRadius(final long radius) {
|
||||||
|
this.radius = radius;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessageResult() {
|
||||||
|
return messageResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMessageResult(String messageResult) {
|
||||||
|
this.messageResult = messageResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFormatResult() {
|
||||||
|
return formatResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFormatResult(String formatResult) {
|
||||||
|
this.formatResult = formatResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
package com.earth2me.essentials.chat.processing;
|
||||||
|
|
||||||
|
import com.earth2me.essentials.Essentials;
|
||||||
|
import com.earth2me.essentials.chat.EssentialsChat;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.EventPriority;
|
||||||
|
import org.bukkit.event.player.AsyncPlayerChatEvent;
|
||||||
|
import org.bukkit.plugin.PluginManager;
|
||||||
|
|
||||||
|
public class LegacyChatHandler extends AbstractChatHandler {
|
||||||
|
public LegacyChatHandler(Essentials ess, EssentialsChat essChat) {
|
||||||
|
super(ess, essChat);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerListeners() {
|
||||||
|
final PluginManager pm = essChat.getServer().getPluginManager();
|
||||||
|
pm.registerEvents(new ChatLowest(), essChat);
|
||||||
|
pm.registerEvents(new ChatNormal(), essChat);
|
||||||
|
pm.registerEvents(new ChatHighest(), essChat);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ChatLowest implements ChatListener {
|
||||||
|
@Override
|
||||||
|
@EventHandler(priority = EventPriority.LOWEST)
|
||||||
|
public void onPlayerChat(AsyncPlayerChatEvent event) {
|
||||||
|
handleChatFormat(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ChatNormal implements ChatListener {
|
||||||
|
@Override
|
||||||
|
@EventHandler(priority = EventPriority.NORMAL)
|
||||||
|
public void onPlayerChat(AsyncPlayerChatEvent event) {
|
||||||
|
handleChatRecipients(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ChatHighest implements ChatListener {
|
||||||
|
@Override
|
||||||
|
@EventHandler(priority = EventPriority.HIGHEST)
|
||||||
|
public void onPlayerChat(AsyncPlayerChatEvent event) {
|
||||||
|
handleChatPostFormat(event);
|
||||||
|
handleChatSubmit(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,120 @@
|
|||||||
|
package com.earth2me.essentials.chat.processing;
|
||||||
|
|
||||||
|
import com.earth2me.essentials.Essentials;
|
||||||
|
import com.earth2me.essentials.I18n;
|
||||||
|
import com.earth2me.essentials.chat.EssentialsChat;
|
||||||
|
import com.earth2me.essentials.utils.VersionUtil;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.EventPriority;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.player.AsyncPlayerChatEvent;
|
||||||
|
import org.bukkit.event.player.AsyncPlayerChatPreviewEvent;
|
||||||
|
import org.bukkit.plugin.PluginManager;
|
||||||
|
|
||||||
|
public class SignedChatHandler extends AbstractChatHandler {
|
||||||
|
|
||||||
|
public SignedChatHandler(Essentials ess, EssentialsChat essChat) {
|
||||||
|
super(ess, essChat);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean tryRegisterListeners() {
|
||||||
|
if (VersionUtil.getServerBukkitVersion().isLowerThan(VersionUtil.v1_19_2_R01)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
final Class<?> previewClass = Class.forName("org.bukkit.event.player.AsyncPlayerChatPreviewEvent");
|
||||||
|
if (!AsyncPlayerChatEvent.class.isAssignableFrom(previewClass)) {
|
||||||
|
essChat.getLogger().severe(I18n.tl("essChatNoSecureMsg", essChat.getDescription().getVersion()));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
essChat.getLogger().severe(I18n.tl("essChatNoSecureMsg", essChat.getDescription().getVersion()));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final PluginManager pm = essChat.getServer().getPluginManager();
|
||||||
|
pm.registerEvents(new PreviewLowest(), essChat);
|
||||||
|
pm.registerEvents(new PreviewHighest(), essChat);
|
||||||
|
pm.registerEvents(new ChatLowest(), essChat);
|
||||||
|
pm.registerEvents(new ChatNormal(), essChat);
|
||||||
|
pm.registerEvents(new ChatHighest(), essChat);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleChatApplyPreview(AsyncPlayerChatEvent event) {
|
||||||
|
final ChatProcessingCache.ProcessedChat chat = cache.getProcessedChat(event.getPlayer());
|
||||||
|
if (chat == null) {
|
||||||
|
handleChatFormat(event);
|
||||||
|
handleChatPostFormat(event);
|
||||||
|
} else {
|
||||||
|
event.setFormat(chat.getFormat());
|
||||||
|
event.setMessage(chat.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleChatConfirmPreview(AsyncPlayerChatEvent event) {
|
||||||
|
if (!ess.getSettings().isDebug()) return;
|
||||||
|
|
||||||
|
final ChatProcessingCache.ProcessedChat chat = cache.getProcessedChat(event.getPlayer());
|
||||||
|
if (chat == null) {
|
||||||
|
// Can't confirm preview for some reason
|
||||||
|
essChat.getLogger().info("Processed chat missing for " + event.getPlayer());
|
||||||
|
} else {
|
||||||
|
if (!event.getFormat().equals(chat.getFormat())) {
|
||||||
|
// Chat format modified by another plugin
|
||||||
|
essChat.getLogger().info("Chat format has been modified for " + event.getPlayer());
|
||||||
|
essChat.getLogger().info("Expected '" + chat.getFormat() + "', got '" + event.getFormat());
|
||||||
|
}
|
||||||
|
if (!event.getMessage().equals(chat.getMessage())) {
|
||||||
|
// Chat message modified by another plugin
|
||||||
|
essChat.getLogger().info("Chat message has been modified for " + event.getPlayer());
|
||||||
|
essChat.getLogger().info("Expected '" + chat.getMessage() + "', got '" + event.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private interface PreviewListener extends Listener {
|
||||||
|
void onPlayerChatPreview(AsyncPlayerChatPreviewEvent event);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class PreviewLowest implements PreviewListener {
|
||||||
|
@EventHandler(priority = EventPriority.LOWEST)
|
||||||
|
public void onPlayerChatPreview(AsyncPlayerChatPreviewEvent event) {
|
||||||
|
handleChatFormat(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class PreviewHighest implements PreviewListener {
|
||||||
|
@EventHandler(priority = EventPriority.HIGHEST)
|
||||||
|
public void onPlayerChatPreview(AsyncPlayerChatPreviewEvent event) {
|
||||||
|
handleChatPostFormat(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ChatLowest implements ChatListener {
|
||||||
|
@Override
|
||||||
|
@EventHandler(priority = EventPriority.LOWEST)
|
||||||
|
public void onPlayerChat(AsyncPlayerChatEvent event) {
|
||||||
|
handleChatApplyPreview(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ChatNormal implements ChatListener {
|
||||||
|
@Override
|
||||||
|
@EventHandler(priority = EventPriority.NORMAL)
|
||||||
|
public void onPlayerChat(AsyncPlayerChatEvent event) {
|
||||||
|
handleChatRecipients(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ChatHighest implements ChatListener {
|
||||||
|
@Override
|
||||||
|
@EventHandler(priority = EventPriority.HIGHEST)
|
||||||
|
public void onPlayerChat(AsyncPlayerChatEvent event) {
|
||||||
|
handleChatConfirmPreview(event);
|
||||||
|
handleChatSubmit(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user