WKEA 发表于 2025-5-8 13:42:48

[原创|Scriptirc] MuteManager 禁言管理器

本帖最后由 WKEA 于 2025-5-8 14:01 编辑

获取脚本:
- 将下方代码保存为MuteManager.java文件
package com.example.muteplugin;

import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabCompleter;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.configuration.file.FileConfiguration;

import java.io.File;
import java.io.IOException;
import java.util.*;

/**
* @pluginName MuteManager
* @Author Scriptirc
* @version 1.0
* @description 支持禁言、解禁、查看禁言列表,自动解禁并提醒,数据持久化,权限分明,指令补全
* mute|禁言玩家
* unmute|解禁玩家
* mutelist|查看禁言列表
* mute.admin|管理员禁言、解禁、查看所有禁言记录
* mute.self|玩家查看自己的禁言记录
*/
public class MuteManager extends JavaPlugin implements TabCompleter {
    // 存储禁言数据的Map,key为玩家UUID,value为禁言信息
    private Map<UUID, MuteInfo> muteMap = new HashMap<>();
    private File muteFile;
    private FileConfiguration muteConfig;

    @Override
    public void onEnable() {
      // 初始化配置文件
      loadMuteData();
      // 注册命令和补全
      getCommand("mute").setTabCompleter(this);
      getCommand("unmute").setTabCompleter(this);
      getCommand("mutelist").setTabCompleter(this);
      // 启动自动解禁检测任务
      startAutoUnmuteTask();
      getLogger().info("MuteManager 插件已启用");
    }

    @Override
    public void onDisable() {
      saveMuteData();
      getLogger().info("MuteManager 插件已禁用");
    }

