diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2826f208..80976fae 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -41,7 +41,7 @@ jobs: - name: Execute Gradle build env: STORAGE_TYPE: ${{ matrix.storageType }} - run: ./gradlew build + run: ./gradlew build --info - name: Publish to Maven Central # only publish once diff --git a/common/src/main/java/me/confuser/banmanager/common/BanManagerPlugin.java b/common/src/main/java/me/confuser/banmanager/common/BanManagerPlugin.java index 86c89439..bf9f64d7 100644 --- a/common/src/main/java/me/confuser/banmanager/common/BanManagerPlugin.java +++ b/common/src/main/java/me/confuser/banmanager/common/BanManagerPlugin.java @@ -409,7 +409,9 @@ public CommonCommand[] getCommands() { new InfoCommand(this), new ImportCommand(this), new KickCommand(this), + new KickAllCommand(this), new LoglessKickCommand(this), + new LoglessKickAllCommand(this), new MuteCommand(this), new MuteIpCommand(this), new NotesCommand(this), diff --git a/common/src/main/java/me/confuser/banmanager/common/commands/KickAllCommand.java b/common/src/main/java/me/confuser/banmanager/common/commands/KickAllCommand.java new file mode 100644 index 00000000..cf05f87b --- /dev/null +++ b/common/src/main/java/me/confuser/banmanager/common/commands/KickAllCommand.java @@ -0,0 +1,101 @@ +package me.confuser.banmanager.common.commands; + +import me.confuser.banmanager.common.BanManagerPlugin; +import me.confuser.banmanager.common.CommonPlayer; +import me.confuser.banmanager.common.data.PlayerData; +import me.confuser.banmanager.common.data.PlayerKickData; +import me.confuser.banmanager.common.util.Message; + +import java.sql.SQLException; + +public class KickAllCommand extends CommonCommand { + + public KickAllCommand(BanManagerPlugin plugin) { + super(plugin, "kickall", true, 0); + } + + @Override + public boolean onCommand(final CommonSender sender, CommandParser parser) { + final boolean isSilent = parser.isSilent(); + + if (parser.isInvalidReason()) { + Message.get("sender.error.invalidReason") + .set("reason", parser.getReason().getMessage()) + .sendTo(sender); + return true; + } + + if (isSilent && !sender.hasPermission(getPermission() + ".silent")) { + sender.sendMessage(Message.getString("sender.error.noPermission")); + return true; + } + + final String reason = parser.args.length > 0 ? parser.getReason().getMessage() : ""; + + getPlugin().getScheduler().runAsync(() -> { + final PlayerData actor = sender.getData(); + + if (actor == null) return; + + CommonPlayer[] onlinePlayers = getPlugin().getServer().getOnlinePlayers(); + + if (getPlugin().getConfig().isKickLoggingEnabled()) { + for (CommonPlayer player : onlinePlayers) { + if (!sender.hasPermission("bm.exempt.override.kick") && player.hasPermission("bm.exempt.kick")) { + continue; + } + + PlayerData playerData = player.getData(); + + if (playerData == null) continue; + + PlayerKickData data = new PlayerKickData(playerData, actor, reason); + + try { + getPlugin().getPlayerKickStorage().addKick(data, isSilent); + } catch (SQLException e) { + sender.sendMessage(Message.get("sender.error.exception").toString()); + e.printStackTrace(); + } + } + } + + + getPlugin().getScheduler().runSync(() -> { + Message message = Message.get(reason.isEmpty() ? "kickall.notify.noReason" : "kickall.notify.reason"); + message.set("actor", actor.getName()).set("reason", reason); + + for (CommonPlayer player : onlinePlayers) { + if (!sender.hasPermission("bm.exempt.override.kick") && player.hasPermission("bm.exempt.kick")) { + continue; + } + + Message kickMessage; + + if (reason.isEmpty()) { + kickMessage = Message.get("kickall.player.noReason"); + } else { + kickMessage = Message.get("kickall.player.reason").set("reason", reason); + } + + kickMessage + .set("displayName", player.getDisplayName()) + .set("player", player.getName()) + .set("playerId", player.getUniqueId().toString()) + .set("actor", actor.getName()); + + player.kick(kickMessage.toString()); + } + + if (isSilent || !sender.hasPermission("bm.notify.kick")) { + message.sendTo(sender); + } + + if (!isSilent) getPlugin().getServer().broadcast(message.toString(), "bm.notify.kick"); + }); + }); + + return true; + } + +} diff --git a/common/src/main/java/me/confuser/banmanager/common/commands/LoglessKickAllCommand.java b/common/src/main/java/me/confuser/banmanager/common/commands/LoglessKickAllCommand.java new file mode 100644 index 00000000..182fd366 --- /dev/null +++ b/common/src/main/java/me/confuser/banmanager/common/commands/LoglessKickAllCommand.java @@ -0,0 +1,74 @@ +package me.confuser.banmanager.common.commands; + +import me.confuser.banmanager.common.BanManagerPlugin; +import me.confuser.banmanager.common.CommonPlayer; +import me.confuser.banmanager.common.data.PlayerData; +import me.confuser.banmanager.common.util.Message; + +public class LoglessKickAllCommand extends CommonCommand { + + public LoglessKickAllCommand(BanManagerPlugin plugin) { + super(plugin, "nlkickall", true, 0); + } + + @Override + public boolean onCommand(final CommonSender sender, CommandParser parser) { + final boolean isSilent = parser.isSilent(); + + if (parser.isInvalidReason()) { + Message.get("sender.error.invalidReason") + .set("reason", parser.getReason().getMessage()) + .sendTo(sender); + return true; + } + + if (isSilent && !sender.hasPermission(getPermission() + ".silent")) { + sender.sendMessage(Message.getString("sender.error.noPermission")); + return true; + } + + final String reason = parser.args.length > 0 ? parser.getReason().getMessage() : ""; + + getPlugin().getScheduler().runAsync(() -> { + final PlayerData actor = sender.getData(); + + if (actor == null) return; + + getPlugin().getScheduler().runSync(() -> { + Message message = Message.get(reason.isEmpty() ? "kickall.notify.noReason" : "kickall.notify.reason"); + message.set("actor", actor.getName()).set("reason", reason); + + for (CommonPlayer player : getPlugin().getServer().getOnlinePlayers()) { + if (!sender.hasPermission("bm.exempt.override.kick") && player.hasPermission("bm.exempt.kick")) { + continue; + } + + Message kickMessage; + + if (reason.isEmpty()) { + kickMessage = Message.get("kickall.player.noReason"); + } else { + kickMessage = Message.get("kickall.player.reason").set("reason", reason); + } + + kickMessage + .set("displayName", player.getDisplayName()) + .set("player", player.getName()) + .set("playerId", player.getUniqueId().toString()) + .set("actor", actor.getName()); + + player.kick(kickMessage.toString()); + } + + if (isSilent || !sender.hasPermission("bm.notify.kick")) { + message.sendTo(sender); + } + + if (!isSilent) getPlugin().getServer().broadcast(message.toString(), "bm.notify.kick"); + }); + }); + + return true; + } + +} diff --git a/common/src/main/resources/messages.yml b/common/src/main/resources/messages.yml index c1396b2b..b42e713f 100644 --- a/common/src/main/resources/messages.yml +++ b/common/src/main/resources/messages.yml @@ -130,6 +130,14 @@ messages: noReason: '&6[player] has been kicked by [actor]' reason: '&6[player] has been kicked by [actor] for &4[reason]' + kickall: + player: + noReason: '&6You have been kicked' + reason: '&6You have been kicked for &4[reason]' + notify: + noReason: 'All players have been kicked by [actor]' + reason: 'All players have been kicked by [actor] for &4[reason]' + ban: player: disallowed: '&6You have been banned from this server for &4[reason]' diff --git a/common/src/main/resources/plugin.yml b/common/src/main/resources/plugin.yml index 2c6c9314..d29fc0df 100644 --- a/common/src/main/resources/plugin.yml +++ b/common/src/main/resources/plugin.yml @@ -2,7 +2,7 @@ main: "${mainPath}" name: BanManager version: "${internalVersion}" author: confuser -website: http://dev.bukkit.org/bukkit-plugins/ban-management/ +website: https://banmanagement.com description: "A database driven punishment system" softdepend: - DiscordSRV @@ -89,6 +89,11 @@ commands: usage: "/kick " aliases: [bmkick] permission: bm.command.kick + kickall: + description: "kick all players on the current server" + usage: "/kickall " + aliases: [bmkickall] + permission: bm.command.kickall mute: description: "mutes a player" usage: "/mute " @@ -139,6 +144,11 @@ commands: usage: "/nlkick " aliases: [bmnlkick] permission: bm.command.nlkick + nlkickall: + description: "kick all players on the current server without logging" + usage: "/nlkickall " + aliases: [bmnlkickall] + permission: bm.command.nlkickall bmreload: description: "Reloads from the config everything except database connection info" usage: "/bmreload" @@ -289,7 +299,9 @@ permissions: bm.command.unbanip: true bm.command.import: true bm.command.kick: true + bm.command.kickall: true bm.command.nlkick: true + bm.command.nlkickall: true bm.command.update: true bm.command.mute: true bm.command.mute.offline: true @@ -360,7 +372,9 @@ permissions: bm.command.unbanip: true bm.command.import: true bm.command.kick: true + bm.command.kickall: true bm.command.nlkick: true + bm.command.nlkickall: true bm.command.update: true bm.command.mute: true bm.command.mute.offline: true @@ -561,6 +575,15 @@ permissions: bm.command.kick: description: Allows a player to kick another player default: op + bm.command.kickall: + description: Allows a player to kick all players on the server + default: op + bm.command.nlkick: + description: Allows a player to kick another player without logging + default: op + bm.command.nlkickall: + description: Allows a player to kick all players on the server without logging + default: op bm.command.update: description: Notifies player of a plugin update default: op @@ -699,12 +722,12 @@ permissions: bm.command.unbanname: description: Allows unbanning a name default: op -bm.command.bmutils: - description: Allows using bmutils base command - default: op -bm.command.bmutils.missingplayers: - description: Allows resolving missing players - default: op -bm.command.bmutils.duplicates: - description: Allows resolving duplicate player names - default: op + bm.command.bmutils: + description: Allows using bmutils base command + default: op + bm.command.bmutils.missingplayers: + description: Allows resolving missing players + default: op + bm.command.bmutils.duplicates: + description: Allows resolving duplicate player names + default: op diff --git a/common/src/test/java/me/confuser/banmanager/common/commands/KickAllCommandTest.java b/common/src/test/java/me/confuser/banmanager/common/commands/KickAllCommandTest.java new file mode 100644 index 00000000..009c408a --- /dev/null +++ b/common/src/test/java/me/confuser/banmanager/common/commands/KickAllCommandTest.java @@ -0,0 +1,115 @@ +package me.confuser.banmanager.common.commands; + +import me.confuser.banmanager.common.BasePluginDbTest; +import me.confuser.banmanager.common.CommonPlayer; +import me.confuser.banmanager.common.data.PlayerData; +import org.junit.Before; +import org.junit.Test; + +import static org.mockito.Mockito.*; + +public class KickAllCommandTest extends BasePluginDbTest { + private KickAllCommand cmd; + + @Before + public void setupCmd() { + for (CommonCommand cmd : plugin.getCommands()) { + if (cmd.getCommandName().equals("kickall")) { + this.cmd = (KickAllCommand) cmd; + break; + } + } + } + + @Test + public void shouldFailIfNoSilentPermission() { + CommonSender sender = spy(plugin.getServer().getConsoleSender()); + String[] args = new String[]{"-s", "test"}; + + when(sender.hasPermission(cmd.getPermission() + ".silent")).thenReturn(false); + + assert (cmd.onCommand(sender, new CommandParser(plugin, args, 0))); + verify(sender).sendMessage("&cYou do not have permission to perform that action"); + } + + @Test + public void shouldFailIfExempt() { + PlayerData player = testUtils.createRandomPlayer(); + CommonSender sender = spy(server.getConsoleSender()); + CommonPlayer commonPlayer = spy(server.getPlayer(player.getName())); + String[] args = new String[]{"test"}; + + when(sender.hasPermission("bm.exempt.override.kick")).thenReturn(false); + when(commonPlayer.hasPermission("bm.exempt.kick")).thenReturn(true); + when(server.getOnlinePlayers()).thenReturn(new CommonPlayer[]{commonPlayer}); + + assert (cmd.onCommand(sender, new CommandParser(plugin, args, 0))); + + verify(commonPlayer, never()).kick("&6You have been kicked"); + verify(server).broadcast("All players have been kicked by Console for &4test", "bm.notify.kick"); + } + + @Test + public void shouldKickPlayerWithoutAReason() { + PlayerData player = testUtils.createRandomPlayer(); + CommonSender sender = spy(server.getConsoleSender()); + CommonPlayer commonPlayer = spy(server.getPlayer(player.getName())); + String[] args = new String[]{}; + + when(commonPlayer.hasPermission("bm.notify.kick")).thenReturn(false); + when(server.getOnlinePlayers()).thenReturn(new CommonPlayer[]{commonPlayer}); + + assert (cmd.onCommand(sender, new CommandParser(plugin, args, 0))); + + verify(commonPlayer).kick("&6You have been kicked"); + verify(server).broadcast("All players have been kicked by Console", "bm.notify.kick"); + } + + @Test + public void shouldKickPlayerWithAReason() { + PlayerData player = testUtils.createRandomPlayer(); + CommonSender sender = spy(server.getConsoleSender()); + CommonPlayer commonPlayer = spy(server.getPlayer(player.getName())); + String[] args = new String[]{"test"}; + + when(commonPlayer.hasPermission("bm.notify.kick")).thenReturn(false); + when(server.getOnlinePlayers()).thenReturn(new CommonPlayer[]{commonPlayer}); + + assert (cmd.onCommand(sender, new CommandParser(plugin, args, 0))); + + verify(commonPlayer).kick("&6You have been kicked for &4" + args[0]); + verify(server).broadcast("All players have been kicked by Console for &4" + args[0], "bm.notify.kick"); + } + + @Test + public void shouldKickPlayerWithoutAReasonSilently() { + PlayerData player = testUtils.createRandomPlayer(); + CommonSender sender = spy(server.getConsoleSender()); + CommonPlayer commonPlayer = spy(server.getPlayer(player.getName())); + String[] args = new String[]{"-s"}; + + when(commonPlayer.hasPermission("bm.notify.kick")).thenReturn(false); + when(server.getOnlinePlayers()).thenReturn(new CommonPlayer[]{commonPlayer}); + + assert (cmd.onCommand(sender, new CommandParser(plugin, args, 0))); + + verify(commonPlayer).kick("&6You have been kicked"); + verify(server, never()).broadcast("All players have been kicked by Console", "bm.notify.kick"); + } + + @Test + public void shouldKickPlayerWithAReasonSilently() { + PlayerData player = testUtils.createRandomPlayer(); + CommonSender sender = spy(server.getConsoleSender()); + CommonPlayer commonPlayer = spy(server.getPlayer(player.getName())); + String[] args = new String[]{"-s", "test", "reason"}; + + when(commonPlayer.hasPermission("bm.notify.kick")).thenReturn(false); + when(server.getOnlinePlayers()).thenReturn(new CommonPlayer[]{commonPlayer}); + + assert (cmd.onCommand(sender, new CommandParser(plugin, args, 0))); + + verify(commonPlayer).kick("&6You have been kicked for &4test reason"); + verify(server, never()).broadcast("All players have been kicked by Console for &4test reason", "bm.notify.kick"); + } +} diff --git a/common/src/test/java/me/confuser/banmanager/common/commands/KickCommandTest.java b/common/src/test/java/me/confuser/banmanager/common/commands/KickCommandTest.java index d99750cf..31c415ec 100644 --- a/common/src/test/java/me/confuser/banmanager/common/commands/KickCommandTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/commands/KickCommandTest.java @@ -2,16 +2,10 @@ import me.confuser.banmanager.common.BasePluginDbTest; import me.confuser.banmanager.common.CommonPlayer; -import me.confuser.banmanager.common.CommonServer; -import me.confuser.banmanager.common.configs.ExemptionsConfig; -import me.confuser.banmanager.common.data.PlayerBanData; import me.confuser.banmanager.common.data.PlayerData; import org.junit.Before; import org.junit.Test; -import java.sql.SQLException; - -import static org.awaitility.Awaitility.await; import static org.junit.Assert.*; import static org.mockito.Mockito.*; diff --git a/common/src/test/java/me/confuser/banmanager/common/commands/LoglessKickAllCommandTest.java b/common/src/test/java/me/confuser/banmanager/common/commands/LoglessKickAllCommandTest.java new file mode 100644 index 00000000..0fc23f90 --- /dev/null +++ b/common/src/test/java/me/confuser/banmanager/common/commands/LoglessKickAllCommandTest.java @@ -0,0 +1,115 @@ +package me.confuser.banmanager.common.commands; + +import me.confuser.banmanager.common.BasePluginDbTest; +import me.confuser.banmanager.common.CommonPlayer; +import me.confuser.banmanager.common.data.PlayerData; +import org.junit.Before; +import org.junit.Test; + +import static org.mockito.Mockito.*; + +public class LoglessKickAllCommandTest extends BasePluginDbTest { + private LoglessKickAllCommand cmd; + + @Before + public void setupCmd() { + for (CommonCommand cmd : plugin.getCommands()) { + if (cmd.getCommandName().equals("nlkickall")) { + this.cmd = (LoglessKickAllCommand) cmd; + break; + } + } + } + + @Test + public void shouldFailIfNoSilentPermission() { + CommonSender sender = spy(plugin.getServer().getConsoleSender()); + String[] args = new String[]{"-s", "test"}; + + when(sender.hasPermission(cmd.getPermission() + ".silent")).thenReturn(false); + + assert (cmd.onCommand(sender, new CommandParser(plugin, args, 0))); + verify(sender).sendMessage("&cYou do not have permission to perform that action"); + } + + @Test + public void shouldFailIfExempt() { + PlayerData player = testUtils.createRandomPlayer(); + CommonSender sender = spy(server.getConsoleSender()); + CommonPlayer commonPlayer = spy(server.getPlayer(player.getName())); + String[] args = new String[]{"test"}; + + when(sender.hasPermission("bm.exempt.override.kick")).thenReturn(false); + when(commonPlayer.hasPermission("bm.exempt.kick")).thenReturn(true); + when(server.getOnlinePlayers()).thenReturn(new CommonPlayer[]{commonPlayer}); + + assert (cmd.onCommand(sender, new CommandParser(plugin, args, 0))); + + verify(commonPlayer, never()).kick("&6You have been kicked"); + verify(server).broadcast("All players have been kicked by Console for &4test", "bm.notify.kick"); + } + + @Test + public void shouldKickPlayerWithoutAReason() { + PlayerData player = testUtils.createRandomPlayer(); + CommonSender sender = spy(server.getConsoleSender()); + CommonPlayer commonPlayer = spy(server.getPlayer(player.getName())); + String[] args = new String[]{}; + + when(commonPlayer.hasPermission("bm.notify.kick")).thenReturn(false); + when(server.getOnlinePlayers()).thenReturn(new CommonPlayer[]{commonPlayer}); + + assert (cmd.onCommand(sender, new CommandParser(plugin, args, 0))); + + verify(commonPlayer).kick("&6You have been kicked"); + verify(server).broadcast("All players have been kicked by Console", "bm.notify.kick"); + } + + @Test + public void shouldKickPlayerWithAReason() { + PlayerData player = testUtils.createRandomPlayer(); + CommonSender sender = spy(server.getConsoleSender()); + CommonPlayer commonPlayer = spy(server.getPlayer(player.getName())); + String[] args = new String[]{"test"}; + + when(commonPlayer.hasPermission("bm.notify.kick")).thenReturn(false); + when(server.getOnlinePlayers()).thenReturn(new CommonPlayer[]{commonPlayer}); + + assert (cmd.onCommand(sender, new CommandParser(plugin, args, 0))); + + verify(commonPlayer).kick("&6You have been kicked for &4" + args[0]); + verify(server).broadcast("All players have been kicked by Console for &4" + args[0], "bm.notify.kick"); + } + + @Test + public void shouldKickPlayerWithoutAReasonSilently() { + PlayerData player = testUtils.createRandomPlayer(); + CommonSender sender = spy(server.getConsoleSender()); + CommonPlayer commonPlayer = spy(server.getPlayer(player.getName())); + String[] args = new String[]{"-s"}; + + when(commonPlayer.hasPermission("bm.notify.kick")).thenReturn(false); + when(server.getOnlinePlayers()).thenReturn(new CommonPlayer[]{commonPlayer}); + + assert (cmd.onCommand(sender, new CommandParser(plugin, args, 0))); + + verify(commonPlayer).kick("&6You have been kicked"); + verify(server, never()).broadcast("All players have been kicked by Console", "bm.notify.kick"); + } + + @Test + public void shouldKickPlayerWithAReasonSilently() { + PlayerData player = testUtils.createRandomPlayer(); + CommonSender sender = spy(server.getConsoleSender()); + CommonPlayer commonPlayer = spy(server.getPlayer(player.getName())); + String[] args = new String[]{"-s", "test", "reason"}; + + when(commonPlayer.hasPermission("bm.notify.kick")).thenReturn(false); + when(server.getOnlinePlayers()).thenReturn(new CommonPlayer[]{commonPlayer}); + + assert (cmd.onCommand(sender, new CommandParser(plugin, args, 0))); + + verify(commonPlayer).kick("&6You have been kicked for &4test reason"); + verify(server, never()).broadcast("All players have been kicked by Console for &4test reason", "bm.notify.kick"); + } +} diff --git a/common/src/test/resources/plugin.yml b/common/src/test/resources/plugin.yml index 77e97082..d29fc0df 100644 --- a/common/src/test/resources/plugin.yml +++ b/common/src/test/resources/plugin.yml @@ -1,10 +1,8 @@ -# This file is the plugin.yml for tests - -main: Unknown +main: "${mainPath}" name: BanManager version: "${internalVersion}" author: confuser -website: http://dev.bukkit.org/bukkit-plugins/ban-management/ +website: https://banmanagement.com description: "A database driven punishment system" softdepend: - DiscordSRV @@ -91,6 +89,11 @@ commands: usage: "/kick " aliases: [bmkick] permission: bm.command.kick + kickall: + description: "kick all players on the current server" + usage: "/kickall " + aliases: [bmkickall] + permission: bm.command.kickall mute: description: "mutes a player" usage: "/mute " @@ -141,6 +144,11 @@ commands: usage: "/nlkick " aliases: [bmnlkick] permission: bm.command.nlkick + nlkickall: + description: "kick all players on the current server without logging" + usage: "/nlkickall " + aliases: [bmnlkickall] + permission: bm.command.nlkickall bmreload: description: "Reloads from the config everything except database connection info" usage: "/bmreload" @@ -291,7 +299,9 @@ permissions: bm.command.unbanip: true bm.command.import: true bm.command.kick: true + bm.command.kickall: true bm.command.nlkick: true + bm.command.nlkickall: true bm.command.update: true bm.command.mute: true bm.command.mute.offline: true @@ -362,7 +372,9 @@ permissions: bm.command.unbanip: true bm.command.import: true bm.command.kick: true + bm.command.kickall: true bm.command.nlkick: true + bm.command.nlkickall: true bm.command.update: true bm.command.mute: true bm.command.mute.offline: true @@ -563,6 +575,15 @@ permissions: bm.command.kick: description: Allows a player to kick another player default: op + bm.command.kickall: + description: Allows a player to kick all players on the server + default: op + bm.command.nlkick: + description: Allows a player to kick another player without logging + default: op + bm.command.nlkickall: + description: Allows a player to kick all players on the server without logging + default: op bm.command.update: description: Notifies player of a plugin update default: op @@ -701,12 +722,12 @@ permissions: bm.command.unbanname: description: Allows unbanning a name default: op -bm.command.bmutils: - description: Allows using bmutils base command - default: op -bm.command.bmutils.missingplayers: - description: Allows resolving missing players - default: op -bm.command.bmutils.duplicates: - description: Allows resolving duplicate player names - default: op + bm.command.bmutils: + description: Allows using bmutils base command + default: op + bm.command.bmutils.missingplayers: + description: Allows resolving missing players + default: op + bm.command.bmutils.duplicates: + description: Allows resolving duplicate player names + default: op