From a501d9d457615de476321da27f5829ad5c6bf7cb Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Wed, 12 Jul 2017 00:00:19 +0200 Subject: [PATCH] Lot of bugfixes and improvements for mcchat #12 (PM support should be finished) #13 works In no particular order: Fixed duplicate responses, messages, wrong quit/join event calls, made the private chat enabled state only persist until the server is restarted, testing message always appears in PMs, added separate senders for public and private chat, sending all kinds of messages to private chat as well, adjusted message for non-whitelisted cmds, changed a lot on sender getting etc. 1-2 hours of sleep, then work, then programming in the morning, more sleep, more programming throughout the day. --- .../discordplugin/DiscordPlayer.java | 11 ++- .../discordplugin/DiscordPlugin.java | 3 +- .../discordplugin/commands/MCChatCommand.java | 19 +++-- .../listeners/CommandListener.java | 9 +- .../listeners/MCChatListener.java | 83 ++++++++++--------- .../discordplugin/listeners/MCListener.java | 45 ++++++---- .../playerfaker/DiscordFakePlayer.java | 2 - 7 files changed, 97 insertions(+), 75 deletions(-) diff --git a/src/main/java/buttondevteam/discordplugin/DiscordPlayer.java b/src/main/java/buttondevteam/discordplugin/DiscordPlayer.java index 48d957a..ec7863c 100644 --- a/src/main/java/buttondevteam/discordplugin/DiscordPlayer.java +++ b/src/main/java/buttondevteam/discordplugin/DiscordPlayer.java @@ -1,12 +1,13 @@ package buttondevteam.discordplugin; +import buttondevteam.discordplugin.listeners.MCChatListener; import buttondevteam.lib.player.ChromaGamerBase; -import buttondevteam.lib.player.PlayerData; import buttondevteam.lib.player.UserClass; @UserClass(foldername = "discord") public class DiscordPlayer extends ChromaGamerBase { private String did; + // private @Getter @Setter boolean minecraftChatEnabled; public DiscordPlayer() { } @@ -17,7 +18,11 @@ public class DiscordPlayer extends ChromaGamerBase { return did; } - public PlayerData minecraftChat() { - return data(false); + /** + * Returns true if player has the private Minecraft chat enabled. For setting the value, see + * {@link MCChatListener#privateMCChat(sx.blah.discord.handle.obj.IChannel, boolean, sx.blah.discord.handle.obj.IUser, DiscordPlayer)} + */ + public boolean isMinecraftChatEnabled() { + return MCChatListener.isMinecraftChatEnabled(this); } } diff --git a/src/main/java/buttondevteam/discordplugin/DiscordPlugin.java b/src/main/java/buttondevteam/discordplugin/DiscordPlugin.java index 2517692..8c446e3 100644 --- a/src/main/java/buttondevteam/discordplugin/DiscordPlugin.java +++ b/src/main/java/buttondevteam/discordplugin/DiscordPlugin.java @@ -256,7 +256,8 @@ public class DiscordPlugin extends JavaPlugin implements IListener { if (channel == chatchannel) MCChatListener.resetLastMessage(); // If this is a chat message, it'll be set again final String content = TBMCCoreAPI.IsTestServer() && channel != chatchannel || channel == botroomchannel // Both are the same for testing - ? "*The following message is from a test server*\n" + message : message; + || channel.isPrivate() // + ? "*The following message is from a test server*\n" + message : message; return perform( () -> embed == null ? channel.sendMessage(content) : channel.sendMessage(content, embed, false)); } catch (Exception e) { diff --git a/src/main/java/buttondevteam/discordplugin/commands/MCChatCommand.java b/src/main/java/buttondevteam/discordplugin/commands/MCChatCommand.java index 6d8f736..a629eda 100644 --- a/src/main/java/buttondevteam/discordplugin/commands/MCChatCommand.java +++ b/src/main/java/buttondevteam/discordplugin/commands/MCChatCommand.java @@ -1,9 +1,9 @@ package buttondevteam.discordplugin.commands; import buttondevteam.discordplugin.DiscordPlayer; +import buttondevteam.discordplugin.DiscordPlugin; import buttondevteam.discordplugin.listeners.MCChatListener; import buttondevteam.lib.TBMCCoreAPI; -import buttondevteam.lib.player.PlayerData; import sx.blah.discord.handle.obj.IMessage; public class MCChatCommand extends DiscordCommandBase { @@ -16,17 +16,18 @@ public class MCChatCommand extends DiscordCommandBase { @Override public void run(IMessage message, String args) { if (!message.getChannel().isPrivate()) { - message.reply("This command can only be issued in a direct message with the bot."); + DiscordPlugin.sendMessageToChannel(message.getChannel(), + "This command can only be issued in a direct message with the bot."); return; } try (final DiscordPlayer user = DiscordPlayer.getUser(message.getAuthor().getStringID(), DiscordPlayer.class)) { - PlayerData mcchat = user.minecraftChat(); - mcchat.set(!mcchat.get()); - MCChatListener.privateMCChat(message.getChannel(), mcchat.get(), message.getAuthor(), user); - message.reply("Minecraft chat " + (mcchat.get() // - ? "enabled. Use '" + message.getClient().getOurUser().mention() - + " mcchat' (with the mention) to disable." // - : "disabled.")); + boolean mcchat = !user.isMinecraftChatEnabled(); + MCChatListener.privateMCChat(message.getChannel(), mcchat, message.getAuthor(), user); + DiscordPlugin.sendMessageToChannel(message.getChannel(), + "Minecraft chat " + (mcchat // + ? "enabled. Use '" + message.getClient().getOurUser().mention() + + " mcchat' (with the mention) to disable." // + : "disabled.")); } catch (Exception e) { TBMCCoreAPI.SendException("Error while setting mcchat for user" + message.getAuthor().getName(), e); } diff --git a/src/main/java/buttondevteam/discordplugin/listeners/CommandListener.java b/src/main/java/buttondevteam/discordplugin/listeners/CommandListener.java index 8313ffa..ddad886 100644 --- a/src/main/java/buttondevteam/discordplugin/listeners/CommandListener.java +++ b/src/main/java/buttondevteam/discordplugin/listeners/CommandListener.java @@ -59,13 +59,10 @@ public class CommandListener { if (event.getMessage().getAuthor().isBot()) return; final IChannel channel = event.getMessage().getChannel(); - if (!channel.getStringID().equals(DiscordPlugin.botchannel.getStringID()) - && (!channel.isPrivate() || DiscordPlugin.checkIfSomeoneIsTestingWhileWeArent())) + if (!channel.getStringID().equals(DiscordPlugin.botchannel.getStringID())) return; if (channel.getStringID().equals(DiscordPlugin.chatchannel.getStringID())) return; // The chat code already handles this - Right now while testing botchannel is the same as chatchannel - if (DiscordPlayer.getUser(event.getAuthor().getStringID(), DiscordPlayer.class).minecraftChat().get()) // Let the MCChatListener handle it - return; event.getMessage().getChannel().setTypingStatus(true); // Fun runCommand(event.getMessage(), true); } @@ -84,8 +81,8 @@ public class CommandListener { DiscordPlugin.sendMessageToChannel(event.getMessage().getChannel(), serverReadyStrings[next]); } if (!event.getMessage().getChannel().isPrivate() // - || DiscordPlayer.getUser(event.getAuthor().getStringID(), DiscordPlayer.class).minecraftChat() - .get() + || DiscordPlayer.getUser(event.getAuthor().getStringID(), DiscordPlayer.class) + .isMinecraftChatEnabled() || DiscordPlugin.checkIfSomeoneIsTestingWhileWeArent()) return; if (event.getMessage().getAuthor().isBot()) diff --git a/src/main/java/buttondevteam/discordplugin/listeners/MCChatListener.java b/src/main/java/buttondevteam/discordplugin/listeners/MCChatListener.java index e97cefe..dfeb248 100644 --- a/src/main/java/buttondevteam/discordplugin/listeners/MCChatListener.java +++ b/src/main/java/buttondevteam/discordplugin/listeners/MCChatListener.java @@ -4,11 +4,12 @@ import java.awt.Color; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; -import java.util.UUID; -import java.util.function.BiFunction; +import java.util.Optional; import java.util.function.Consumer; import java.util.function.Supplier; import java.util.stream.Collectors; +import java.util.stream.Stream; + import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -75,7 +76,7 @@ public class MCChatListener implements Listener, IListener final IUser iUser = data.channel.getUsersHere().stream() .filter(u -> u.getLongID() != u.getClient().getOurUser().getLongID()).findFirst().get(); // Doesn't support group DMs final DiscordPlayer user = DiscordPlayer.getUser(iUser.getStringID(), DiscordPlayer.class); - if (user.minecraftChat().get() && e.shouldSendTo(getSender(data.channel, iUser, user))) + if (user.isMinecraftChatEnabled() && e.shouldSendTo(getSender(data.channel, iUser, user))) doit.accept(data); } } // TODO: Author URL @@ -132,11 +133,11 @@ public class MCChatListener implements Listener, IListener if (start) { val sender = new DiscordConnectedPlayer(user, channel, mcp.getUUID()); ConnectedSenders.put(user.getStringID(), sender); - if (p == null)// If the player is online, that takes precedence + if (p == null)// Player is offline - If the player is online, that takes precedence Bukkit.getPluginManager().callEvent(new PlayerJoinEvent(sender, "")); } else { val sender = ConnectedSenders.remove(user.getStringID()); - if (p == null)// If the player is online, that takes precedence + if (p == null)// Player is offline - If the player is online, that takes precedence Bukkit.getPluginManager().callEvent(new PlayerQuitEvent(sender, "")); } } @@ -145,7 +146,6 @@ public class MCChatListener implements Listener, IListener : lastmsgPerUser.removeIf(lmd -> lmd.channel.getLongID() == channel.getLongID()); } - // // ......................DiscordSender....DiscordConnectedPlayer.DiscordPlayerSender // Offline public chat......x............................................ // Online public chat.......x...........................................x @@ -155,10 +155,21 @@ public class MCChatListener implements Listener, IListener // If leaving the server and private chat is enabled (has ConnectedPlayer), call login in a task on lowest priority // If private chat is enabled and joining the server, logout the fake player on highest priority // If online and disabling private chat, don't logout - // The maps may not contain the senders except for DiscordPlayerSender + // The maps may not contain the senders for UnconnectedSenders + public static boolean isMinecraftChatEnabled(DiscordPlayer dp) { + return lastmsgPerUser.stream().anyMatch( + lmd -> ((IPrivateChannel) lmd.channel).getRecipient().getStringID().equals(dp.getDiscordID())); + } + + /** + * May contain P<DiscordID> as key for public chat + */ public static final HashMap UnconnectedSenders = new HashMap<>(); public static final HashMap ConnectedSenders = new HashMap<>(); + /** + * May contain P<DiscordID> as key for public chat + */ public static final HashMap OnlineSenders = new HashMap<>(); public static short ListC = 0; @@ -166,12 +177,18 @@ public class MCChatListener implements Listener, IListener (lastmsgdata == null ? lastmsgdata = new LastMsgData(DiscordPlugin.chatchannel) : lastmsgdata).message = null; // Don't set the whole object to null, the player and channel information should } // be preserved + public static void sendSystemMessageToChat(String msg) { + DiscordPlugin.sendMessageToChannel(DiscordPlugin.chatchannel, msg); + for (LastMsgData data : lastmsgPerUser) + DiscordPlugin.sendMessageToChannel(data.channel, msg); + } + @Override // Discord public void handle(MessageReceivedEvent event) { val author = event.getMessage().getAuthor(); val user = DiscordPlayer.getUser(author.getStringID(), DiscordPlayer.class); if (!event.getMessage().getChannel().getStringID().equals(DiscordPlugin.chatchannel.getStringID()) - && !(event.getMessage().getChannel().isPrivate() && user.minecraftChat().get() + && !(event.getMessage().getChannel().isPrivate() && user.isMinecraftChatEnabled() && !DiscordPlugin.checkIfSomeoneIsTestingWhileWeArent())) return; resetLastMessage(); @@ -200,12 +217,13 @@ public class MCChatListener implements Listener, IListener if (dsender instanceof DiscordSender && !Arrays.stream(UnconnectedCmds) .anyMatch(s -> cmd.equals(s) || cmd.startsWith(s + " "))) { // Command not whitelisted - dsender.sendMessage( // TODO - "Sorry, you need to be online on the server and have your accounts connected, you can only access these commands:\n" - + Arrays.stream(UnconnectedCmds).map(uc -> "/" + uc) - .collect(Collectors.joining(", ")) - + "\nTo connect your accounts, use @ChromaBot connect in " - + DiscordPlugin.botchannel.mention()); + dsender.sendMessage("Sorry, you can only access these commands:\n" + + Arrays.stream(UnconnectedCmds).map(uc -> "/" + uc).collect(Collectors.joining(", ")) + + (user.getConnectedID(TBMCPlayer.class) == null + ? "\nTo access your commands, first please connect your accounts, using @ChromaBot connect in " + + DiscordPlugin.botchannel.mention() + + "\nThen you can access all of your regular commands (even offline) in private chat: DM me `mcchat`!" + : "\nYou can access all of your regular commands (even offline) in private chat: DM me `mcchat`!")); return; } if (lastlist > 5) { @@ -248,29 +266,18 @@ public class MCChatListener implements Listener, IListener } } - @SuppressWarnings("unchecked") - private DiscordSenderBase getSender(IChannel channel, final IUser author, - DiscordPlayer dp) { - final DiscordSenderBase dsender; - final Player mcp; - final String cid; - BiFunction, Supplier, DiscordSenderBase> getsender = (senders, maker) -> { - if (!senders.containsKey(author.getStringID())) - senders.put(author.getStringID(), maker.get()); - return senders.get(author.getStringID()); - }; - if ((cid = dp.getConnectedID(TBMCPlayer.class)) != null) { // Connected? - if ((mcp = Bukkit.getPlayer(UUID.fromString(cid))) != null) // Online? - Execute as ingame player - dsender = getsender.apply((HashMap) OnlineSenders, - () -> (T) new DiscordPlayerSender(author, channel, mcp)); - else // Offline - dsender = getsender.apply((HashMap) ConnectedSenders, - () -> (T) new DiscordConnectedPlayer(author, channel, UUID.fromString(cid))); - } else { // Not connected - TBMCPlayer p = dp.getAs(TBMCPlayer.class); - dsender = getsender.apply((HashMap) UnconnectedSenders, - () -> (T) new DiscordSender(author, channel, p == null ? null : p.PlayerName().get())); // Display the playername, if found - } - return dsender; + /** + * This method will find the best sender to use: if the player is online, use that, if not but connected then use that etc. + */ + private DiscordSenderBase getSender(IChannel channel, final IUser author, DiscordPlayer dp) { + val key = (channel.isPrivate() ? "" : "P") + author.getStringID(); + return Stream.>>of( // https://stackoverflow.com/a/28833677/2703239 + () -> Optional.ofNullable(OnlineSenders.get(key)), // Find first non-null + () -> Optional.ofNullable(ConnectedSenders.get(author.getStringID())), // This doesn't support it + () -> Optional.ofNullable(UnconnectedSenders.get(key)), () -> { + val dsender = new DiscordSender(author, channel); + UnconnectedSenders.put(key, dsender); + return Optional.of(dsender); + }).map(Supplier::get).filter(Optional::isPresent).map(Optional::get).findFirst().get(); } } diff --git a/src/main/java/buttondevteam/discordplugin/listeners/MCListener.java b/src/main/java/buttondevteam/discordplugin/listeners/MCListener.java index 8dbec53..dc7fcad 100644 --- a/src/main/java/buttondevteam/discordplugin/listeners/MCListener.java +++ b/src/main/java/buttondevteam/discordplugin/listeners/MCListener.java @@ -1,10 +1,13 @@ package buttondevteam.discordplugin.listeners; +import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.entity.PlayerDeathEvent; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.event.server.ServerCommandEvent; import com.earth2me.essentials.CommandSource; @@ -16,6 +19,7 @@ import buttondevteam.discordplugin.DiscordPlugin; import buttondevteam.discordplugin.commands.ConnectCommand; import buttondevteam.lib.TBMCCoreAPI; import buttondevteam.lib.player.*; +import lombok.val; import net.ess3.api.events.*; import sx.blah.discord.handle.obj.IRole; import sx.blah.discord.handle.obj.IUser; @@ -23,14 +27,22 @@ import sx.blah.discord.util.DiscordException; import sx.blah.discord.util.MissingPermissionsException; public class MCListener implements Listener { - @EventHandler + @EventHandler(priority = EventPriority.LOWEST) public void onPlayerJoin(TBMCPlayerJoinEvent e) { if (e.getPlayer() instanceof DiscordConnectedPlayer) return; // Don't show the joined message for the fake player final Player p = e.getPlayer(); DiscordPlayer dp = e.GetPlayer().getAs(DiscordPlayer.class); - // if(dp!=null) //TODO - // MCChatListener.OnlineSenders.put(dp.getDiscordID(), new DiscordPlayerSender(DiscordPlugin.dc.getUserByID(Long.parseLong(dp.getDiscordID()), channel, player)) + if (dp != null) { + val user = DiscordPlugin.dc.getUserByID(Long.parseLong(dp.getDiscordID())); + MCChatListener.OnlineSenders.put(dp.getDiscordID(), + new DiscordPlayerSender(user, user.getOrCreatePMChannel(), p)); + MCChatListener.OnlineSenders.put("P" + dp.getDiscordID(), + new DiscordPlayerSender(user, DiscordPlugin.chatchannel, p)); + MCChatListener.ConnectedSenders.values().stream() + .filter(s -> s.getUniqueId().equals(e.getPlayer().getUniqueId())).findAny() + .ifPresent(dcp -> Bukkit.getPluginManager().callEvent(new PlayerQuitEvent(dcp, ""))); + } if (ConnectCommand.WaitingToConnect.containsKey(e.GetPlayer().PlayerName().get())) { IUser user = DiscordPlugin.dc .getUserByID(Long.parseLong(ConnectCommand.WaitingToConnect.get(e.GetPlayer().PlayerName().get()))); @@ -38,18 +50,20 @@ public class MCListener implements Listener { + " do /discord accept"); p.sendMessage("§bIf it wasn't you, do /discord decline"); } - DiscordPlugin.sendMessageToChannel(DiscordPlugin.chatchannel, - e.GetPlayer().PlayerName().get() + " joined the game"); + MCChatListener.sendSystemMessageToChat(e.GetPlayer().PlayerName().get() + " joined the game"); MCChatListener.ListC = 0; } - @EventHandler + @EventHandler(priority = EventPriority.HIGHEST) public void onPlayerLeave(TBMCPlayerQuitEvent e) { - if (MCChatListener.OnlineSenders.entrySet() - .removeIf(entry -> entry.getValue().getUniqueId().equals(e.getPlayer().getUniqueId()))) - ; // TODO - DiscordPlugin.sendMessageToChannel(DiscordPlugin.chatchannel, - e.GetPlayer().PlayerName().get() + " left the game"); + if (e.getPlayer() instanceof DiscordConnectedPlayer) + return; // Only care about real users + MCChatListener.OnlineSenders.entrySet() + .removeIf(entry -> entry.getValue().getUniqueId().equals(e.getPlayer().getUniqueId())); + MCChatListener.ConnectedSenders.values().stream() + .filter(s -> s.getUniqueId().equals(e.getPlayer().getUniqueId())).findAny() + .ifPresent(dcp -> Bukkit.getPluginManager().callEvent(new PlayerJoinEvent(dcp, ""))); + MCChatListener.sendSystemMessageToChat(e.GetPlayer().PlayerName().get() + " left the game"); } @EventHandler @@ -70,16 +84,15 @@ public class MCListener implements Listener { @EventHandler(priority = EventPriority.LOW) public void onPlayerDeath(PlayerDeathEvent e) { - DiscordPlugin.sendMessageToChannel(DiscordPlugin.chatchannel, e.getDeathMessage()); + MCChatListener.sendSystemMessageToChat(e.getDeathMessage()); } @EventHandler public void onPlayerAFK(AfkStatusChangeEvent e) { - if (e.isCancelled()) + if (e.isCancelled() || !e.getAffected().getBase().isOnline()) return; - DiscordPlugin.sendMessageToChannel(DiscordPlugin.chatchannel, - DiscordPlugin.sanitizeString(e.getAffected().getBase().getDisplayName()) + " is " - + (e.getValue() ? "now" : "no longer") + " AFK."); + MCChatListener.sendSystemMessageToChat(DiscordPlugin.sanitizeString(e.getAffected().getBase().getDisplayName()) + + " is " + (e.getValue() ? "now" : "no longer") + " AFK."); } @EventHandler diff --git a/src/main/java/buttondevteam/discordplugin/playerfaker/DiscordFakePlayer.java b/src/main/java/buttondevteam/discordplugin/playerfaker/DiscordFakePlayer.java index 09ecf9b..729c873 100644 --- a/src/main/java/buttondevteam/discordplugin/playerfaker/DiscordFakePlayer.java +++ b/src/main/java/buttondevteam/discordplugin/playerfaker/DiscordFakePlayer.java @@ -9,7 +9,6 @@ import org.bukkit.conversations.Conversation; import org.bukkit.conversations.ConversationAbandonedEvent; import org.bukkit.entity.*; import org.bukkit.event.player.AsyncPlayerChatEvent; -import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.map.MapView; import org.bukkit.permissions.PermissibleBase; import org.bukkit.permissions.ServerOperator; @@ -29,7 +28,6 @@ public class DiscordFakePlayer extends DiscordHumanEntity implements Player { perm = new PermissibleBase(new ServerOperator() { private @Getter @Setter boolean op; }); - Bukkit.getPluginManager().callEvent(new PlayerJoinEvent(this, "Discord fake player joined")); } @Delegate