    // 指令处理
    @Override
    public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
      if (command.getName().equalsIgnoreCase("mute")) {
            return handleMute(sender, args);
      } else if (command.getName().equalsIgnoreCase("unmute")) {
            return handleUnmute(sender, args);
      } else if (command.getName().equalsIgnoreCase("mutelist")) {
            return handleMuteList(sender, args);
      }
      return false;
    }

    // 禁言命令实现
    private boolean handleMute(CommandSender sender, String[] args) {
      if (!sender.hasPermission("mute.admin")) {
            sender.sendMessage(ChatColor.RED + "你没有权限使用此命令。");
            return true;
      }
      if (args.length < 2) {
            sender.sendMessage(ChatColor.YELLOW + "/mute <玩家> <时长> <原因>");
            return true;
      }
      Player target = Bukkit.getPlayer(args);
      if (target == null) {
            sender.sendMessage(ChatColor.RED + "玩家不存在或不在线。");
            return true;
      }
      long duration = parseDuration(args);
      if (duration <= 0) {
            sender.sendMessage(ChatColor.RED + "时长格式错误,示例: 10m 1h 1d");
            return true;
      }
      String reason = args.length >= 3 ? String.join(" ", Arrays.copyOfRange(args, 2, args.length)) : "无";
      UUID uuid = target.getUniqueId();
      long endTime = System.currentTimeMillis() + duration;
      MuteInfo info = new MuteInfo(target.getName(), endTime, reason, sender.getName());
      muteMap.put(uuid, info);
      saveMuteData();
      sender.sendMessage(ChatColor.GREEN + "已禁言 " + target.getName() + ",时长: " + args + ",原因: " + reason);
      target.sendMessage(ChatColor.RED + "你已被禁言,时长: " + args + ",原因: " + reason);
      return true;
    }

    // 解禁命令实现
    private boolean handleUnmute(CommandSender sender, String[] args) {
      if (!sender.hasPermission("mute.admin")) {
            sender.sendMessage(ChatColor.RED + "你没有权限使用此命令。");
            return true;
      }
      if (args.length < 1) {
            sender.sendMessage(ChatColor.YELLOW + "/unmute <玩家>");
            return true;
      }
      OfflinePlayer target = Bukkit.getOfflinePlayer(args);
      if (target == null || target.getName() == null) {
            sender.sendMessage(ChatColor.RED + "玩家不存在。");
            return true;
      }
      UUID uuid = target.getUniqueId();
      if (muteMap.containsKey(uuid)) {
            muteMap.remove(uuid);
            saveMuteData();
            sender.sendMessage(ChatColor.GREEN + "已解除 " + target.getName() + " 的禁言。");
            Player online = Bukkit.getPlayer(uuid);
            if (online != null) {
                online.sendMessage(ChatColor.GREEN + "你的禁言已被解除。");
            }
      } else {
            sender.sendMessage(ChatColor.YELLOW + "该玩家未被禁言。");
      }
      return true;
    }

    // 查看禁言列表命令实现
    private boolean handleMuteList(CommandSender sender, String[] args) {
      if (sender.hasPermission("mute.admin")) {
            // 管理员可查所有人或指定玩家
            if (args.length == 0) {
                sender.sendMessage(ChatColor.AQUA + "当前禁言列表:");
                for (MuteInfo info : muteMap.values()) {
                  sender.sendMessage(ChatColor.YELLOW + info.playerName + " | 剩余: " + formatRemain(info.endTime) + " | 原因: " + info.reason);
                }
            } else {
                OfflinePlayer target = Bukkit.getOfflinePlayer(args);
                if (target == null || target.getName() == null) {
                  sender.sendMessage(ChatColor.RED + "玩家不存在。");
                  return true;
                }
                UUID uuid = target.getUniqueId();
                if (muteMap.containsKey(uuid)) {
                  MuteInfo info = muteMap.get(uuid);
                  sender.sendMessage(ChatColor.YELLOW + info.playerName + " | 剩余: " + formatRemain(info.endTime) + " | 原因: " + info.reason);
                } else {
                  sender.sendMessage(ChatColor.YELLOW + "该玩家未被禁言。");
                }
            }
      } else if (sender instanceof Player && sender.hasPermission("mute.self")) {
            Player player = (Player) sender;
            UUID uuid = player.getUniqueId();
            if (muteMap.containsKey(uuid)) {
                MuteInfo info = muteMap.get(uuid);
                sender.sendMessage(ChatColor.YELLOW + "你当前被禁言 | 剩余: " + formatRemain(info.endTime) + " | 原因: " + info.reason);
            } else {
                sender.sendMessage(ChatColor.GREEN + "你当前未被禁言。");
            }
      } else {
            sender.sendMessage(ChatColor.RED + "你没有权限查看禁言记录。");
      }
      return true;
    }

    // Tab补全实现
    @Override
    public List<String> onTabComplete(CommandSender sender, Command cmd, String label, String[] args) {
      if (cmd.getName().equalsIgnoreCase("mute")) {
            if (args.length == 1) {
                List<String> names = new ArrayList<>();
                for (Player p : Bukkit.getOnlinePlayers()) {
                  names.add(p.getName());
                }
                return names;
            } else if (args.length == 2) {
                return Arrays.asList("10m", "1h", "1d");
            }
      } else if (cmd.getName().equalsIgnoreCase("unmute")) {
            if (args.length == 1) {
                List<String> names = new ArrayList<>();
                for (UUID uuid : muteMap.keySet()) {
                  names.add(muteMap.get(uuid).playerName);
                }
                return names;
            }
      } else if (cmd.getName().equalsIgnoreCase("mutelist")) {
            if (args.length == 1 && sender.hasPermission("mute.admin")) {
                List<String> names = new ArrayList<>();
                for (MuteInfo info : muteMap.values()) {
                  names.add(info.playerName);
                }
                return names;
            }
      }
      return null;
    }

    // 聊天事件监听(禁止被禁言者发言)
    @org.bukkit.event.EventHandler
    public void onPlayerChat(org.bukkit.event.player.AsyncPlayerChatEvent event) {
      Player player = event.getPlayer();
      UUID uuid = player.getUniqueId();
      if (muteMap.containsKey(uuid)) {
            MuteInfo info = muteMap.get(uuid);
            if (System.currentTimeMillis() < info.endTime) {
                player.sendMessage(ChatColor.RED + "你已被禁言,剩余: " + formatRemain(info.endTime) + ",原因: " + info.reason);
                event.setCancelled(true);
            } else {
                // 到期自动解禁
                muteMap.remove(uuid);
                saveMuteData();
                player.sendMessage(ChatColor.GREEN + "你的禁言已自动解除。");
            }
      }
    }

    // 自动解禁定时任务
    private void startAutoUnmuteTask() {
      new BukkitRunnable() {
            @Override
            public void run() {
                long now = System.currentTimeMillis();
                Iterator<Map.Entry<UUID, MuteInfo>> it = muteMap.entrySet().iterator();
                while (it.hasNext()) {
                  Map.Entry<UUID, MuteInfo> entry = it.next();
                  if (now >= entry.getValue().endTime) {
                        Player p = Bukkit.getPlayer(entry.getKey());
                        if (p != null && p.isOnline()) {
                            p.sendMessage(ChatColor.GREEN + "你的禁言已自动解除。");
                        }
                        it.remove();
                  }
                }
                saveMuteData();
            }
      }.runTaskTimer(this, 20L, 120L); // 每6秒检测一次
    }

    // 持久化禁言数据到配置文件
    private void saveMuteData() {
      if (muteConfig == null || muteFile == null) return;
      muteConfig.set("mutes", null);
      for (Map.Entry<UUID, MuteInfo> entry : muteMap.entrySet()) {
            String path = "mutes." + entry.getKey().toString();
            MuteInfo info = entry.getValue();
            muteConfig.set(path + ".name", info.playerName);
            muteConfig.set(path + ".endTime", info.endTime);
            muteConfig.set(path + ".reason", info.reason);
            muteConfig.set(path + ".by", info.by);
      }
      try {
            muteConfig.save(muteFile);
      } catch (IOException e) {
            e.printStackTrace();
      }
    }

    // 加载禁言数据
    private void loadMuteData() {
      muteFile = new File(getDataFolder(), "mutes.yml");
      if (!muteFile.exists()) {
            muteFile.getParentFile().mkdirs();
            try { muteFile.createNewFile(); } catch (IOException ignored) {}
      }
      muteConfig = YamlConfiguration.loadConfiguration(muteFile);
      muteMap.clear();
      if (muteConfig.contains("mutes")) {
            for (String key : muteConfig.getConfigurationSection("mutes").getKeys(false)) {
                String name = muteConfig.getString("mutes." + key + ".name");
                long endTime = muteConfig.getLong("mutes." + key + ".endTime");
                String reason = muteConfig.getString("mutes." + key + ".reason");
                String by = muteConfig.getString("mutes." + key + ".by");
                muteMap.put(UUID.fromString(key), new MuteInfo(name, endTime, reason, by));
            }
      }
    }

    // 工具方法:解析时长字符串(如10m/1h/1d)为毫秒
    private long parseDuration(String input) {
      try {
            if (input.endsWith("m")) {
                return Integer.parseInt(input.replace("m", "")) * 60 * 1000L;
            } else if (input.endsWith("h")) {
                return Integer.parseInt(input.replace("h", "")) * 60 * 60 * 1000L;
            } else if (input.endsWith("d")) {
                return Integer.parseInt(input.replace("d", "")) * 24 * 60 * 60 * 1000L;
            }
      } catch (Exception ignored) {}
      return -1;
    }

    // 工具方法:格式化剩余时间
    private String formatRemain(long endTime) {
      long remain = endTime - System.currentTimeMillis();
      if (remain <= 0) return "已到期";
      long sec = remain / 1000;
      long min = sec / 60;
      long hour = min / 60;
      long day = hour / 24;
      if (day > 0) return day + "天";
      if (hour > 0) return hour + "小时";
      if (min > 0) return min + "分钟";
      return sec + "秒";
    }

    // 禁言信息结构体
    private static class MuteInfo {
      public String playerName;
      public long endTime;
      public String reason;
      public String by;
      public MuteInfo(String playerName, long endTime, String reason, String by) {
            this.playerName = playerName;
            this.endTime = endTime;
            this.reason = reason;
            this.by = by;
      }
    }
}



