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