diff --git a/src/main/java/org/xgqy/survival/Survival.java b/src/main/java/org/xgqy/survival/Survival.java index 6fecaac..7277202 100644 --- a/src/main/java/org/xgqy/survival/Survival.java +++ b/src/main/java/org/xgqy/survival/Survival.java @@ -8,6 +8,7 @@ import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.entity.Player; import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.scheduler.BukkitRunnable; +import org.xgqy.survival.command.CommandTabExecutor; import org.xgqy.survival.command.DqshopCommandExecutor; import org.xgqy.survival.command.FriendCommandExecutor; import org.xgqy.survival.command.HandleCommandExecutor; @@ -17,6 +18,7 @@ import org.xgqy.survival.command.InventoryCommandExecutor; import org.xgqy.survival.command.KillCommandExecutor; import org.xgqy.survival.command.LandCommandExecutor; import org.xgqy.survival.command.LoginCommandExecutor; +import org.xgqy.survival.command.NoticeCommandExecutor; import org.xgqy.survival.command.PartyCommandExecutor; import org.xgqy.survival.command.PcCommandExecutor; import org.xgqy.survival.command.PointCommandExecutor; @@ -41,6 +43,7 @@ import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -274,9 +277,26 @@ public final class Survival extends JavaPlugin { getCommand("selfkill").setExecutor(new KillCommandExecutor(this)); getCommand("land").setExecutor(new LandCommandExecutor(this)); getCommand("qd").setExecutor(new LandCommandExecutor(this)); - getLogger().info("Survival插件已启用,信用分持久化功能加载完成!"); + getCommand("notice").setExecutor(new NoticeCommandExecutor(this)); + CommandTabExecutor tabExecutor = new CommandTabExecutor(); + registerTabCompleter(tabExecutor); } + private void registerTabCompleter(CommandTabExecutor completer) { + // 所有需要补全的命令列表 + List commands = Arrays.asList( + "handle", "tpacc", "friend", "party", "settag", "teleport", + "point", "land", "pvp", "help", "tag", "hub", "selfkill", + "qd", "reg", "login", "msg", "inventory", "notice" + ); + for (String cmd : commands) { + if (getCommand(cmd) != null) { + getCommand(cmd).setTabCompleter(completer); + } else { + getLogger().warning("命令 " + cmd + " 未在 plugin.yml 中配置,无法注册补全器!"); + } + } + } @Override public void onDisable() { // 保存玩家标签数据 diff --git a/src/main/java/org/xgqy/survival/command/CommandTabExecutor.java b/src/main/java/org/xgqy/survival/command/CommandTabExecutor.java new file mode 100644 index 0000000..a917b2b --- /dev/null +++ b/src/main/java/org/xgqy/survival/command/CommandTabExecutor.java @@ -0,0 +1,314 @@ +package org.xgqy.survival.command; + +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 统一命令补全器:整合所有命令的自动补全逻辑 + */ +public class CommandTabExecutor implements TabCompleter { + + // 支持的颜色列表(用于 settag 命令) + private static final Map COLOR_MAP = new HashMap<>(); + + static { + // 初始化颜色映射(与 SetTagCommandExecutor 对应) + COLOR_MAP.put("black", ""); + COLOR_MAP.put("dark_blue", ""); + COLOR_MAP.put("dark_green", ""); + COLOR_MAP.put("dark_aqua", ""); + COLOR_MAP.put("dark_red", ""); + COLOR_MAP.put("dark_purple", ""); + COLOR_MAP.put("gold", ""); + COLOR_MAP.put("gray", ""); + COLOR_MAP.put("dark_gray", ""); + COLOR_MAP.put("blue", ""); + COLOR_MAP.put("green", ""); + COLOR_MAP.put("aqua", ""); + COLOR_MAP.put("red", ""); + COLOR_MAP.put("light_purple", ""); + COLOR_MAP.put("yellow", ""); + COLOR_MAP.put("white", ""); + } + + @Nullable + @Override + public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { + List completions = new ArrayList<>(); + String cmdName = command.getName().toLowerCase(); + + // 根据命令名称分发补全逻辑 + switch (cmdName) { + case "handle": + completions = handleTabComplete(args); + break; + case "tpacc": + completions = tpAccTabComplete(args); + break; + case "friend": + completions = friendTabComplete(sender, args); + break; + case "party": + completions = partyTabComplete(sender, args); + break; + case "settag": + completions = setTagTabComplete(sender, args); + break; + case "teleport": + completions = teleportTabComplete(sender, args); + break; + case "point": + case "land": + completions = pointLandTabComplete(sender, args); + break; + case "pvp": + case "help": + case "tag": + case "hub": + case "selfkill": + case "qd": + case "reg": + case "login": + case "msg": + case "inventory": + case "notice": + completions = simpleCommandTabComplete(sender, cmdName, args); + break; + default: + // 未知命令返回空 + completions.clear(); + break; + } + + return completions; + } + + // ------------------------------ 各命令补全逻辑 ------------------------------ + + /** + * /handle 命令补全(处理举报) + */ + private List handleTabComplete(String[] args) { + List completions = new ArrayList<>(); + // 一级参数:no/point/12h/1d/3d/7d/14d/1mo/3mo/1y/ban + if (args.length == 1) { + List options = Arrays.asList( + "no", "point", "12h", "1d", "3d", "7d", "14d", + "1mo", "3mo", "1y", "ban" + ); + completions = filterMatches(options, args[0]); + } + // 二级参数:ban 后补全天数提示 + else if (args.length == 2 && "ban".equalsIgnoreCase(args[0])) { + completions.add("<天数>"); + } + return completions; + } + + /** + * /tpacc 命令补全(传送申请处理) + */ + private List tpAccTabComplete(String[] args) { + List completions = new ArrayList<>(); + // 一级参数:accept/deny/tome/detome + if (args.length == 1) { + List options = Arrays.asList("accept", "deny", "tome", "detome"); + completions = filterMatches(options, args[0]); + } + return completions; + } + + /** + * /friend 命令补全(好友操作) + */ + private List friendTabComplete(CommandSender sender, String[] args) { + List completions = new ArrayList<>(); + if (!(sender instanceof Player)) return completions; + + // 一级参数:add/allow/deny/remove/list + if (args.length == 1) { + List options = Arrays.asList("add", "allow", "deny", "remove", "list"); + completions = filterMatches(options, args[0]); + } + // 二级参数:操作后补全在线玩家名 + else if (args.length == 2 && Arrays.asList("add", "allow", "deny", "remove").contains(args[0].toLowerCase())) { + completions = getOnlinePlayers(args[1]); + } + return completions; + } + + /** + * /party 命令补全(队伍操作) + */ + private List partyTabComplete(CommandSender sender, String[] args) { + List completions = new ArrayList<>(); + if (!(sender instanceof Player)) return completions; + + // 一级参数:create/quit/join/list/kick/ban/disband/help + if (args.length == 1) { + List options = Arrays.asList( + "create", "quit", "join", "list", "kick", "ban", "disband", "help" + ); + completions = filterMatches(options, args[0]); + } + // 二级参数:join/kick/ban 后补全在线玩家名 + else if (args.length == 2 && Arrays.asList("join", "kick", "ban").contains(args[0].toLowerCase())) { + completions = getOnlinePlayers(args[1]); + } + return completions; + } + + /** + * /settag 命令补全(设置标签) + */ + private List setTagTabComplete(CommandSender sender, String[] args) { + List completions = new ArrayList<>(); + // 权限校验:无权限则不显示补全 + if (!sender.hasPermission("permission.settag")) return completions; + + // 一级参数:在线玩家名 + if (args.length == 1) { + completions = getOnlinePlayers(args[1]); + } + // 二级参数:add/remove/check + else if (args.length == 2) { + List options = Arrays.asList("add", "remove", "check"); + completions = filterMatches(options, args[1]); + } + // 三级参数:add/check/remove 后补全标签内容 + else if (args.length == 3) { + String op = args[1].toLowerCase(); + if (Arrays.asList("add", "check", "remove").contains(op)) { + completions.add("<标签内容>"); + } + } + // 四级参数:add/check 后补全颜色 + else if (args.length == 4) { + String op = args[1].toLowerCase(); + if (Arrays.asList("add", "check").contains(op)) { + completions = filterMatches(new ArrayList<>(COLOR_MAP.keySet()), args[3]); + } + } + // 五级参数:add 后补全天数 + else if (args.length == 5 && "add".equalsIgnoreCase(args[1])) { + completions.add("<天数(999=永久)>"); + } + return completions; + } + + /** + * /teleport 命令补全(传送) + */ + private List teleportTabComplete(CommandSender sender, String[] args) { + List completions = new ArrayList<>(); + if (!(sender instanceof Player)) return completions; + + // 一级参数:在线玩家名 + if (args.length == 1) { + completions = getOnlinePlayers(args[0]); + } + // 二级参数:me + else if (args.length == 2) { + if ("me".startsWith(args[1].toLowerCase())) { + completions.add("me"); + } + } + return completions; + } + + /** + * /point /land 命令补全(信用分/领地操作) + */ + private List pointLandTabComplete(CommandSender sender, String[] args) { + List completions = new ArrayList<>(); + if (!(sender instanceof Player)) return completions; + + // 一级参数:在线玩家名 + if (args.length == 1) { + completions = getOnlinePlayers(args[0]); + } + // 二级参数:set/add/remove + else if (args.length == 2) { + List options = Arrays.asList("set", "add", "remove"); + completions = filterMatches(options, args[1]); + } + // 三级参数:数值提示 + else if (args.length == 3) { + completions.add("<数值>"); + } + return completions; + } + + /** + * 简单命令补全(pvp/help/tag/hub/selfkill/qd/reg/login/msg/inventory/notice) + */ + private List simpleCommandTabComplete(CommandSender sender, String cmdName, String[] args) { + List completions = new ArrayList<>(); + if (!(sender instanceof Player)) return completions; + + switch (cmdName) { + // /reg <密码> <确认密码> + case "reg": + if (args.length == 1) completions.add("<密码>"); + else if (args.length == 2) completions.add("<确认密码>"); + break; + // /login <密码> + case "login": + if (args.length == 1) completions.add("<密码>"); + break; + // /msg <玩家名> <消息> + case "msg": + if (args.length == 1) completions = getOnlinePlayers(args[0]); + else if (args.length == 2) completions.add("<消息内容>"); + break; + // /inventory <玩家名> + case "inventory": + if (args.length == 1) completions = getOnlinePlayers(args[0]); + break; + // /notice <玩家名> + case "notice": + if (args.length == 1) completions = getOnlinePlayers(args[0]); + break; + // 无参数命令(pvp/help/tag/hub/selfkill/qd) + default: + completions.clear(); + break; + } + return completions; + } + + // ------------------------------ 通用工具方法 ------------------------------ + + /** + * 获取在线玩家名列表(支持前缀匹配) + */ + private static List getOnlinePlayers(String partialName) { + List playerNames = new ArrayList<>(); + for (Player player : Bukkit.getOnlinePlayers()) { + playerNames.add(player.getName()); + } + return filterMatches(playerNames, partialName); + } + + /** + * 从候选列表中过滤出匹配输入前缀的选项(不区分大小写) + */ + private static List filterMatches(List candidates, String partial) { + String lowerPartial = partial.toLowerCase(); + return candidates.stream() + .filter(candidate -> candidate.toLowerCase().startsWith(lowerPartial)) + .collect(Collectors.toList()); + } +} \ No newline at end of file diff --git a/src/main/java/org/xgqy/survival/command/HandleCommandExecutor.java b/src/main/java/org/xgqy/survival/command/HandleCommandExecutor.java index 2ec7579..f446a4e 100644 --- a/src/main/java/org/xgqy/survival/command/HandleCommandExecutor.java +++ b/src/main/java/org/xgqy/survival/command/HandleCommandExecutor.java @@ -11,10 +11,11 @@ import org.jetbrains.annotations.NotNull; import org.xgqy.survival.Survival; import java.util.Date; +import java.util.HashSet; public class HandleCommandExecutor implements CommandExecutor { - private Survival plugin; + private final Survival plugin; public HandleCommandExecutor(Survival plugin) { this.plugin = plugin; @@ -22,37 +23,254 @@ public class HandleCommandExecutor implements CommandExecutor { @Override public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { - if (sender instanceof Player) { - if (args.length != 1) { - sender.sendMessage(ChatColor.RED + "无效语法 :\n/handle - 处理玩家\n/handle list - 待处理列表"); - return true; - } - if (args[0].equals("list")) { - sender.sendMessage(ChatColor.YELLOW + "---------------------------------------\n" + ChatColor.GREEN + plugin.banlist.get(sender) + ChatColor.YELLOW + " 原因: ", ChatColor.RED + plugin.banreason.get(plugin.banlist.get(sender)) + ChatColor.YELLOW + "\n---------------------------------------\n"); - return true; - } - if (!Bukkit.getPlayer(args[0]).isOnline() && !args[0].equals("list") && Bukkit.getPlayer(args[0]) != plugin.banlist.get((Player) sender)) { - sender.sendMessage(ChatColor.RED + "玩家不在线"); - return true; - } - - BanList banlis = Bukkit.getBanList(BanList.Type.NAME); - long dur = 24 * 60 * 60 * 1000L; - Date expdat = new Date(System.currentTimeMillis() + dur); - plugin.banreason.put(plugin.banlist.get(sender), null); - plugin.banlist.put((Player) sender, null); - sender.sendMessage(ChatColor.YELLOW + "处理完成\n"); - for (Player repo : plugin.reportlist.get(Bukkit.getPlayer(args[0]))) { - repo.sendMessage(ChatColor.LIGHT_PURPLE + "举报系统" + ChatColor.WHITE + " | " + - ChatColor.GREEN + "\n 您举报的玩家 " + ChatColor.RED + ChatColor.BOLD + - args[0] + ChatColor.GREEN + " 已经被封禁\n" + ChatColor.LIGHT_PURPLE + "举报系统" + ChatColor.WHITE + " | \n" + ChatColor.GREEN + " 感谢您为维护游戏平衡做贡献!"); - } - plugin.reportlist.put(Bukkit.getPlayer(args[0]), null); - banlis.addBan(args[0], ChatColor.AQUA + "\n\n星阁钱语\n" + ChatColor.RED + "您的账号 " + args[0] + " 已被封禁\n原因: 管理员处理作弊行为", expdat, null); - Bukkit.getPlayer(args[0]).kickPlayer(ChatColor.AQUA + "星阁钱语\n" + ChatColor.RED + "你被封禁了" + ChatColor.BOLD + ChatColor.YELLOW + " 1 " + ChatColor.RED + "天\n被封禁的账号: " + ChatColor.RED + ChatColor.BOLD + args[0] + ChatColor.RED + "\n封禁原因:" + "管理员处理"); - } else { + // 1. 校验执行者必须是在线管理员(OP) + if (!(sender instanceof Player admin)) { + sender.sendMessage(ChatColor.RED + "只有游戏内管理员可执行此处理命令"); return true; } + if (!admin.isOp()) { + admin.sendMessage(ChatColor.RED + "权限不足!仅管理员可处理举报"); + return true; + } + + // 2. 校验管理员是否有待处理的举报 + Player target = plugin.banlist.get(admin); + String reportReason = plugin.banreason.get(target); + if (target == null || !target.isOnline()) { + admin.sendMessage(ChatColor.RED + "无待处理的举报玩家,或该玩家已离线"); + return true; + } + + // 3. 校验参数合法性 + if (args.length == 0) { + sendUsage(admin); + return true; + } + + // 4. 分支处理不同命令参数(新增带单位的固定封禁) + switch (args[0]) { + case "no": + handleReject(admin, target); + break; + case "point": + handleDeductPoint(admin, target, reportReason); + break; + case "12h": + case "1d": + case "3d": + case "7d": + case "14d": + case "1mo": + case "3mo": + case "1y": + handleFixedBan(admin, target, reportReason, args[0]); + break; + case "ban": + handleCustomBan(admin, target, reportReason, args); + break; + default: + admin.sendMessage(ChatColor.RED + "无效参数!"); + sendUsage(admin); + break; + } + return true; } + + /** + * 修正:同步新的命令用法提示(新增带单位的固定封禁参数) + */ + private void sendUsage(Player admin) { + admin.sendMessage(ChatColor.LIGHT_PURPLE + "举报处理命令用法:"); + admin.sendMessage(ChatColor.WHITE + "/handle no - 驳回举报"); + admin.sendMessage(ChatColor.WHITE + "/handle point - 扣除玩家信用分"); + admin.sendMessage(ChatColor.WHITE + "/handle 12h/1d/3d/7d/14d - 封禁12小时/1天/3天/7天/14天"); + admin.sendMessage(ChatColor.WHITE + "/handle 1mo/3mo/1y - 封禁1个月/3个月/1年"); + admin.sendMessage(ChatColor.WHITE + "/handle ban <天数> - 自定义封禁天数(按天计算)"); + } + + // 原有方法:驳回举报(无修改) + private void handleReject(Player admin, Player target) { + plugin.banlist.remove(admin); + plugin.banreason.remove(target); + admin.sendMessage(ChatColor.LIGHT_PURPLE + "举报系统" + ChatColor.WHITE + " | " + + ChatColor.GREEN + "已驳回玩家 " + ChatColor.RED + target.getName() + " 的举报"); + notifyReporters(target, ChatColor.LIGHT_PURPLE+"举报系统 "+ChatColor.WHITE+"| "+ + ChatColor.GREEN+"您举报的玩家 "+ChatColor.YELLOW+ChatColor.BOLD+target.getName()+ChatColor.GREEN+" 经查实无违规行为,已驳回请求\n"+ + ChatColor.LIGHT_PURPLE+"举报系统 "+ChatColor.WHITE+"| "+ChatColor.YELLOW+"感谢您为维护游戏环境做出贡献,但请不要滥用举报。"); + } + + // 原有方法:扣除信用分(无修改) + private void handleDeductPoint(Player admin, Player target, String reason) { + plugin.banlist.remove(admin); + plugin.banreason.remove(target); + target.getScoreboard().getObjective("handled").getScore(target).setScore(target.getScoreboard().getObjective(("handled")).getScore(target).getScore()+5); + admin.sendMessage(ChatColor.LIGHT_PURPLE + "举报系统" + ChatColor.WHITE + " | " + + ChatColor.GREEN + "已扣除玩家 " + ChatColor.RED + target.getName() + + ChatColor.GREEN + " 的信用分,举报原因:" + ChatColor.YELLOW + reason); + notifyReporters(target, ChatColor.LIGHT_PURPLE+"举报系统 "+ChatColor.WHITE+"| "+ + ChatColor.GREEN+"您举报的玩家 "+ChatColor.YELLOW+ChatColor.BOLD+target.getName()+ChatColor.GREEN+" 已经被处理\n"+ + ChatColor.LIGHT_PURPLE+"举报系统 "+ChatColor.WHITE+"| "+ChatColor.YELLOW+"感谢您为维护游戏平衡做出贡献。"); + } + + /** + * 修正:处理带单位的固定封禁(12h/1d/1mo等) + */ + private void handleFixedBan(Player admin, Player target, String reason, String timeStr) { + // 调用重载的banPlayer方法(支持带单位参数) + boolean banSuccess = banPlayer(target, reason, timeStr); + if (banSuccess) { + // 清理待处理数据 + plugin.banlist.remove(admin); + plugin.banreason.remove(target); + + // 修正:通知消息显示真实单位(如12小时、1个月) + String timeDisplay = getTimeDisplay(timeStr); + admin.sendMessage(ChatColor.LIGHT_PURPLE + "举报系统" + ChatColor.WHITE + " | " + + ChatColor.GREEN + "已成功封禁玩家 " + ChatColor.RED + target.getName() + + ChatColor.GREEN + " " + timeDisplay + ",举报原因:" + ChatColor.YELLOW + reason); + } else { + admin.sendMessage(ChatColor.RED + "封禁失败!无效的封禁时长格式"); + } + } + + // 原有方法:自定义天数封禁(无修改,仍按天计算) + private void handleCustomBan(Player admin, Player target, String reason, String[] args) { + if (args.length != 2) { + admin.sendMessage(ChatColor.RED + "自定义封禁用法错误!正确格式:/handle ban <天数>"); + return; + } + + int days; + try { + days = Integer.parseInt(args[1]); + if (days <= 0) { + admin.sendMessage(ChatColor.RED + "封禁天数必须为正整数"); + return; + } + } catch (NumberFormatException e) { + admin.sendMessage(ChatColor.RED + "请输入有效的数字作为封禁天数"); + return; + } + + // 调用原有的banPlayer方法(按天数计算) + banPlayer(target, reason, days); + plugin.banlist.remove(admin); + plugin.banreason.remove(target); + + admin.sendMessage(ChatColor.LIGHT_PURPLE + "举报系统" + ChatColor.WHITE + " | " + + ChatColor.GREEN + "已自定义封禁玩家 " + ChatColor.RED + target.getName() + + ChatColor.GREEN + " " + days + " 天,举报原因:" + ChatColor.YELLOW + reason); + } + + /** + * 新增:重载banPlayer方法(支持带单位的时间字符串) + * @param timeStr 带单位的封禁时长(12h/1d/3d/7d/14d/1mo/3mo/1y) + * @return 封禁是否成功 + */ + private boolean banPlayer(Player player, String reason, String timeStr) { + // 1. 解析时间字符串为毫秒数 + long banMillis = parseTimeToMillis(timeStr); + if (banMillis <= 0) { + return false; + } + + // 2. 计算封禁到期时间 + Date expireDate = new Date(System.currentTimeMillis() + banMillis); + + // 3. 构建封禁消息(显示真实单位) + String timeDisplay = getTimeDisplay(timeStr); + String banReason = ChatColor.AQUA + "CloudNest" + ChatColor.DARK_AQUA + "NetWork\n" + + ChatColor.RED + "您的账号 " + player.getName() + " 已被封禁\n" + + ChatColor.RED + "原因: " + reason + "\n" + + ChatColor.RED + "封禁时长: " + timeDisplay; + + String kickMessage = ChatColor.AQUA + "CloudNest" + ChatColor.DARK_AQUA + "NetWork\n" + + ChatColor.RED + "你被封禁了" + ChatColor.BOLD + ChatColor.YELLOW + " " + timeDisplay + " " + + ChatColor.RED + "\n被封禁的账号: " + ChatColor.RED + ChatColor.BOLD + player.getName() + + ChatColor.RED + "\n封禁原因: " + reason; + + // 4. 执行封禁操作 + Bukkit.getBanList(BanList.Type.NAME).addBan(player.getName(), banReason, expireDate, null); + player.kickPlayer(kickMessage); + + // 5. 通知举报者 + notifyReporters(player, ChatColor.LIGHT_PURPLE+"举报系统 "+ChatColor.WHITE+"| "+ + ChatColor.GREEN+"您举报的玩家 "+ChatColor.YELLOW+ChatColor.BOLD+player.getName()+ChatColor.GREEN+" 已经被处理\n"+ + ChatColor.LIGHT_PURPLE+"举报系统 "+ChatColor.WHITE+"| "+ChatColor.YELLOW+"感谢您为维护游戏平衡做出贡献。"); + + // 6. 清理举报缓存 + plugin.reportlist.remove(player); + return true; + } + + /** + * 原有:按天数封禁(保留,用于自定义封禁) + */ + private void banPlayer(Player player, String reason, int days) { + long banDuration = (long) days * 24 * 60 * 60 * 1000L; + Date expireDate = new Date(System.currentTimeMillis() + banDuration); + + String banReason = ChatColor.AQUA + "CloudNest" + ChatColor.DARK_AQUA + "NetWork\n" + + ChatColor.RED + "您的账号 " + player.getName() + " 已被封禁\n" + + ChatColor.RED + "原因: " + reason + "\n" + + ChatColor.RED + "封禁时长: " + days + " 天"; + + String kickMessage = ChatColor.AQUA + "CloudNest" + ChatColor.DARK_AQUA + "NetWork\n" + + ChatColor.RED + "你被封禁了" + ChatColor.BOLD + ChatColor.YELLOW + " " + days + " " + + ChatColor.RED + "天\n被封禁的账号: " + ChatColor.RED + ChatColor.BOLD + player.getName() + + ChatColor.RED + "\n封禁原因: " + reason; + + Bukkit.getBanList(BanList.Type.NAME).addBan(player.getName(), banReason, expireDate, null); + player.kickPlayer(kickMessage); + notifyReporters(player, ChatColor.LIGHT_PURPLE+"举报系统 "+ChatColor.WHITE+"| "+ + ChatColor.GREEN+"您举报的玩家 "+ChatColor.YELLOW+ChatColor.BOLD+player.getName()+ChatColor.GREEN+" 已经被处理\n"+ + ChatColor.LIGHT_PURPLE+"举报系统 "+ChatColor.WHITE+"| "+ChatColor.YELLOW+"感谢您为维护游戏平衡做出贡献。"); + plugin.reportlist.remove(player); + } + + /** + * 工具方法:将带单位的时间字符串转换为毫秒数 + * 对应关系:12h=12小时,1d=1天,1mo=30天,1y=365天 + */ + private long parseTimeToMillis(String timeStr) { + return switch (timeStr.toLowerCase()) { + case "12h" -> 12L * 60 * 60 * 1000; // 12小时 = 12*3600*1000ms + case "1d" -> 1L * 24 * 60 * 60 * 1000; // 1天 + case "3d" -> 3L * 24 * 60 * 60 * 1000; // 3天 + case "7d" -> 7L * 24 * 60 * 60 * 1000; // 7天 + case "14d" -> 14L * 24 * 60 * 60 * 1000; // 14天 + case "1mo" -> 30L * 24 * 60 * 60 * 1000; // 1个月(按30天算) + case "3mo" -> 90L * 24 * 60 * 60 * 1000; // 3个月(90天) + case "1y" -> 365L * 24 * 60 * 60 * 1000; // 1年(365天) + default -> -1; // 无效格式 + }; + } + + /** + * 工具方法:将时间字符串转换为友好显示文本(如12h→12小时,1mo→1个月) + */ + private String getTimeDisplay(String timeStr) { + return switch (timeStr.toLowerCase()) { + case "12h" -> "12小时"; + case "1d" -> "1天"; + case "3d" -> "3天"; + case "7d" -> "7天"; + case "14d" -> "14天"; + case "1mo" -> "1个月"; + case "3mo" -> "3个月"; + case "1y" -> "1年"; + default -> timeStr; // 异常情况直接显示原字符串 + }; + } + + // 原有方法:通知举报者(无修改) + private void notifyReporters(Player target, String message) { + var reporters = plugin.reportlist.getOrDefault(target, new HashSet<>()); + for (Player reporter : reporters) { + if (reporter != null && reporter.isOnline()) { + reporter.sendMessage(message); + } + } + } } \ No newline at end of file diff --git a/src/main/java/org/xgqy/survival/command/NoticeCommandExecutor.java b/src/main/java/org/xgqy/survival/command/NoticeCommandExecutor.java new file mode 100644 index 0000000..3ea8a07 --- /dev/null +++ b/src/main/java/org/xgqy/survival/command/NoticeCommandExecutor.java @@ -0,0 +1,36 @@ +package org.xgqy.survival.command; + +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.xgqy.survival.Survival; + +public class NoticeCommandExecutor implements CommandExecutor { + + private Survival plugin; + + public NoticeCommandExecutor(Survival plugin) { + this.plugin = plugin; + } + + @Override + public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { + Player player = Bukkit.getPlayer(args[0]); + if(!player.isOnline()){ + sender.sendMessage(ChatColor.RED+"玩家不在线"); + return true; + } + if(!player.isOp()){ + sender.sendMessage(ChatColor.RED+"你无权使用该命令"); + return true; + } + player.sendMessage(ChatColor.RED+"警告"+ChatColor.WHITE+" | "+ChatColor.GRAY+"检测到您有疑似作弊行为,请立即停止。"); + player.sendTitle(ChatColor.AQUA+"星阁钱语",ChatColor.RED+"您的行为已上报,正在自动检查。",20,160,20); + //player.playSound(player, Sound.BLOCK_NOTE_BLOCK_XYLOPHONE,1.0f,1.0f); + return true; + } +} \ No newline at end of file diff --git a/src/main/java/org/xgqy/survival/command/PointCommandExecutor.java b/src/main/java/org/xgqy/survival/command/PointCommandExecutor.java index 2296e74..5fd19d7 100644 --- a/src/main/java/org/xgqy/survival/command/PointCommandExecutor.java +++ b/src/main/java/org/xgqy/survival/command/PointCommandExecutor.java @@ -19,7 +19,6 @@ public class PointCommandExecutor implements CommandExecutor { @Override public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { - if (sender instanceof Player) { if(args.length != 4){ sender.sendMessage(ChatColor.RED+"参数不足或参数过多!\n/point "); return true; @@ -40,15 +39,11 @@ public class PointCommandExecutor implements CommandExecutor { }else if(args[1].equals("remove")){ plugin.ppoint.put(player,plugin.ppoint.get(player)-Integer.parseInt(args[2])); plugin.breason.put(player,args[3]); - player.getScoreboard().getObjective("handled").getScore(player).setScore(Integer.parseInt(args[2])); + player.getScoreboard().getObjective("handled").getScore(player).setScore(player.getScoreboard().getObjective(("handled")).getScore(player).getScore()+Integer.parseInt(args[2])); }else{ sender.sendMessage(ChatColor.RED+"参数错误!\n/point "); return true; } return true; - } else { - sender.sendMessage(ChatColor.RED + "无法对非玩家类使用"); - return true; - } } } \ No newline at end of file diff --git a/src/main/java/org/xgqy/survival/command/ReportCommandExecutor.java b/src/main/java/org/xgqy/survival/command/ReportCommandExecutor.java index 985405c..1e34f86 100644 --- a/src/main/java/org/xgqy/survival/command/ReportCommandExecutor.java +++ b/src/main/java/org/xgqy/survival/command/ReportCommandExecutor.java @@ -1,5 +1,8 @@ package org.xgqy.survival.command; +import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.api.chat.ClickEvent; +import net.md_5.bungee.api.chat.ComponentBuilder; import org.bukkit.BanList; import org.bukkit.Bukkit; import org.bukkit.ChatColor; @@ -317,7 +320,18 @@ public class ReportCommandExecutor implements CommandExecutor, Listener { ChatColor.GREEN + "举报原因: " + ChatColor.YELLOW + reason + "\n" + ChatColor.LIGHT_PURPLE + "举报系统" + ChatColor.WHITE + " | " + ChatColor.YELLOW + "请尽快处理该举报"); - + BaseComponent[] message = new ComponentBuilder(ChatColor.YELLOW+"工具栏: ") + .append(new ComponentBuilder(ChatColor.GREEN + "[传送到玩家] ").event(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/tp "+target.getName())).create()) + .append(new ComponentBuilder(ChatColor.GREEN + "[提醒该玩家] \n").event(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/notice "+target.getName())).create()) + .append(ChatColor.YELLOW+"处理栏: ") + .append(new ComponentBuilder(ChatColor.RED + "[驳回]").event(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/handle no")).create()) + .append(new ComponentBuilder(ChatColor.RED + "[扣除信用分]").event(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/handle point")).create()) + .append(new ComponentBuilder(ChatColor.RED + "[封禁1天]").event(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/handle 1")).create()) + .append(new ComponentBuilder(ChatColor.RED + "[封禁3天]").event(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/handle 3")).create()) + .append(new ComponentBuilder(ChatColor.RED + "[封禁7天]").event(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/handle 7")).create()) + .append(new ComponentBuilder(ChatColor.RED + "[自定义封禁]").event(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/handle ban")).create()) + .create(); + admin.spigot().sendMessage(message); // 添加到待处理列表 plugin.banlist.put(admin, target); plugin.banreason.put(target, reason); diff --git a/src/main/java/org/xgqy/survival/event/LoginEvent.java b/src/main/java/org/xgqy/survival/event/LoginEvent.java index 5d8d2b0..a210393 100644 --- a/src/main/java/org/xgqy/survival/event/LoginEvent.java +++ b/src/main/java/org/xgqy/survival/event/LoginEvent.java @@ -47,7 +47,11 @@ public class LoginEvent implements Listener { initialLocations.put(player, initialLoc); // 记录初始位置 // 初始化未登录隔离状态:隐身+高处传送+致盲 setupUnloggedState(player); - + if(!plugin.getAccountManager().isRegistered(player.getUniqueId())){ + if(!plugin.getPlayerTags().getTags(player).contains("小萌新")){ + plugin.getPlayerTags().addTag(player,"小萌新",30); + } + } // 登录提示定时器(每秒发送一次,登录后停止) new BukkitRunnable() { @Override diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index a9f8b7d..73a3bf4 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -83,6 +83,9 @@ commands: selfkill: description: self kill usage: / + notice: + description: notice a player + usage: / permissions: permission.settag: description: Allows setting player tags