脚本功能说明
[*]支持三大指令:

[*]/mute <玩家> <时长> <原因>:管理员禁言玩家,时长如10m/1h/1d,原因可选。
[*]/unmute <玩家>:管理员解禁。
[*]/mutelist [玩家]:管理员可查所有人,玩家仅查自己。
[*]禁言信息持久化到 Data/plugins/MuteManager/mutes.yml。
[*]到期自动解禁,在线玩家会收到解禁通知。
[*]禁言期间玩家无法发言,会收到剩余时间和原因提示。
[*]指令补全:玩家名、常用时长、禁言名单等一键补全。
[*]权限分明:

[*]mute.admin:禁言、解禁、全服查询
[*]mute.self:玩家仅查自己

使用方法
   (0. 在此之前你需要在服务器中安装 Scriptirc )

[*]将脚本源码保存到 plugins/Scriptirc/script_src/MuteManager.java
[*]进入服务器,执行 /si compiler MuteManager.java 进行编译
[*]编译成功后,执行 /si load MuteManager 加载插件
注意事项
[*]请务必在正式服上线前充分测试所有功能!
[*]禁言数据保存在 mutes.yml,请定期备份,防止数据丢失。
[*]如需自定义权限、指令等,可参考源码注释进行修改。
[*]插件未集成经济等外部依赖,纯净高效。


