[原创|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,请定期备份,防止数据丢失。
[*]如需自定义权限、指令等,可参考源码注释进行修改。
[*]插件未集成经济等外部依赖,纯净高效。
不建议没有经验的人或者ai直接发布生产级别的内容:
1. 莫名其妙的MuteInfo类
2. 保存/加载config里面忽略了所有错误
3. 没有必要的定时任务
等
我没有插件开发经验...可能缺漏/不准确 WisW 发表于 2025-5-8 19:31
不建议没有经验的人或者ai直接发布生产级别的内容:
1. 莫名其妙的MuteInfo类
2. 保存/加载config里面忽略了 ...
感谢提建议 脚本确实测试可用 我也在学习开发 本质上确实不够完善 ,后期如果有人提出 报错 或者bug 我会持续修复它 WKEA 发表于 2025-5-8 19:43
感谢提建议 脚本确实测试可用 我也在学习开发 本质上确实不够完善 ,后期如果有人提出 报错 或者bug 我会 ...
这不是报错不报错, 能不能跑的问题, 至少是ai目前没有这个能力来应对这类问题
就第一行package com.example.muteplugin; 这就很离谱
而且没有人能保证ai是不是会写出一些很离谱的高危漏洞, 这个概率似乎还比较高, 而且我个人认为这个项目是直接对接生产环境的 WisW 发表于 2025-5-8 21:27
这不是报错不报错, 能不能跑的问题, 至少是ai目前没有这个能力来应对这类问题
就第一行package com.examp ...
大佬 确实你说的对 目前ai确实会犯低级错误 以至于可能会导致高危的BUG 我尽可能的也在调整这些
就像你一开始对代码的评价 一眼就看出问题,scriptirc脚本和sk等其他脚本都是将所有代码透明的展示给所有人 ,即使存在一些漏洞 开发者也能看出这个脚本的逻辑和行为,是否存在 高危漏洞
当然 一些不懂得开发的服主 遇到这些问题依旧棘手 所以 目前我做的这几个脚本都是 逻辑非常简单功能单一 不会产生极端的灾难性漏洞,
我也想尝试 尽可能的将 插件代码更直观的展示给每个人 ,牢的评价很中肯 接受批评 除了报错不报错能不能跑 代码逻辑规范也很重要
页:
[1]