From 8a3a7e22233a3b67c795589c18b36c8e20523fb4 Mon Sep 17 00:00:00 2001 From: Tomasz Kuraczyk Date: Tue, 24 Feb 2026 22:24:39 +0100 Subject: [PATCH] Add working ban command --- .../net/kuraczyk/banhammer/BanHammer.java | 13 +- .../java/net/kuraczyk/banhammer/cmds/Ban.java | 185 ++++++++++++++++++ .../banhammer/events/PlayerPreLogin.java | 20 +- .../net/kuraczyk/banhammer/utils/IPBan.java | 20 ++ .../kuraczyk/banhammer/utils/PlayerBan.java | 25 +++ .../kuraczyk/banhammer/utils/TextUtils.java | 19 ++ src/main/resources/config.yml | 4 +- src/main/resources/plugin.yml | 5 + src/main/resources/translations.yml | 30 +++ 9 files changed, 313 insertions(+), 8 deletions(-) create mode 100644 src/main/java/net/kuraczyk/banhammer/cmds/Ban.java create mode 100644 src/main/java/net/kuraczyk/banhammer/utils/IPBan.java create mode 100644 src/main/java/net/kuraczyk/banhammer/utils/PlayerBan.java create mode 100644 src/main/resources/translations.yml diff --git a/src/main/java/net/kuraczyk/banhammer/BanHammer.java b/src/main/java/net/kuraczyk/banhammer/BanHammer.java index 70087ae..37ced9b 100644 --- a/src/main/java/net/kuraczyk/banhammer/BanHammer.java +++ b/src/main/java/net/kuraczyk/banhammer/BanHammer.java @@ -1,15 +1,21 @@ package net.kuraczyk.banhammer; -import net.kuraczyk.banhammer.events.PlayerPreLogin; import org.bukkit.ChatColor; import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.plugin.java.JavaPlugin; import net.kuraczyk.banhammer.utils.*; +import net.kuraczyk.banhammer.cmds.*; +import net.kuraczyk.banhammer.events.*; +import org.yaml.snakeyaml.Yaml; + +import java.io.File; public final class BanHammer extends JavaPlugin { public FileConfiguration config; + public FileConfiguration translations; public DBManager db; @Override @@ -17,6 +23,9 @@ public final class BanHammer extends JavaPlugin { saveDefaultConfig(); config = getConfig(); + saveResource("translations.yml", false); + translations = YamlConfiguration.loadConfiguration(new File(getDataFolder(), "translations.yml")); + try { Class.forName("net.kuraczyk.libs.postgresql.Driver"); } catch (ClassNotFoundException e) { @@ -41,6 +50,8 @@ public final class BanHammer extends JavaPlugin { getLogger().info(ChatColor.translateAlternateColorCodes('&', "&a&b+&r&8 | &aBan&7Hammer")); + new Ban(this); + getServer().getPluginManager().registerEvents(new PlayerPreLogin(this), this); } diff --git a/src/main/java/net/kuraczyk/banhammer/cmds/Ban.java b/src/main/java/net/kuraczyk/banhammer/cmds/Ban.java new file mode 100644 index 0000000..227ecbe --- /dev/null +++ b/src/main/java/net/kuraczyk/banhammer/cmds/Ban.java @@ -0,0 +1,185 @@ +package net.kuraczyk.banhammer.cmds; + +import io.papermc.paper.configuration.type.fallback.FallbackValue; +import net.kuraczyk.banhammer.BanHammer; +import net.kuraczyk.banhammer.utils.PlayerBan; +import net.kuraczyk.banhammer.utils.TextUtils; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.title.Title; +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.sql.*; +import java.util.Objects; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +public class Ban implements CommandExecutor { + + BanHammer plugin; + + public Ban(BanHammer p){ + plugin = p; + p.getCommand("ban").setExecutor(this); + } + + @Override + public boolean onCommand(CommandSender sender, Command cmd, String lab, String[] args) { + if(sender instanceof Player) return banCommand((Player)sender, args); + else return banCommand(args); + } + + private boolean banCommand(Player p, String[] args) { + if(args.length==0) p.sendMessage(TextUtils.colorString(addPrefix(plugin.translations.getString("command.ban.usage")))); + else if(args.length>=1) { + Player target = Bukkit.getPlayer(args[0]); + UUID targetUuid = Bukkit.getOfflinePlayer(args[0]).getUniqueId(); + Timestamp banEndDate=null; + String scope = "*", reason = plugin.translations.getString("default.reason"), duration="-1"; + if(target!=null) + if(target.isOnline()) targetUuid=target.getUniqueId(); + + if(args.length>=2) scope=args[1]; + if(args.length>=3) { + duration=args[2]; + char type = plugin.config.getString("defaultDurationType").charAt(0); + Integer time=0; + boolean chk = false; + for(int i = 0; i < duration.length(); i++){ + if(duration.charAt(i)==':'){ + chk = true; + time = Integer.parseInt(duration.substring(0, i)); + if(i+1 < duration.length()) type = duration.charAt(i+1); + } + } + if(!chk) time = Integer.parseInt(duration); + switch (type){ + case 'd': + banEndDate = new Timestamp(System.currentTimeMillis()+TimeUnit.DAYS.toMillis(time)); + break; + case 'h': + banEndDate = new Timestamp(System.currentTimeMillis()+TimeUnit.HOURS.toMillis(time)); + break; + case 'm': + banEndDate = new Timestamp(System.currentTimeMillis()+TimeUnit.MINUTES.toMillis(time)); + break; + case 's': + banEndDate = new Timestamp(System.currentTimeMillis()+TimeUnit.SECONDS.toMillis(time)); + break; + default: + p.sendMessage(TextUtils.colorString(plugin.translations.getString("command.ban.durationFormatError"))); + return false; + } + } + + StringBuilder newReason = new StringBuilder(); + for(int i = 3; i < args.length; i++){ + newReason.append(args[i]).append(" "); + } + if(!newReason.isEmpty()) reason=newReason.toString(); + + if(Objects.equals(duration, "-1") || Objects.equals(duration, "perm")) banEndDate = null; + + PlayerBan b = banPlayer(args[0], targetUuid, reason, scope, banEndDate, p.getName(), p.getUniqueId()); + + if(b != null) { + if(banEndDate == null) { + if(target!=null) + if(target.isOnline()) + if(Objects.equals(plugin.config.getString("serverID"), scope) || Objects.equals(scope, "*")) target.kick(TextUtils.colorString(TextUtils.resolvePlaceholders(plugin.translations.getString("banKickMessage.playerBanPerm"), b))); + p.sendMessage(TextUtils.colorString(resolveBanMessagePlaceholders(plugin.translations.getString("command.ban.successfulBanMessagePerm"), args[0], scope, reason, p.getName()))); + Bukkit.broadcast(TextUtils.colorString(resolveBanMessagePlaceholders(plugin.translations.getString("command.ban.banPublicAnnouncementPerm"), args[0], scope, reason, p.getName()))); + } + else { + long timeLeft = banEndDate.getTime() - new Timestamp(System.currentTimeMillis()).getTime(); + long timeLeftDays = TimeUnit.MILLISECONDS.toDays(timeLeft); + timeLeft-=TimeUnit.DAYS.toMillis(timeLeftDays); + long timeLeftHours = TimeUnit.MILLISECONDS.toHours(timeLeft); + timeLeft-=TimeUnit.HOURS.toMillis(timeLeftHours); + long timeLeftMinutes = TimeUnit.MILLISECONDS.toMinutes(timeLeft); + timeLeft-=TimeUnit.MINUTES.toMillis(timeLeftMinutes); + long timeLeftSeconds = TimeUnit.MILLISECONDS.toSeconds(timeLeft); + String parsedBanDuration = TextUtils.parsePolishDate(timeLeftDays, timeLeftHours, timeLeftMinutes, timeLeftSeconds); + + if(target!=null) + if(target.isOnline()) + if(Objects.equals(plugin.config.getString("serverID"), scope) || Objects.equals(scope, "*")) target.kick(TextUtils.colorString(TextUtils.resolvePlaceholders(plugin.translations.getString("banKickMessage.playerBanTemp"), b))); + p.sendMessage(TextUtils.colorString(resolveBanMessagePlaceholders(plugin.translations.getString("command.ban.successfulBanMessageTemp"), args[0], scope, reason, p.getName(), parsedBanDuration))); + Bukkit.broadcast(TextUtils.colorString(resolveBanMessagePlaceholders(plugin.translations.getString("command.ban.banPublicAnnouncementTemp"), args[0], scope, reason, p.getName(), parsedBanDuration))); + + } + return true; + } + else { + plugin.getLogger().severe("BanHammer: Something went wrong while user used ban command... Maybe DB or Technician has skill issue???"); + p.sendMessage(TextUtils.colorString(plugin.translations.getString("command.ban.unexpectedError"))); + return false; + } + + } + return false; + } + private boolean banCommand(String[] args) { + if(args.length==0) plugin.getServer().getConsoleSender().sendMessage(TextUtils.colorString(addPrefix(plugin.translations.getString("command.ban.usage")))); + return false; + } + + private String addPrefix(String s) { + return (String)(plugin.translations.getString("prefix")+s); + } + + private PlayerBan banPlayer(String playerName, UUID playerUuid, String reason, String scope, Timestamp endDate, String op, UUID opUuid) { + try { + Connection conn = plugin.db.getConnection(); + PreparedStatement ps = conn.prepareStatement("INSERT INTO player_bans (player_name, player_uuid, reason, scope, operator_name, operator_uuid, end_time) VALUES (?, ?, ?, ?, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS); + ps.setString(1, playerName); + ps.setObject(2, playerUuid); + ps.setString(3, reason); + ps.setString(4, scope); + ps.setString(5, op); + ps.setObject(6, opUuid); + ps.setObject(7, endDate); + if(ps.executeUpdate() == 0) return null; + + ResultSet rs = ps.getGeneratedKeys(); + rs.next(); + + Timestamp ts = (Timestamp)rs.getObject("end_time"); + String parsedDate = null; + + if(ts!=null) { + long timeLeft = (ts).getTime() - new Timestamp(System.currentTimeMillis()).getTime(); + long timeLeftDays = TimeUnit.MILLISECONDS.toDays(timeLeft); + timeLeft-=TimeUnit.DAYS.toMillis(timeLeftDays); + long timeLeftHours = TimeUnit.MILLISECONDS.toHours(timeLeft); + timeLeft-=TimeUnit.HOURS.toMillis(timeLeftHours); + long timeLeftMinutes = TimeUnit.MILLISECONDS.toMinutes(timeLeft); + timeLeft-=TimeUnit.MINUTES.toMillis(timeLeftMinutes); + long timeLeftSeconds = TimeUnit.MILLISECONDS.toSeconds(timeLeft); + parsedDate = TextUtils.parsePolishDate(timeLeftDays, timeLeftHours, timeLeftMinutes, timeLeftSeconds); + } + + PlayerBan b = new PlayerBan(playerName, playerUuid, reason, parsedDate, op, rs.getObject("id", UUID.class)); + return b; + } + catch (SQLException e) { + plugin.getLogger().severe(e.toString()); + return null; + } + } + + private String resolveBanMessagePlaceholders(String s, String player, String scope, String reason, String operator) { + return s.replace("{player}", player) + .replace("{scope}", scope) + .replace("{reason}", reason) + .replace("{operator}", operator) + .replace("{prefix}", plugin.translations.getString("prefix")); + } + private String resolveBanMessagePlaceholders(String s, String player, String scope, String reason, String operator, String duration) { + return resolveBanMessagePlaceholders(s, player, scope, reason, operator).replace("{duration}", duration); + } + +} diff --git a/src/main/java/net/kuraczyk/banhammer/events/PlayerPreLogin.java b/src/main/java/net/kuraczyk/banhammer/events/PlayerPreLogin.java index c4e5689..943f641 100644 --- a/src/main/java/net/kuraczyk/banhammer/events/PlayerPreLogin.java +++ b/src/main/java/net/kuraczyk/banhammer/events/PlayerPreLogin.java @@ -1,6 +1,8 @@ package net.kuraczyk.banhammer.events; import net.kuraczyk.banhammer.BanHammer; +import net.kuraczyk.banhammer.utils.IPBan; +import net.kuraczyk.banhammer.utils.PlayerBan; import net.kyori.adventure.text.Component; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; @@ -42,6 +44,8 @@ public class PlayerPreLogin implements Listener { while(rs.next()){ String reason = rs.getString("reason"); + String operator = rs.getString("operator_name"); + UUID banId = rs.getObject("id", UUID.class); String parsedDate; if(rs.getObject("end_time")!=null){ @@ -61,15 +65,16 @@ public class PlayerPreLogin implements Listener { long timeLeftMinutes = TimeUnit.MILLISECONDS.toMinutes(timeLeft); timeLeft-=TimeUnit.MINUTES.toMillis(timeLeftMinutes); long timeLeftSeconds = TimeUnit.MILLISECONDS.toSeconds(timeLeft); - timeLeft-=TimeUnit.SECONDS.toMillis(timeLeftSeconds); parsedDate = TextUtils.parsePolishDate(timeLeftDays, timeLeftHours, timeLeftMinutes, timeLeftSeconds); - kickMessage = TextUtils.colorString(TextUtils.resolvePlaceholders(String.format("Zostałeś zbanowany na %s.\nPowód: %s", parsedDate, reason))); + PlayerBan b = new PlayerBan(nickname, uuid, reason, parsedDate, operator, banId); + kickMessage = TextUtils.colorString(TextUtils.resolvePlaceholders(plugin.translations.getString("banKickMessage.playerBanTemp"), b)); return true; } else { - kickMessage = TextUtils.colorString(TextUtils.resolvePlaceholders(String.format("Zostałeś permanentnie zbanowany.\nPowód: %s", reason))); + PlayerBan b = new PlayerBan(nickname, uuid, reason, null, operator, banId); + kickMessage = TextUtils.colorString(TextUtils.resolvePlaceholders(plugin.translations.getString("banKickMessage.playerBanPerm"), b)); return true; } @@ -81,6 +86,8 @@ public class PlayerPreLogin implements Listener { ResultSet rs2 = ps2.executeQuery(); while(rs2.next()) { String reason = rs2.getString("reason"); + String operator = rs2.getString("operator_name"); + UUID banId = rs2.getObject("id", UUID.class); String parsedDate; if(rs2.getObject("end_time")!=null){ @@ -100,15 +107,16 @@ public class PlayerPreLogin implements Listener { long timeLeftMinutes = TimeUnit.MILLISECONDS.toMinutes(timeLeft); timeLeft-=TimeUnit.MINUTES.toMillis(timeLeftMinutes); long timeLeftSeconds = TimeUnit.MILLISECONDS.toSeconds(timeLeft); - timeLeft-=TimeUnit.SECONDS.toMillis(timeLeftSeconds); parsedDate = TextUtils.parsePolishDate(timeLeftDays, timeLeftHours, timeLeftMinutes, timeLeftSeconds); - kickMessage = TextUtils.colorString(TextUtils.resolvePlaceholders(String.format("Zostałeś IPzbanowany na %s.\nPowód: %s", parsedDate, reason))); + IPBan b = new IPBan(ip, reason, parsedDate, operator, banId); + kickMessage = TextUtils.colorString(TextUtils.resolvePlaceholders(plugin.translations.getString("banKickMessage.ipBanTemp"), b)); return true; } else { - kickMessage = TextUtils.colorString(TextUtils.resolvePlaceholders(String.format("Zostałeś permanentnie IPzbanowany.\nPowód: %s", reason))); + IPBan b = new IPBan(ip, reason, null, operator, banId); + kickMessage = TextUtils.colorString(TextUtils.resolvePlaceholders(plugin.translations.getString("banKickMessage.ipBanPerm"), b)); return true; } diff --git a/src/main/java/net/kuraczyk/banhammer/utils/IPBan.java b/src/main/java/net/kuraczyk/banhammer/utils/IPBan.java new file mode 100644 index 0000000..adb6d68 --- /dev/null +++ b/src/main/java/net/kuraczyk/banhammer/utils/IPBan.java @@ -0,0 +1,20 @@ +package net.kuraczyk.banhammer.utils; + +import java.net.InetAddress; +import java.util.UUID; + +public class IPBan { + InetAddress ip; + String reason; + String timeLeft; + String operator; + UUID banId; + + public IPBan(InetAddress ip, String reason, String timeLeft, String operator, UUID banId){ + this.ip = ip; + this.reason = reason; + this.timeLeft = timeLeft; + this.operator = operator; + this.banId = banId; + } +} diff --git a/src/main/java/net/kuraczyk/banhammer/utils/PlayerBan.java b/src/main/java/net/kuraczyk/banhammer/utils/PlayerBan.java new file mode 100644 index 0000000..7172850 --- /dev/null +++ b/src/main/java/net/kuraczyk/banhammer/utils/PlayerBan.java @@ -0,0 +1,25 @@ +package net.kuraczyk.banhammer.utils; + +import org.bukkit.entity.Player; + +import java.sql.Timestamp; +import java.util.UUID; + +public class PlayerBan { + String playerName; + UUID uuid; + String reason; + String timeLeft; + String operator; + UUID banId; + + public PlayerBan(String playerName, UUID uuid, String reason, String timeLeft, String operator, UUID banId) { + this.playerName = playerName; + this.uuid = uuid; + this.reason = reason; + this.timeLeft = timeLeft; + this.operator = operator; + this.banId = banId; + } + +} \ No newline at end of file diff --git a/src/main/java/net/kuraczyk/banhammer/utils/TextUtils.java b/src/main/java/net/kuraczyk/banhammer/utils/TextUtils.java index 925b261..47ba286 100644 --- a/src/main/java/net/kuraczyk/banhammer/utils/TextUtils.java +++ b/src/main/java/net/kuraczyk/banhammer/utils/TextUtils.java @@ -34,6 +34,25 @@ public class TextUtils { .replace("{playerName}", p.getName()) .replace("{ping}", String.valueOf(p.getPing())); } + public static String resolvePlaceholders(String s, PlayerBan b){ + String res = resolvePlaceholders(s) + .replace("{playerName}", b.playerName) + .replace("{uuid}", b.uuid.toString()) + .replace("{reason}", b.reason) + .replace("{operator}", b.operator) //implement new + .replace("{banId}", b.banId.toString()); + if(b.timeLeft!=null) res=res.replace("{timeLeft}", b.timeLeft); + return res; + } + public static String resolvePlaceholders(String s, IPBan b){ + String res = resolvePlaceholders(s) + .replace("{ipAddr}", b.ip.getHostAddress()) + .replace("{reason}", b.reason) + .replace("{operator}", b.operator) //implement new + .replace("{banId}", b.banId.toString()); + if(b.timeLeft!=null) res=res.replace("{timeLeft}", b.timeLeft); + return res; + } public static String parsePolishDate(long days, long hours, long minutes, long seconds){ String parsedDays, parsedHours, parsedMinutes, parsedSeconds; diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 9a603c8..77683f4 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -5,4 +5,6 @@ database: pass: "postgres" database: "postgres" -serverID: "mcserver" \ No newline at end of file +serverID: "mcserver" + +defaultDurationType: 'h' diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 3264ae0..e02151b 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -2,3 +2,8 @@ name: BanHammer version: '0.1.0-SNAPSHOT' main: net.kuraczyk.banhammer.BanHammer api-version: '1.21' + +commands: + ban: + description: "Ban a player" + permission: banhammer.ban diff --git a/src/main/resources/translations.yml b/src/main/resources/translations.yml new file mode 100644 index 0000000..1e50fea --- /dev/null +++ b/src/main/resources/translations.yml @@ -0,0 +1,30 @@ +prefix: 'BanHammer <#808080>- ' + +default: + reason: 'No reason provided.' + +banKickMessage: + playerBanPerm: |- + <#FF5744>You are banned from this server! + Reason: <#C03221>{reason} + playerBanTemp: |- + <#FF5744>You are banned from this server! + <#FF5744>Time left: <#C03221>{timeLeft} + <#FF5744>Reason: <#C03221>{reason} + ipBanPerm: |- + <#FF5744>You are IP-banned from this server! + Reason: <#C03221>{reason} + ipBanTemp: |- + <#FF5744>You are IP-banned from this server! + <#FF5744>Time left: <#C03221>{timeLeft} + <#FF5744>Reason: <#C03221>{reason} + +command: + ban: + usage: '<#ffffff>Usage: <#FF5744>/ban ' + durationFormatError: 'Error: bad duration syntax. Available syntax: ":d", ":h", ":m", ":s"' + unexpectedError: 'Error: something went terribly wrong. Please contact someone who knows what he is doing.' + successfulBanMessagePerm: 'Successfully banned {player} in scope {scope} for reason {reason}' + successfulBanMessageTemp: 'Successfully banned {player} for {duration} in scope {scope} for reason {reason}' + banPublicAnnouncementPerm: 'Player {player} was banned by operator {operator} in scope {scope} for reason {reason}' + banPublicAnnouncementTemp: 'Player {player} was banned by operator {operator} for {duration} in scope {scope} for reason {reason}' \ No newline at end of file