WisW 发表于 2025-5-8 19:31:37

不建议没有经验的人或者ai直接发布生产级别的内容:
1. 莫名其妙的MuteInfo类
2. 保存/加载config里面忽略了所有错误
3. 没有必要的定时任务


我没有插件开发经验...可能缺漏/不准确

WKEA 发表于 2025-5-8 19:43:21

WisW 发表于 2025-5-8 19:31
不建议没有经验的人或者ai直接发布生产级别的内容:
1. 莫名其妙的MuteInfo类
2. 保存/加载config里面忽略了 ...

感谢提建议 脚本确实测试可用 我也在学习开发 本质上确实不够完善 ,后期如果有人提出 报错 或者bug 我会持续修复它

WisW 发表于 2025-5-8 21:27:47

WKEA 发表于 2025-5-8 19:43
感谢提建议 脚本确实测试可用 我也在学习开发 本质上确实不够完善 ,后期如果有人提出 报错 或者bug 我会 ...

这不是报错不报错, 能不能跑的问题, 至少是ai目前没有这个能力来应对这类问题
就第一行package com.example.muteplugin; 这就很离谱
而且没有人能保证ai是不是会写出一些很离谱的高危漏洞, 这个概率似乎还比较高, 而且我个人认为这个项目是直接对接生产环境的

WKEA 发表于 2025-5-9 01:54:28

WisW 发表于 2025-5-8 21:27
这不是报错不报错, 能不能跑的问题, 至少是ai目前没有这个能力来应对这类问题
就第一行package com.examp ...

大佬 确实你说的对 目前ai确实会犯低级错误 以至于可能会导致高危的BUG 我尽可能的也在调整这些
就像你一开始对代码的评价 一眼就看出问题,scriptirc脚本和sk等其他脚本都是将所有代码透明的展示给所有人 ,即使存在一些漏洞 开发者也能看出这个脚本的逻辑和行为,是否存在 高危漏洞
当然 一些不懂得开发的服主 遇到这些问题依旧棘手 所以 目前我做的这几个脚本都是 逻辑非常简单功能单一 不会产生极端的灾难性漏洞,
我也想尝试 尽可能的将 插件代码更直观的展示给每个人 ,牢的评价很中肯 接受批评 除了报错不报错能不能跑 代码逻辑规范也很重要


页: [1]
查看完整版本: [原创|Scriptirc] MuteManager 禁言管理器