From 891be91d69aea46e98425894c2cf514c232b15d5 Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Thu, 8 Oct 2020 00:02:49 +0200 Subject: [PATCH] Fix some mcchat and a reset issue Using ConcurrentHashMaps (#62) Add custom /list to hide vanished players (#120) Fixed /discord reset for non-Paper servers (#103) --- .../discordplugin/fun/FunModule.java | 2 +- .../discordplugin/mcchat/MCChatCustom.java | 32 +++++---- .../discordplugin/mcchat/MCChatListener.java | 38 +++++++---- .../discordplugin/mcchat/MCChatPrivate.java | 66 ++++++++++--------- .../discordplugin/mcchat/MCChatUtils.java | 21 +++--- .../mccommands/DiscordMCCommand.java | 9 ++- 6 files changed, 98 insertions(+), 70 deletions(-) diff --git a/src/main/java/buttondevteam/discordplugin/fun/FunModule.java b/src/main/java/buttondevteam/discordplugin/fun/FunModule.java index d05bd2c..2a455af 100644 --- a/src/main/java/buttondevteam/discordplugin/fun/FunModule.java +++ b/src/main/java/buttondevteam/discordplugin/fun/FunModule.java @@ -100,7 +100,7 @@ public class FunModule extends Component implements Listener { ListC = 0; lastlist = 0; } - if (msglowercased.equals("list") && Bukkit.getOnlinePlayers().size() == lastlistp && ListC++ > 2) // Lowered already + if (msglowercased.equals("/list") && Bukkit.getOnlinePlayers().size() == lastlistp && ListC++ > 2) // Lowered already { DPUtils.reply(message, Mono.empty(), "stop it. You know the answer.").subscribe(); lastlist = 0; diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatCustom.java b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatCustom.java index ee368f5..611d0e8 100644 --- a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatCustom.java +++ b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatCustom.java @@ -20,15 +20,17 @@ public class MCChatCustom { /** * Used for town or nation chats or anything else */ - static ArrayList lastmsgCustom = new ArrayList<>(); + static final ArrayList lastmsgCustom = new ArrayList<>(); public static void addCustomChat(MessageChannel channel, String groupid, Channel mcchannel, User user, DiscordConnectedPlayer dcp, int toggles, Set brtoggles) { - if (mcchannel instanceof ChatRoom) { - ((ChatRoom) mcchannel).joinRoom(dcp); - if (groupid == null) groupid = mcchannel.getGroupID(dcp); + synchronized (lastmsgCustom) { + if (mcchannel instanceof ChatRoom) { + ((ChatRoom) mcchannel).joinRoom(dcp); + if (groupid == null) groupid = mcchannel.getGroupID(dcp); + } + val lmd = new CustomLMD(channel, user, groupid, mcchannel, dcp, toggles, brtoggles); + lastmsgCustom.add(lmd); } - val lmd = new CustomLMD(channel, user, groupid, mcchannel, dcp, toggles, brtoggles); - lastmsgCustom.add(lmd); } public static boolean hasCustomChat(Snowflake channel) { @@ -41,14 +43,16 @@ public class MCChatCustom { } public static boolean removeCustomChat(Snowflake channel) { - MCChatUtils.lastmsgfromd.remove(channel.asLong()); - return lastmsgCustom.removeIf(lmd -> { - if (lmd.channel.getId().asLong() != channel.asLong()) - return false; - if (lmd.mcchannel instanceof ChatRoom) - ((ChatRoom) lmd.mcchannel).leaveRoom(lmd.dcp); - return true; - }); + synchronized (lastmsgCustom) { + MCChatUtils.lastmsgfromd.remove(channel.asLong()); + return lastmsgCustom.removeIf(lmd -> { + if (lmd.channel.getId().asLong() != channel.asLong()) + return false; + if (lmd.mcchannel instanceof ChatRoom) + ((ChatRoom) lmd.mcchannel).leaveRoom(lmd.dcp); + return true; + }); + } } public static List getCustomChats() { diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatListener.java b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatListener.java index 66b207c..a75cb7e 100755 --- a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatListener.java +++ b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatListener.java @@ -131,17 +131,19 @@ public class MCChatListener implements Listener { doit.accept(data); } - val iterator = MCChatCustom.lastmsgCustom.iterator(); - while (iterator.hasNext()) { - val lmd = iterator.next(); - if ((e.isFromCommand() || isdifferentchannel.test(lmd.channel.getId())) //Test if msg is from Discord - && e.getChannel().ID.equals(lmd.mcchannel.ID) //If it's from a command, the command msg has been deleted, so we need to send it - && e.getGroupID().equals(lmd.groupID)) { //Check if this is the group we want to test - #58 - if (e.shouldSendTo(lmd.dcp)) //Check original user's permissions - doit.accept(lmd); - else { - iterator.remove(); //If the user no longer has permission, remove the connection - lmd.channel.createMessage("The user no longer has permission to view the channel, connection removed.").subscribe(); + synchronized (MCChatCustom.lastmsgCustom) { + val iterator = MCChatCustom.lastmsgCustom.iterator(); + while (iterator.hasNext()) { + val lmd = iterator.next(); + if ((e.isFromCommand() || isdifferentchannel.test(lmd.channel.getId())) //Test if msg is from Discord + && e.getChannel().ID.equals(lmd.mcchannel.ID) //If it's from a command, the command msg has been deleted, so we need to send it + && e.getGroupID().equals(lmd.groupID)) { //Check if this is the group we want to test - #58 + if (e.shouldSendTo(lmd.dcp)) //Check original user's permissions + doit.accept(lmd); + else { + iterator.remove(); //If the user no longer has permission, remove the connection + lmd.channel.createMessage("The user no longer has permission to view the channel, connection removed.").subscribe(); + } } } } @@ -217,7 +219,7 @@ public class MCChatListener implements Listener { } private BukkitTask rectask; - private LinkedBlockingQueue recevents = new LinkedBlockingQueue<>(); + private final LinkedBlockingQueue recevents = new LinkedBlockingQueue<>(); private Runnable recrun; private static Thread recthread; @@ -368,6 +370,7 @@ public class MCChatListener implements Listener { return true; } module.log(dsender.getName() + " ran from DC: /" + cmd); + if (runCustomCommand(dsender, cmdlowercased)) return true; val channel = clmd == null ? user.channel().get() : clmd.mcchannel; val ev = new TBMCCommandPreprocessEvent(dsender, channel, dmessage, clmd == null ? dsender : clmd.dcp); Bukkit.getScheduler().runTask(DiscordPlugin.plugin, //Commands need to be run sync @@ -396,6 +399,17 @@ public class MCChatListener implements Listener { return true; } + private boolean runCustomCommand(DiscordSenderBase dsender, String cmdlowercased) { + if (cmdlowercased.startsWith("list")) { + var players = Bukkit.getOnlinePlayers(); + dsender.sendMessage("There are " + players.size() + " out of " + Bukkit.getMaxPlayers() + " players online."); + dsender.sendMessage("Players: " + players.stream().filter(MCChatUtils::checkEssentials) + .map(Player::getDisplayName).collect(Collectors.joining(", "))); + return true; + } + return false; + } + @FunctionalInterface private interface InterruptibleConsumer { void accept(T value) throws TimeoutException, InterruptedException; diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatPrivate.java b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatPrivate.java index 80f1748..0f4cc71 100644 --- a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatPrivate.java +++ b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatPrivate.java @@ -20,32 +20,34 @@ public class MCChatPrivate { static ArrayList lastmsgPerUser = new ArrayList<>(); public static boolean privateMCChat(MessageChannel channel, boolean start, User user, DiscordPlayer dp) { - TBMCPlayer mcp = dp.getAs(TBMCPlayer.class); - if (mcp != null) { // If the accounts aren't connected, can't make a connected sender - val p = Bukkit.getPlayer(mcp.getUUID()); - val op = Bukkit.getOfflinePlayer(mcp.getUUID()); - val mcm = ComponentManager.getIfEnabled(MinecraftChatModule.class); - if (start) { - val sender = DiscordConnectedPlayer.create(user, channel, mcp.getUUID(), op.getName(), mcm); - MCChatUtils.addSender(MCChatUtils.ConnectedSenders, user, sender); - MCChatUtils.LoggedInPlayers.put(mcp.getUUID(), sender); - if (p == null) // Player is offline - If the player is online, that takes precedence - MCChatUtils.callLoginEvents(sender); - } else { - val sender = MCChatUtils.removeSender(MCChatUtils.ConnectedSenders, channel.getId(), user); - assert sender != null; - MCChatUtils.LoggedInPlayers.remove(sender.getUniqueId()); - if (p == null // Player is offline - If the player is online, that takes precedence - && sender.isLoggedIn()) //Don't call the quit event if login failed - MCChatUtils.callLogoutEvent(sender, true); - sender.setLoggedIn(false); - } - } // ---- PermissionsEx warning is normal on logout ---- - if (!start) - MCChatUtils.lastmsgfromd.remove(channel.getId().asLong()); - return start // - ? lastmsgPerUser.add(new MCChatUtils.LastMsgData(channel, user)) // Doesn't support group DMs - : lastmsgPerUser.removeIf(lmd -> lmd.channel.getId().asLong() == channel.getId().asLong()); + synchronized (MCChatUtils.ConnectedSenders) { + TBMCPlayer mcp = dp.getAs(TBMCPlayer.class); + if (mcp != null) { // If the accounts aren't connected, can't make a connected sender + val p = Bukkit.getPlayer(mcp.getUUID()); + val op = Bukkit.getOfflinePlayer(mcp.getUUID()); + val mcm = ComponentManager.getIfEnabled(MinecraftChatModule.class); + if (start) { + val sender = DiscordConnectedPlayer.create(user, channel, mcp.getUUID(), op.getName(), mcm); + MCChatUtils.addSender(MCChatUtils.ConnectedSenders, user, sender); + MCChatUtils.LoggedInPlayers.put(mcp.getUUID(), sender); + if (p == null) // Player is offline - If the player is online, that takes precedence + MCChatUtils.callLoginEvents(sender); + } else { + val sender = MCChatUtils.removeSender(MCChatUtils.ConnectedSenders, channel.getId(), user); + assert sender != null; + MCChatUtils.LoggedInPlayers.remove(sender.getUniqueId()); + if (p == null // Player is offline - If the player is online, that takes precedence + && sender.isLoggedIn()) //Don't call the quit event if login failed + MCChatUtils.callLogoutEvent(sender, true); + sender.setLoggedIn(false); + } + } // ---- PermissionsEx warning is normal on logout ---- + if (!start) + MCChatUtils.lastmsgfromd.remove(channel.getId().asLong()); + return start // + ? lastmsgPerUser.add(new MCChatUtils.LastMsgData(channel, user)) // Doesn't support group DMs + : lastmsgPerUser.removeIf(lmd -> lmd.channel.getId().asLong() == channel.getId().asLong()); + } } public static boolean isMinecraftChatEnabled(DiscordPlayer dp) { @@ -59,11 +61,13 @@ public class MCChatPrivate { } public static void logoutAll() { - for (val entry : MCChatUtils.ConnectedSenders.entrySet()) - for (val valueEntry : entry.getValue().entrySet()) - if (MCChatUtils.getSender(MCChatUtils.OnlineSenders, valueEntry.getKey(), valueEntry.getValue().getUser()) == null) //If the player is online then the fake player was already logged out - MCChatUtils.callLogoutEvent(valueEntry.getValue(), false); //This is sync - MCChatUtils.ConnectedSenders.clear(); + synchronized (MCChatUtils.ConnectedSenders) { + for (val entry : MCChatUtils.ConnectedSenders.entrySet()) + for (val valueEntry : entry.getValue().entrySet()) + if (MCChatUtils.getSender(MCChatUtils.OnlineSenders, valueEntry.getKey(), valueEntry.getValue().getUser()) == null) //If the player is online then the fake player was already logged out + MCChatUtils.callLogoutEvent(valueEntry.getValue(), false); //This is sync + MCChatUtils.ConnectedSenders.clear(); + } } } diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatUtils.java b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatUtils.java index 163f148..467fa30 100644 --- a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatUtils.java +++ b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatUtils.java @@ -34,6 +34,7 @@ import reactor.core.publisher.Mono; import javax.annotation.Nullable; import java.net.InetAddress; import java.util.*; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; import java.util.function.Supplier; @@ -45,13 +46,13 @@ public class MCChatUtils { /** * May contain P<DiscordID> as key for public chat */ - public static final HashMap> UnconnectedSenders = new HashMap<>(); - public static final HashMap> ConnectedSenders = new HashMap<>(); + public static final ConcurrentHashMap> UnconnectedSenders = new ConcurrentHashMap<>(); + public static final ConcurrentHashMap> ConnectedSenders = new ConcurrentHashMap<>(); /** * May contain P<DiscordID> as key for public chat */ - public static final HashMap> OnlineSenders = new HashMap<>(); - public static final HashMap LoggedInPlayers = new HashMap<>(); + public static final ConcurrentHashMap> OnlineSenders = new ConcurrentHashMap<>(); + public static final ConcurrentHashMap LoggedInPlayers = new ConcurrentHashMap<>(); static @Nullable LastMsgData lastmsgdata; static LongObjectHashMap lastmsgfromd = new LongObjectHashMap<>(); // Last message sent by a Discord user, used for clearing checkmarks private static MinecraftChatModule module; @@ -104,28 +105,28 @@ public class MCChatUtils { ((TextChannel) lmd.channel).edit(tce -> tce.setTopic(String.join("\n----\n", s)).setReason("Player list update")).subscribe(); //Don't wait } - private static boolean checkEssentials(Player p) { + static boolean checkEssentials(Player p) { var ess = MainPlugin.ess; if (ess == null) return true; return !ess.getUser(p).isHidden(); } - public static T addSender(HashMap> senders, + public static T addSender(ConcurrentHashMap> senders, User user, T sender) { return addSender(senders, user.getId().asString(), sender); } - public static T addSender(HashMap> senders, + public static T addSender(ConcurrentHashMap> senders, String did, T sender) { var map = senders.get(did); if (map == null) - map = new HashMap<>(); + map = new ConcurrentHashMap<>(); map.put(sender.getChannel().getId(), sender); senders.put(did, map); return sender; } - public static T getSender(HashMap> senders, + public static T getSender(ConcurrentHashMap> senders, Snowflake channel, User user) { var map = senders.get(user.getId().asString()); if (map != null) @@ -133,7 +134,7 @@ public class MCChatUtils { return null; } - public static T removeSender(HashMap> senders, + public static T removeSender(ConcurrentHashMap> senders, Snowflake channel, User user) { var map = senders.get(user.getId().asString()); if (map != null) diff --git a/src/main/java/buttondevteam/discordplugin/mccommands/DiscordMCCommand.java b/src/main/java/buttondevteam/discordplugin/mccommands/DiscordMCCommand.java index 61d9d6b..7d46b3a 100644 --- a/src/main/java/buttondevteam/discordplugin/mccommands/DiscordMCCommand.java +++ b/src/main/java/buttondevteam/discordplugin/mccommands/DiscordMCCommand.java @@ -74,7 +74,7 @@ public class DiscordMCCommand extends ICommand2MC { "This command disables and then enables the plugin." // }) public void reset(CommandSender sender) { - Bukkit.getScheduler().runTaskAsynchronously(DiscordPlugin.plugin, () -> { + Runnable task = () -> { if (!DiscordPlugin.plugin.tryReloadConfig()) { sender.sendMessage("§cFailed to reload config so not resetting. Check the console."); return; @@ -87,7 +87,12 @@ public class DiscordMCCommand extends ICommand2MC { Bukkit.getPluginManager().enablePlugin(DiscordPlugin.plugin); if (!(sender instanceof DiscordSenderBase)) //Sending to Discord errors sender.sendMessage("§bReset finished!"); - }); + }; + if (!Bukkit.getName().equals("Paper")) { + getPlugin().getLogger().warning("Async plugin events are not supported by the server, running on main thread"); + Bukkit.getScheduler().runTask(DiscordPlugin.plugin, task); + } else + Bukkit.getScheduler().runTaskAsynchronously(DiscordPlugin.plugin, task); } @Command2.Subcommand(helpText = {