From c25767e473390372213f29debfe042ac1cc65ed9 Mon Sep 17 00:00:00 2001 From: Teriuihi Date: Sun, 7 Apr 2024 18:07:20 +0200 Subject: [PATCH] Add VoteToMuteHelper and enhance ActiveVoteToMute Implemented a new module titled VoteToMuteHelper to enhance the voting system and augment the user experience. This module enhances the system by providing relevant player suggestions and setting up the mute player. Made updates to the ActiveVoteToMute module to handle potential voters and mute the player if the vote is passed. VoteToMute module is also updated to include the total eligible players. The code is made robust by adding appropriate error checks. --- .../com/alttd/velocitychat/VelocityChat.java | 6 +- .../velocitychat/commands/VoteToMute.java | 109 ++++---- .../commands/VoteToMuteHelper.java | 261 ++++++++++++++++++ .../vote_to_mute/ActiveVoteToMute.java | 140 ++++++++-- .../vote_to_mute/VoteToMuteStarter.java | 22 +- .../listeners/ProxyPlayerListener.java | 18 +- 6 files changed, 471 insertions(+), 85 deletions(-) create mode 100644 velocity/src/main/java/com/alttd/velocitychat/commands/VoteToMuteHelper.java diff --git a/velocity/src/main/java/com/alttd/velocitychat/VelocityChat.java b/velocity/src/main/java/com/alttd/velocitychat/VelocityChat.java index 44ed641..a53c34e 100755 --- a/velocity/src/main/java/com/alttd/velocitychat/VelocityChat.java +++ b/velocity/src/main/java/com/alttd/velocitychat/VelocityChat.java @@ -5,6 +5,7 @@ import com.alttd.chat.ChatImplementation; import com.alttd.chat.managers.ChatUserManager; import com.alttd.chat.managers.PartyManager; import com.alttd.chat.objects.ChatUser; +import com.alttd.chat.objects.chat_log.ChatLogHandler; import com.alttd.velocitychat.commands.*; import com.alttd.chat.config.Config; import com.alttd.chat.database.DatabaseConnection; @@ -23,6 +24,7 @@ import com.velocitypowered.api.event.proxy.ProxyInitializeEvent; import com.velocitypowered.api.plugin.Dependency; import com.velocitypowered.api.plugin.Plugin; import com.velocitypowered.api.plugin.annotation.DataDirectory; +import com.velocitypowered.api.proxy.ConsoleCommandSource; import com.velocitypowered.api.proxy.ProxyServer; import com.velocitypowered.api.proxy.messages.ChannelIdentifier; import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier; @@ -108,12 +110,14 @@ public class VelocityChat { } public void loadCommands() { + ChatLogHandler instance = ChatLogHandler.getInstance(); //TODO disable logging part new SilentJoinCommand(server); new GlobalAdminChat(server); new Reload(server); new MailCommand(server); new Report(server); - new VoteToMute(server); + new VoteToMute(server, instance); + new VoteToMuteHelper(server); server.getCommandManager().register("party", new PartyCommand()); // all (proxy)commands go here } diff --git a/velocity/src/main/java/com/alttd/velocitychat/commands/VoteToMute.java b/velocity/src/main/java/com/alttd/velocitychat/commands/VoteToMute.java index 115bb73..c6dc8e8 100644 --- a/velocity/src/main/java/com/alttd/velocitychat/commands/VoteToMute.java +++ b/velocity/src/main/java/com/alttd/velocitychat/commands/VoteToMute.java @@ -1,8 +1,8 @@ package com.alttd.velocitychat.commands; -import com.alttd.chat.config.Config; +import com.alttd.chat.objects.chat_log.ChatLogHandler; import com.alttd.chat.util.Utility; -import com.alttd.velocitychat.commands.vote_to_mute.ActiveVoteToMute; +import com.alttd.velocitychat.commands.vote_to_mute.VoteToMuteStarter; import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.builder.RequiredArgumentBuilder; @@ -17,7 +17,6 @@ import com.velocitypowered.api.proxy.ServerConnection; import com.velocitypowered.api.proxy.server.RegisteredServer; import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; -import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Optional; @@ -25,7 +24,7 @@ import java.util.stream.Collectors; public class VoteToMute { - public VoteToMute(ProxyServer proxyServer) { + public VoteToMute(ProxyServer proxyServer, ChatLogHandler chatLogHandler) { RequiredArgumentBuilder playerNode = RequiredArgumentBuilder .argument("player", StringArgumentType.string()) .suggests((context, builder) -> { @@ -59,77 +58,71 @@ public class VoteToMute { return 1; }); - RequiredArgumentBuilder yesNoNode = RequiredArgumentBuilder. - argument("vote", StringArgumentType.string()) - .suggests(((commandContext, suggestionsBuilder) -> { - List yesNoValues = Arrays.asList("yes", "no"); - String remaining = suggestionsBuilder.getRemaining().toLowerCase(); - yesNoValues.stream() - .filter((String str) -> str.toLowerCase().startsWith(remaining)) - .map(StringArgumentType::escapeIfRequired) - .forEach(suggestionsBuilder::suggest); - return suggestionsBuilder.buildFuture(); - })); - - LiteralArgumentBuilder voteNode = LiteralArgumentBuilder - .literal("vote") - .then(playerNode - .then(yesNoNode - .executes(commandContext -> { - if (!(commandContext.getSource() instanceof Player)) { - commandContext.getSource().sendMessage(Utility.parseMiniMessage( - "Only players are allowed to vote")); - } - Player source = (Player) commandContext.getSource(); - String playerName = commandContext.getArgument("player", String.class); - Optional optionalActiveVoteToMute = ActiveVoteToMute.getInstance(playerName); - if (optionalActiveVoteToMute.isEmpty()) { - commandContext.getSource().sendMessage(Utility.parseMiniMessage( - "This player does not have an active vote to mute them")); - return 1; - } - ActiveVoteToMute activeVoteToMute = optionalActiveVoteToMute.get(); - String vote = commandContext.getArgument("vote", String.class); - switch (vote.toLowerCase()) { - case "yes" -> activeVoteToMute.vote(source.getUniqueId(), true); - case "no" -> activeVoteToMute.vote(source.getUniqueId(), false); - default -> commandContext.getSource().sendMessage(Utility.parseMiniMessage( - " is not a valid vote option", Placeholder.parsed("vote", vote))); - } - return 1; - })).executes(context -> { - sendHelpMessage(context.getSource()); - return 1; - })).executes(context -> { - sendHelpMessage(context.getSource()); - return 1; - }); - LiteralCommandNode command = LiteralArgumentBuilder .literal("votetomute") .requires(commandSource -> commandSource.hasPermission("chat.vote-to-mute")) .requires(commandSource -> commandSource instanceof Player) .then(playerNode + .suggests(((commandContext, suggestionsBuilder) -> { + if (!(commandContext.getSource() instanceof Player player)) { + return suggestionsBuilder.buildFuture(); + } + Optional currentServer = player.getCurrentServer(); + if (currentServer.isEmpty()) { + sendHelpMessage(commandContext.getSource()); + return suggestionsBuilder.buildFuture(); + } + String remaining = suggestionsBuilder.getRemaining().toLowerCase(); + currentServer.get().getServer().getPlayersConnected().stream() + .filter(connectedPlayer -> connectedPlayer.hasPermission("chat.affected-by-vote-to-mute")) + .map(Player::getUsername) + .filter((String str) -> str.toLowerCase().startsWith(remaining)) + .map(StringArgumentType::escapeIfRequired) + .forEach(suggestionsBuilder::suggest); + return suggestionsBuilder.buildFuture(); + })) .executes(commandContext -> { String playerName = commandContext.getArgument("player", String.class); Optional optionalPlayer = proxyServer.getPlayer(playerName); if (optionalPlayer.isEmpty()) { commandContext.getSource().sendMessage(Utility.parseMiniMessage( - "Player is not online", + "Player is not online.", Placeholder.parsed("player", playerName))); return 1; } + Player voteTarget = optionalPlayer.get(); + if (!voteTarget.hasPermission("chat.affected-by-vote-to-mute")) { + commandContext.getSource().sendMessage(Utility.parseMiniMessage( + "Player can not be muted by a vote.", + Placeholder.parsed("player", playerName))); + return 1; + } + Player player = (Player) commandContext.getSource(); + Optional currentServer = player.getCurrentServer(); + if (currentServer.isEmpty()) { + sendHelpMessage(commandContext.getSource()); + return 1; + } + RegisteredServer server = currentServer.get().getServer(); + boolean countLowerRanks = false; + long count = getTotalEligiblePlayers(server, false); + if (count < 10) { + countLowerRanks = true; + count = getTotalEligiblePlayers(server, true); + if (count < 10) { + commandContext.getSource().sendMessage(Utility.parseMiniMessage("Not enough eligible players online to vote.")); + return 1; + } + } + new VoteToMuteStarter(chatLogHandler, voteTarget, player, server.getServerInfo().getName(), countLowerRanks) + .start(); return 1; })) - .then(voteNode) .executes(context -> { sendHelpMessage(context.getSource()); return 1; }) .build(); - //TODO test command - //TODO add command to pick out the messages - //TODO add command to go to the next page BrigadierCommand brigadierCommand = new BrigadierCommand(command); @@ -140,6 +133,12 @@ public class VoteToMute { proxyServer.getCommandManager().register(meta, brigadierCommand); } + private int getTotalEligiblePlayers(RegisteredServer server, boolean countLowerRanks) { + return (int) server.getPlayersConnected().stream() + .filter(player -> countLowerRanks ? player.hasPermission("chat.backup-vote-to-mute") : player.hasPermission("chat.vote-to-mute")) + .count(); + } + private void sendHelpMessage(CommandSource commandSource) { commandSource.sendMessage(Utility.parseMiniMessage("Use: /votetomute .")); } diff --git a/velocity/src/main/java/com/alttd/velocitychat/commands/VoteToMuteHelper.java b/velocity/src/main/java/com/alttd/velocitychat/commands/VoteToMuteHelper.java new file mode 100644 index 0000000..b2c6666 --- /dev/null +++ b/velocity/src/main/java/com/alttd/velocitychat/commands/VoteToMuteHelper.java @@ -0,0 +1,261 @@ +package com.alttd.velocitychat.commands; + +import com.alttd.chat.util.Utility; +import com.alttd.velocitychat.commands.vote_to_mute.ActiveVoteToMute; +import com.alttd.velocitychat.commands.vote_to_mute.VoteToMuteStarter; +import com.mojang.brigadier.arguments.IntegerArgumentType; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.builder.RequiredArgumentBuilder; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.tree.LiteralCommandNode; +import com.velocitypowered.api.command.BrigadierCommand; +import com.velocitypowered.api.command.CommandMeta; +import com.velocitypowered.api.command.CommandSource; +import com.velocitypowered.api.proxy.Player; +import com.velocitypowered.api.proxy.ProxyServer; +import com.velocitypowered.api.proxy.ServerConnection; +import com.velocitypowered.api.proxy.server.RegisteredServer; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; + +import java.time.Duration; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +public class VoteToMuteHelper { + + public VoteToMuteHelper(ProxyServer proxyServer) { + RequiredArgumentBuilder playerNode = RequiredArgumentBuilder + .argument("player", StringArgumentType.string()) + .suggests((context, builder) -> { + List possiblePlayers; + if (context.getSource() instanceof Player player) { + Optional currentServer = player.getCurrentServer(); + if (currentServer.isPresent()) { + possiblePlayers = getEligiblePlayers(currentServer.get().getServer()); + } else { + possiblePlayers = getEligiblePlayers(proxyServer); + } + } else { + possiblePlayers = getEligiblePlayers(proxyServer); + } + Collection possibleValues = possiblePlayers.stream() + .map(Player::getUsername) + .toList(); + + if (possibleValues.isEmpty()) + return Suggestions.empty(); + + String remaining = builder.getRemaining().toLowerCase(); + possibleValues.stream() + .filter(str -> str.toLowerCase().startsWith(remaining)) + .map(StringArgumentType::escapeIfRequired) + .forEach(builder::suggest); + return builder.buildFuture(); + }) + .executes(context -> { + sendHelpMessage(context.getSource()); + return 1; + }); + + RequiredArgumentBuilder yesNoNode = RequiredArgumentBuilder. + argument("yesNo", StringArgumentType.string()) + .suggests(((commandContext, suggestionsBuilder) -> { + List yesNoValues = Arrays.asList("yes", "no"); + String remaining = suggestionsBuilder.getRemaining().toLowerCase(); + yesNoValues.stream() + .filter((String str) -> str.toLowerCase().startsWith(remaining)) + .map(StringArgumentType::escapeIfRequired) + .forEach(suggestionsBuilder::suggest); + return suggestionsBuilder.buildFuture(); + })); + + LiteralArgumentBuilder pageNode = LiteralArgumentBuilder + .literal("page") + .requires(commandSource -> commandSource.hasPermission("chat.vote-to-mute")) + .then(RequiredArgumentBuilder.argument("page number", IntegerArgumentType.integer(1)) + .suggests(((commandContext, suggestionsBuilder) -> { + if (!(commandContext.getSource() instanceof Player player)) { + return suggestionsBuilder.buildFuture(); + } + Optional instance = VoteToMuteStarter.getInstance(player.getUniqueId()); + if (instance.isEmpty()) { + return suggestionsBuilder.buildFuture(); + } + VoteToMuteStarter voteToMuteStarter = instance.get(); + String remaining = suggestionsBuilder.getRemaining().toLowerCase(); + int totalPages = voteToMuteStarter.getTotalPages(); + IntStream.range(1, totalPages + 1) + .mapToObj(String::valueOf) + .filter((String str) -> str.toLowerCase().startsWith(remaining)) + .map(StringArgumentType::escapeIfRequired) + .forEach(suggestionsBuilder::suggest); + return suggestionsBuilder.buildFuture(); + })) + .executes(commandContext -> { + if (!(commandContext.getSource() instanceof Player player)) { + commandContext.getSource().sendMessage(Utility.parseMiniMessage("Only players can use this command.")); + return 1; + } + Optional instance = VoteToMuteStarter.getInstance(player.getUniqueId()); + if (instance.isEmpty()) { + commandContext.getSource().sendMessage(Utility.parseMiniMessage("You don't have an active vote to mute.")); + return 1; + } + int pageNumber = commandContext.getArgument("page number", Integer.class); + instance.get().showPage(pageNumber); + return 1; + }) + ).executes(commandContext -> { + sendHelpMessage(commandContext.getSource()); + return 1; + }); + + LiteralArgumentBuilder enterPageNode = LiteralArgumentBuilder + .literal("messages") + .requires(commandSource -> commandSource.hasPermission("chat.vote-to-mute")) + .then(RequiredArgumentBuilder.argument("list of messages", StringArgumentType.greedyString()) + .executes(commandContext -> { + if (!(commandContext.getSource() instanceof Player player)) { + commandContext.getSource().sendMessage(Utility.parseMiniMessage("Only players can use this command.")); + return 1; + } + Optional instance = VoteToMuteStarter.getInstance(player.getUniqueId()); + if (instance.isEmpty()) { + commandContext.getSource().sendMessage(Utility.parseMiniMessage("You don't have an active vote to mute.")); + return 1; + } + String listOfPages = commandContext.getArgument("list of messages", String.class); + if (!listOfPages.matches("([1-9][0-9]*, )*[1-9][0-9]*")) { + commandContext.getSource().sendMessage(Utility.parseMiniMessage("Please make sure to format the command correctly.")); + return 1; + } + VoteToMuteStarter voteToMuteStarter = instance.get(); + + List collect = Arrays.stream(listOfPages.split(", ")) + .map(Integer::parseInt) + .collect(Collectors.toList()); + Optional max = collect.stream().max(Integer::compare); + if (max.isEmpty()) { + commandContext.getSource().sendMessage(Utility.parseMiniMessage("Some of your selected messages do not exist.")); + return 1; + } + int highestLogEntry = max.get(); + + if (voteToMuteStarter.getTotalLogEntries() > highestLogEntry) { + commandContext.getSource().sendMessage(Utility.parseMiniMessage("Some of your selected messages do not exist.")); + return 1; + } + + Optional currentServer = player.getCurrentServer(); + if (currentServer.isEmpty()) { + sendHelpMessage(commandContext.getSource()); + return 1; + } + + Component chatLogs = voteToMuteStarter.getChatLogsAndClose(collect); + RegisteredServer server = currentServer.get().getServer(); + long count = getTotalEligiblePlayers(server, voteToMuteStarter.countLowerRanks()); + new ActiveVoteToMute(voteToMuteStarter.getVotedPlayer(), server, proxyServer, Duration.ofMinutes(5), + (int) count, voteToMuteStarter.countLowerRanks(), chatLogs) + .start(); + return 1; + }) + ).executes(commandContext -> { + sendHelpMessage(commandContext.getSource()); + return 1; + }); + + LiteralArgumentBuilder voteNode = LiteralArgumentBuilder + .literal("vote") + .then(playerNode + .then(yesNoNode + .executes(commandContext -> { + if (!(commandContext.getSource() instanceof Player)) { + commandContext.getSource().sendMessage(Utility.parseMiniMessage( + "Only players are allowed to vote")); + } + Player source = (Player) commandContext.getSource(); + String playerName = commandContext.getArgument("player", String.class); + Optional optionalActiveVoteToMute = ActiveVoteToMute.getInstance(playerName); + if (optionalActiveVoteToMute.isEmpty()) { + commandContext.getSource().sendMessage(Utility.parseMiniMessage( + "This player does not have an active vote to mute them.")); + return 1; + } + ActiveVoteToMute activeVoteToMute = optionalActiveVoteToMute.get(); + + if (!activeVoteToMute.countLowerRanks()) { + if (!source.hasPermission("chat.vote-to-mute")) { + source.sendMessage(Utility.parseMiniMessage("You are not eligible to vote.")); + return 1; + } + } + + String vote = commandContext.getArgument("yesno", String.class); + switch (vote.toLowerCase()) { + case "yes" -> activeVoteToMute.vote(source.getUniqueId(), true); + case "no" -> activeVoteToMute.vote(source.getUniqueId(), false); + default -> commandContext.getSource().sendMessage(Utility.parseMiniMessage( + " is not a valid vote option", Placeholder.parsed("vote", vote))); + } + return 1; + })).executes(context -> { + sendHelpMessage(context.getSource()); + return 1; + })).executes(context -> { + sendHelpMessage(context.getSource()); + return 1; + }); + + LiteralCommandNode command = LiteralArgumentBuilder + .literal("votetomutehelper") + .requires(commandSource -> commandSource.hasPermission("chat.backup-vote-to-mute")) + .requires(commandSource -> commandSource instanceof Player) + .then(voteNode) + .then(pageNode) + .then(enterPageNode) + .executes(context -> { + sendHelpMessage(context.getSource()); + return 1; + }) + .build(); + + BrigadierCommand brigadierCommand = new BrigadierCommand(command); + + CommandMeta.Builder metaBuilder = proxyServer.getCommandManager().metaBuilder(brigadierCommand); + + CommandMeta meta = metaBuilder.build(); + + proxyServer.getCommandManager().register(meta, brigadierCommand); + } + + private int getTotalEligiblePlayers(RegisteredServer server, boolean countLowerRanks) { + return (int) server.getPlayersConnected().stream() + .filter(player -> countLowerRanks ? player.hasPermission("chat.backup-vote-to-mute") : player.hasPermission("chat.vote-to-mute")) + .count(); + } + + private void sendHelpMessage(CommandSource commandSource) { + commandSource.sendMessage(Utility.parseMiniMessage("Use: /votetomutehelper .")); + } + + private List getEligiblePlayers(ProxyServer proxyServer) { + return proxyServer.getAllPlayers().stream() + .filter(player -> player.hasPermission("chat.affected-by-vote-to-mute")) + .collect(Collectors.toList()); + } + + private List getEligiblePlayers(RegisteredServer registeredServer) { + return registeredServer.getPlayersConnected().stream() + .filter(player -> player.hasPermission("chat.affected-by-vote-to-mute")) + .collect(Collectors.toList()); + + } + +} diff --git a/velocity/src/main/java/com/alttd/velocitychat/commands/vote_to_mute/ActiveVoteToMute.java b/velocity/src/main/java/com/alttd/velocitychat/commands/vote_to_mute/ActiveVoteToMute.java index 47ef42f..bfb4c62 100644 --- a/velocity/src/main/java/com/alttd/velocitychat/commands/vote_to_mute/ActiveVoteToMute.java +++ b/velocity/src/main/java/com/alttd/velocitychat/commands/vote_to_mute/ActiveVoteToMute.java @@ -2,29 +2,35 @@ package com.alttd.velocitychat.commands.vote_to_mute; import com.alttd.chat.util.Utility; import com.velocitypowered.api.proxy.Player; +import com.velocitypowered.api.proxy.ProxyServer; +import com.velocitypowered.api.proxy.ServerConnection; import com.velocitypowered.api.proxy.server.RegisteredServer; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; import org.jetbrains.annotations.NotNull; import java.time.Duration; -import java.time.Instant; import java.util.HashMap; import java.util.HashSet; import java.util.Optional; import java.util.UUID; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; public class ActiveVoteToMute { private static final HashMap instances = new HashMap<>(); private static final Component prefix = Utility.parseMiniMessage("[VoteMute]"); - private final Instant start; private final Player votedPlayer; - private final Duration duration; private HashSet votedFor = new HashSet<>(); private HashSet votedAgainst = new HashSet<>(); private int totalEligibleVoters; + private final boolean countLowerRanks; + private final RegisteredServer server; + private final ProxyServer proxyServer; + private final Component chatLogs; public static Optional getInstance(String username) { if (!instances.containsKey(username)) @@ -32,26 +38,71 @@ public class ActiveVoteToMute { return Optional.of(instances.get(username)); } - public ActiveVoteToMute(@NotNull Player votedPlayer, Duration duration, int totalEligibleVoters) { - this.start = Instant.now(); - this.votedPlayer = votedPlayer; - this.duration = duration; - this.totalEligibleVoters = totalEligibleVoters; - instances.put(votedPlayer.getUsername(), this); + public static void removePotentialVoter(Player player, RegisteredServer previousServer) { + if (!player.hasPermission("chat.backup-vote-to-mute")) + return; + if (player.hasPermission("chat.vote-to-mute")) { + instances.values().stream() + .filter(activeVoteToMute -> previousServer == null || activeVoteToMute.getServer().getServerInfo().hashCode() == previousServer.getServerInfo().hashCode()) + .forEach(inst -> inst.removeEligibleVoter(player.getUniqueId())); + } else { + instances.values().stream() + .filter(ActiveVoteToMute::countLowerRanks) + .filter(activeVoteToMute -> previousServer == null || activeVoteToMute.getServer().getServerInfo().hashCode() == previousServer.getServerInfo().hashCode()) + .forEach(inst -> inst.removeEligibleVoter(player.getUniqueId())); + } } - public void start(@NotNull RegisteredServer registeredServer, Component chatLogs) { - Component message = Utility.parseMiniMessage( - String.format(""" - [VoteMute] A vote to mute for one hour has been started, please read the logs below before voting. - - Click: Mute --- Don't mute""", - votedPlayer.getUsername(), votedPlayer.getUsername()), - Placeholder.component("prefix", prefix), - Placeholder.parsed("player", votedPlayer.getUsername()), - Placeholder.component("logs", chatLogs)); - registeredServer.getPlayersConnected().stream() - .filter(player -> player.hasPermission("chat.vote-to-mute")) + public static void addPotentialVoter(Player player, ServerConnection server) { + if (!player.hasPermission("chat.backup-vote-to-mute")) + return; + if (player.hasPermission("chat.vote-to-mute")) { + instances.values().stream() + .filter(activeVoteToMute -> activeVoteToMute.getServer().getServerInfo().hashCode() == server.getServerInfo().hashCode()) + .forEach(activeVoteToMute -> activeVoteToMute.addEligibleVoter(player)); + } else { + instances.values().stream() + .filter(ActiveVoteToMute::countLowerRanks) + .filter(activeVoteToMute -> activeVoteToMute.getServer().getServerInfo().hashCode() == server.getServerInfo().hashCode()) + .forEach(activeVoteToMute -> activeVoteToMute.addEligibleVoter(player)); + } + } + + public ActiveVoteToMute(@NotNull Player votedPlayer, @NotNull RegisteredServer server, ProxyServer proxyServer, Duration duration, + int totalEligibleVoters, boolean countLowerRanks, Component chatLogs) { + this.chatLogs = chatLogs; + this.votedPlayer = votedPlayer; + this.totalEligibleVoters = totalEligibleVoters; + this.countLowerRanks = countLowerRanks; + this.server = server; + this.proxyServer = proxyServer; + instances.put(votedPlayer.getUsername(), this); + ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(); + executorService.schedule(this::endVote, + duration.toMinutes(), TimeUnit.MINUTES); + } + + private RegisteredServer getServer() { + return server; + } + + private void endVote() { + instances.remove(votedPlayer.getUsername()); + if (votePassed()) { + mutePlayer(); + return; + } + Component message = Utility.parseMiniMessage(" The vote to mute has failed, they will not be muted.", + Placeholder.component("prefix", prefix), Placeholder.parsed("player", votedPlayer.getUsername())); + server.getPlayersConnected().stream() + .filter(player -> countLowerRanks ? player.hasPermission("chat.backup-vote-to-mute") : player.hasPermission("chat.vote-to-mute")) + .forEach(player -> player.sendMessage(message)); + } + + public void start() { + Component message = getVoteStartMessage(); + server.getPlayersConnected().stream() + .filter(player -> countLowerRanks ? player.hasPermission("chat.backup-vote-to-mute") : player.hasPermission("chat.vote-to-mute")) .forEach(player -> player.sendMessage(message)); } @@ -59,6 +110,11 @@ public class ActiveVoteToMute { if (votedToMute) { votedFor.add(uuid); votedAgainst.remove(uuid); + if (!votePassed()) { + return; + } + instances.remove(votedPlayer.getUsername()); + mutePlayer(); } else { votedAgainst.add(uuid); votedFor.remove(uuid); @@ -73,9 +129,43 @@ public class ActiveVoteToMute { return votedFor.size() / totalVotes > 0.6; } - public boolean voteEnded() { - if (votedFor.size() + votedAgainst.size() == totalEligibleVoters) - return true; - return duration.minus(Duration.between(start, Instant.now())).isNegative(); + public boolean countLowerRanks() { + return countLowerRanks; + } + + private void mutePlayer() { + Component message = Utility.parseMiniMessage(" The vote to mute has passed, they will be muted.", + Placeholder.component("prefix", prefix), Placeholder.parsed("player", votedPlayer.getUsername())); + server.getPlayersConnected().stream() + .filter(player -> countLowerRanks ? player.hasPermission("chat.backup-vote-to-mute") : player.hasPermission("chat.vote-to-mute")) + .forEach(player -> player.sendMessage(message)); + proxyServer.getCommandManager().executeAsync(proxyServer.getConsoleCommandSource(), + String.format("tempmute %s 1h Muted by the community - under review.", votedPlayer.getUsername())); + } + + public void addEligibleVoter(Player player) { + UUID uuid = player.getUniqueId(); + if (votedAgainst.contains(uuid) || votedFor.contains(uuid)) + return; + totalEligibleVoters++; + player.sendMessage(getVoteStartMessage()); + } + + public void removeEligibleVoter(UUID uuid) { + if (votedFor.contains(uuid) || votedAgainst.contains(uuid)) + return; + totalEligibleVoters--; + } + + private Component getVoteStartMessage() { + return Utility.parseMiniMessage( + String.format(""" + [VoteMute] A vote to mute for one hour has been started, please read the logs below before voting. + + Click: Mute --- Don't mute""", + votedPlayer.getUsername(), votedPlayer.getUsername()), + Placeholder.component("prefix", prefix), + Placeholder.parsed("player", votedPlayer.getUsername()), + Placeholder.component("logs", chatLogs)); } } diff --git a/velocity/src/main/java/com/alttd/velocitychat/commands/vote_to_mute/VoteToMuteStarter.java b/velocity/src/main/java/com/alttd/velocitychat/commands/vote_to_mute/VoteToMuteStarter.java index dc2e04d..d20c8af 100644 --- a/velocity/src/main/java/com/alttd/velocitychat/commands/vote_to_mute/VoteToMuteStarter.java +++ b/velocity/src/main/java/com/alttd/velocitychat/commands/vote_to_mute/VoteToMuteStarter.java @@ -24,6 +24,7 @@ public class VoteToMuteStarter { private final Player commandSource; private final String serverName; private List parsedChatLogs; + private final boolean countLowerRanks; public static Optional getInstance(UUID uuid) { if (!instanceMap.containsKey(uuid)) @@ -31,11 +32,12 @@ public class VoteToMuteStarter { return Optional.of(instanceMap.get(uuid)); } - public VoteToMuteStarter(ChatLogHandler chatLogHandler, Player votedPlayer, Player commandSource, String serverName) { + public VoteToMuteStarter(ChatLogHandler chatLogHandler, Player votedPlayer, Player commandSource, String serverName, boolean countLowerRanks) { this.chatLogHandler = chatLogHandler; this.votedPlayer = votedPlayer; this.commandSource = commandSource; this.serverName = serverName; + this.countLowerRanks = countLowerRanks; instanceMap.put(commandSource.getUniqueId(), this); } @@ -50,7 +52,7 @@ public class VoteToMuteStarter { parseChatLogs(chatLogs); commandSource.sendMessage(Utility.parseMiniMessage( " Please select up to 10 messages other players should see to decide their vote, seperated by comma's. " + - "Example: /votetomute messages 1, 2, 5, 8")); + "Example: /votetomutehelper messages 1, 2, 5, 8")); showPage(0); }); } @@ -107,4 +109,20 @@ public class VoteToMuteStarter { instanceMap.remove(commandSource.getUniqueId()); return Component.join(JoinConfiguration.newlines(), selectedChatLogs); } + + public int getTotalPages() { + return (int) Math.ceil((double) parsedChatLogs.size() / 10); + } + + public Player getVotedPlayer() { + return votedPlayer; + } + + public int getTotalLogEntries() { + return parsedChatLogs.size(); + } + + public boolean countLowerRanks() { + return countLowerRanks; + } } diff --git a/velocity/src/main/java/com/alttd/velocitychat/listeners/ProxyPlayerListener.java b/velocity/src/main/java/com/alttd/velocitychat/listeners/ProxyPlayerListener.java index 091270f..f8c2f83 100755 --- a/velocity/src/main/java/com/alttd/velocitychat/listeners/ProxyPlayerListener.java +++ b/velocity/src/main/java/com/alttd/velocitychat/listeners/ProxyPlayerListener.java @@ -6,6 +6,7 @@ import com.alttd.chat.objects.Mail; import com.alttd.chat.util.Utility; import com.alttd.velocitychat.VelocityChat; import com.alttd.chat.config.Config; +import com.alttd.velocitychat.commands.vote_to_mute.ActiveVoteToMute; import com.alttd.velocitychat.data.ServerWrapper; import com.alttd.velocitychat.handlers.ServerHandler; import com.alttd.chat.managers.PartyManager; @@ -17,6 +18,7 @@ import com.velocitypowered.api.event.connection.LoginEvent; import com.velocitypowered.api.event.player.ServerConnectedEvent; import com.velocitypowered.api.event.player.ServerPostConnectEvent; import com.velocitypowered.api.proxy.Player; +import com.velocitypowered.api.proxy.ServerConnection; import com.velocitypowered.api.proxy.server.RegisteredServer; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; @@ -48,9 +50,20 @@ public class ProxyPlayerListener { @Subscribe(order = PostOrder.LAST) public void afterPlayerLogin(ServerPostConnectEvent event) { - if (event.getPreviousServer() != null) - return; Player player = event.getPlayer(); + RegisteredServer previousServer = event.getPreviousServer(); + if (previousServer != null) { + ActiveVoteToMute.removePotentialVoter(player, previousServer); + Optional currentServer = player.getCurrentServer(); + if (currentServer.isEmpty()) + return; + ActiveVoteToMute.addPotentialVoter(player, currentServer.get()); + return; + } + Optional currentServer = player.getCurrentServer(); + if (currentServer.isEmpty()) + return; + ActiveVoteToMute.addPotentialVoter(player, currentServer.get()); ChatUser chatUser = ChatUserManager.getChatUser(player.getUniqueId()); List unReadMail = chatUser.getUnReadMail(); if (unReadMail.isEmpty()) @@ -62,6 +75,7 @@ public class ProxyPlayerListener { @Subscribe public void quitEvent(DisconnectEvent event) { + ActiveVoteToMute.removePotentialVoter(event.getPlayer(), null); UUID uuid = event.getPlayer().getUniqueId(); Party party = PartyManager.getParty(event.getPlayer().getUniqueId()); if (party == null) return;