From 86062bfe5012b5ef645fe1dd95cc3b6ab92c2381 Mon Sep 17 00:00:00 2001 From: guo <109414536+GuoKsmall@users.noreply.github.com> Date: Mon, 28 Jul 2025 13:47:40 +0800 Subject: [PATCH 1/5] Update 0.0.5 --- .../__init__.py" | 331 +++--------------- 1 file changed, 44 insertions(+), 287 deletions(-) diff --git "a/\345\215\217\347\256\241\347\263\273\347\273\237/__init__.py" "b/\345\215\217\347\256\241\347\263\273\347\273\237/__init__.py" index 644914b8..378da57a 100644 --- "a/\345\215\217\347\256\241\347\263\273\347\273\237/__init__.py" +++ "b/\345\215\217\347\256\241\347\263\273\347\273\237/__init__.py" @@ -1,304 +1,61 @@ -from tooldelta import Plugin, plugin_entry, Player, Chat, FrameExit, cfg, game_utils, utils, fmts, TYPE_CHECKING +from tooldelta import Plugin, plugin_entry, cfg +from tooldelta.constants import PacketIDS + +from .config import CONFIG_DEFAULT, CONFIG_STD +from .core import Core +from .deepseek import DeepSeek + class Auxiliary(Plugin): name = "协管系统" author = "果_k" - version = (0, 0, 2) + version = (0, 0, 5) def __init__(self, frame): super().__init__(frame) - self.players: "PlayerInfoMaintainer" = self.game_ctrl.players + self.players = self.game_ctrl.players self.ListenPreload(self.on_def) - CONFIG_DEFAULT = { - "协管名单": [ - "player1", - "player2" - ], - "命令转发": "命令转发 *转发协管输入命令内容", - "命令转发禁用关键词":[ - "kick", - "clear", - "op", - "deop", - "give", - "fog", - "gamemode", - "replaceitem", - "setblock", - "fill", - "setworldspawn", - "spawnpoint", - "structure", - "execute", - "tag", - "tickingarea", - "clone", - "difficulty", - "event", - "gamerule", - "scoreboard" - ], - "快捷功能": { - "视角投射":True, - "玩家查询":True, - "清理掉落物":True, - "查询背包物品":True, - "权限设置":True - - }, - "在线时间计分板":"zxsj", - - } - CONFIG_STD = { - "协管名单": cfg.JsonList((int,str)), - "命令转发": str, - "命令转发禁用关键词": cfg.JsonList(str), - "快捷功能": { - "视角投射":bool, - "玩家查询":bool, - "清理掉落物":bool, - "查询背包物品":bool, - "权限设置":bool - }, - "在线时间计分板":str, - } + config, _ = cfg.get_plugin_config_and_version( self.name, CONFIG_STD, CONFIG_DEFAULT, self.version - ) + ) + self.config = config self.GMlist = config["协管名单"] - self.CMDsend = config["命令转发"] + self.Info = config["Info"] self.NO_CMDsend = config["命令转发禁用关键词"] self.Focus = config["快捷功能"] self.online_time_scoreboard = config["在线时间计分板"] - + self.RandomRemark = config["随机插话"] + self.SetDeepOSeek = config["SetDeepOSeek"] + self.SetDeepSeek = config["SetDeepSeek"] + + # 初始化功能模块 + self.core = Core(self) + self.deepseek = DeepSeek(self) + + #监听数据包与注册 + if config.get("随机插话是否启用", False): + self.ListenPacket(PacketIDS.Text, self.deepseek.RandomRemark) def on_def(self): self.chatbar = self.GetPluginAPI("聊天栏菜单") - self.chatbar.add_new_trigger(["协管系统"], [], "协管系统", self.GMmenu) - self.chatbar.add_new_trigger(["命令转发","转发"], ..., "命令转发", self.CMDpost) - self.chatbar.add_new_trigger(["快捷功能"], [], "协管的快捷功能", self.GM_focus) - self.chatbar.add_new_trigger(["协管名单"], [], "查看所有协管名称", self.GMuser) - def GMmenu(self, player: Player, args: tuple): - playername = player.name - if playername in self.GMlist: - player.show("§b§l当前协管功能包含") - player.show("§a§l" + self.CMDsend) - player.show("§a§l您已是协管") - else: - player.show("您当前不是协管") - - @utils.thread_func("CMD") - def CMDpost(self, player: Player, args: tuple): - playername = player.name - if playername not in self.GMlist: - player.show("§c§l您不是协管") - return + self.deepseek.conversations_O.clear() # 清除所有的历史对话 + # 固定注册 + always_registered = [ + (["协管系统"], [], "协管系统", self.core.GMmenu), + (["快捷功能"], ..., "触发词后加对应功能数字可快速选择", self.core.GM_focus), + ] + # 按条件配置注册 + conditional_registered = [ + # 格式为 "开关配置名 [触发词] 参数 描述 处理函数" + ("命令转发是否启用", ["命令转发", "转发"], ..., "命令转发", self.core.CMDpost), + ("伪魔法指令DeepSeek是否启用", ["DeepOSeek", "dos"], ..., "DeepOSeek", self.deepseek.DeepOSeek), + ("协管名单是否启用", ["协管名单"], [], "查看所有协管名称", self.core.GMuser), + ("DeepSeek", ["DeepSeek", "ds"], ..., "DeepSeek", self.deepseek.DeepSeek), + ] + for trigger in always_registered: + self.chatbar.add_new_trigger(*trigger) + + for config_key, *trigger_info in conditional_registered: + if self.config[config_key]: + self.chatbar.add_new_trigger(*trigger_info) - if not args: - player.show("§c§l没有参数") - return - - try: - if isinstance(args[0], list): #拼接参数 - cmd = ' '.join(args[0]) - else: - cmd = ' '.join(args) - - for keyword in self.NO_CMDsend: #筛选指令内容 - if keyword in cmd: - player.show(f"§c§l命令包含禁止关键词:§e§l {keyword}§c§l,无法执行。") - return - - full_cmd = f"/execute as {playername} at {playername} run {cmd}" - resp = self.game_ctrl.sendwocmd(full_cmd) - fmts.print_inf(f"协管:{playername} 执行了指令: {cmd}") - player.show(resp) - - except IndexError as e: - player.show(f"§c§l参数索引错误: {str(e)}") - except Exception as e: - player.show(f"§c§l发生未知错误: {str(e)}") - def GM_focus(self, player: Player, args: tuple): - playername = player.name - if playername not in self.GMlist: - player.show("§c§l您不是协管") - return - enabled_features = [key for key, value in self.Focus.items() if value is True] - if not enabled_features: - player.show("§c§l当前没有可用的快捷功能。") - return - player.show("§b§l请选择一个功能:") - for idx, feature in enumerate(enabled_features, 1): - player.show(f"§f§l{idx}. {feature}") - feature_handlers = { - "视角投射": self.PosProjection, - "玩家查询": self.PlayerQuery, - "清理掉落物": self.ClearDropItems, - "查询背包物品": self.QueryInventory, - "权限设置": self.AbilitiesSet, - # 此处进行函数映射 - } - try: - choice_str = player.input("请输入功能序号: ") - choice = int(choice_str) - 1 - - if 0 <= choice < len(enabled_features): - selected_feature = enabled_features[choice] - handler = feature_handlers.get(selected_feature) - if handler: - handler(player, playername) # 调用对应的功能处理函数 - else: - player.show("§c§l该功能尚未实现。") - else: - player.show("§c§l无效的序号。") - except ValueError: - player.show("§c§l请输入一个有效的数字。") - - def GMuser(self, player: Player, args: tuple): - if not self.GMlist: - player.show("§c§l当前没有协管玩家。") - return - - player.show("§e§l当前协管名单如下: ") - for idx, gm in enumerate(self.GMlist, 1): - player.show(f"§f§l{idx}. {gm}") - pass - - def PosProjection(self, player, playername: str): - online_players = self.players.getAllPlayers() - player.show("§e§l请选择一个玩家: ") - for idx, p in enumerate(online_players, 1): - player.show(f"§f§l{idx}. {p.name}") - try: - choice_str = player.input("请输入玩家序号: ") - choice = int(choice_str) - 1 - - if 0 <= choice < len(online_players): - selected_player = online_players[choice] - player.show(f"§a§l您选择了: {selected_player.name}") - _,x1,y1,z1 = player.getPos() - dim,x,y,z = selected_player.getPos() - fmts.print_inf(f"协管:{playername} 使用了视角投射对: {selected_player.name} 至 {x} {y} {z}") - self.game_ctrl.sendwocmd(f"/gamemode spectator {playername}") - self.game_ctrl.sendwocmd(f"/execute as {playername} at {selected_player.name} run tp {x} {y} {z}") - player.show(f"§a§l已传送至: {selected_player.name} 维度 {dim}") - player.input("§a§l输入任意返回至原位置" ,-1 ) - self.game_ctrl.sendwocmd(f"/gamemode survival {playername}") - self.game_ctrl.sendwocmd(f"/execute as {playername} at {playername} run tp {x1} {y1} {z1}") - - else: - player.show("§c§l无效的序号。") - except ValueError: - player.show("§c§l请输入一个有效的数字。") - - def PlayerQuery(self, player, playername: str): - online_players = self.players.getAllPlayers() - player.show("§e§l请选择一个玩家:") - for idx, p in enumerate(online_players, 1): - player.show(f"§f§l{idx}. {p.name}") - try: - choice_str = player.input("请输入玩家序号: ") - choice = int(choice_str) - 1 - - if 0 <= choice < len(online_players): - selected_player = online_players[choice] - fmts.print_inf(f"协管:{playername} 使用了玩家查询: {selected_player.name}") - dim, x, y, z = selected_player.getPos() - onlinetinme = selected_player.getScore(self.online_time_scoreboard) - player.show(f"§a§l您选择了: {selected_player.name}") - player.show(f"§b§l游戏时长: {onlinetinme}") - player.show(f"§b§lUUID: {selected_player.uuid}") - player.show(f"§b§lUnique_id: {selected_player.unique_id}") - player.show(f"§b§l游戏平台: {selected_player.build_platform}") - player.show(f"§b§l玩家当前维度与坐标: {dim} {x} {y} {z}") - else: - player.show("§c§l无效的序号。") - except ValueError: - player.show("§c§l请输入一个有效的数字。") - def ClearDropItems(self, player, playername: str): - self.game_ctrl.sendwocmd(f"/kill @e[type=item]") - fmts.print_inf(f"协管:{playername} 使用了清理掉落物") - player.show("§a§l已清理掉落物") - def QueryInventory(self, player, playername: str): - online_players = self.players.getAllPlayers() - player.show("§e§l请选择一个玩家:") - for idx, p in enumerate(online_players, 1): - player.show(f"§f§l{idx}. {p.name}") - try: - choice_str = player.input("请输入玩家序号: ") - choice = int(choice_str) - 1 - - if 0 <= choice < len(online_players): - dim, x, y, z = player.getPos() - self.game_ctrl.sendwocmd(f"/setblock {x} {y} {z} shulker_box") #创建容器 - selected_player = online_players[choice] - inventory = selected_player.queryInventory() - for i in range(27): # 0 到 26 - slot = inventory.slots[i] if i < len(inventory.slots) else None - if slot is not None: - item_id = getattr(slot, 'id', '未知ID') - stack_size = getattr(slot, 'stackSize', 0) - player.show(f"§f§l槽位 {i}: ID={item_id}, 数量={stack_size}") - self.game_ctrl.sendwocmd( - f"/replaceitem block {x} {y} {z} slot.container {i} {item_id} {stack_size}" - ) - else: - player.show(f"§f§l槽位 {i}: 空") - self.game_ctrl.sendwocmd(f"/setblock {x} {y + 1} {z} shulker_box") # 在上方放置第二个箱子 - - for i in range(27, 36): # 27 到 35 - slot = inventory.slots[i] if i < len(inventory.slots) else None - if slot is not None: - item_id = getattr(slot, 'id', '未知ID') - stack_size = getattr(slot, 'stackSize', 0) - player.show(f"§f§l槽位 {i}: ID={item_id}, 数量={stack_size}") - self.game_ctrl.sendwocmd( - f"/replaceitem block {x} {y + 1} {z} slot.container {i - 27} {item_id} {stack_size}" - ) - else: - player.show(f"§f§l槽位 {i}: 空") - self.game_ctrl.sendwocmd(f"/setblock {x} {y} {z} air 0 destroy") - self.game_ctrl.sendwocmd(f"/setblock {x} {y + 1} {z} air 0 destroy") - fmts.print_inf(f"协管:{playername} 使用了查询玩家背包: {selected_player.name}") - else: - player.show("§c§l无效的序号。") - except ValueError: - player.show("§c§l请输入一个有效的数字。") - def AbilitiesSet(self, player, playername: str): - online_players = self.players.getAllPlayers() - player.show("§e§l请选择一个玩家:") - for idx, p in enumerate(online_players, 1): - player.show(f"§f§l{idx}. {p.name}") - try: - choice_str = player.input("请输入玩家序号: ") - choice = int(choice_str) - 1 - - if 0 <= choice < len(online_players): - selected_player = online_players[choice] - abilities = selected_player.abilities - - player.show("§b§l当前能力设置:") #显示当前能力设置 - player.show(f"§f§l1. 破坏方块: {'§a开启' if abilities.mine else '§c关闭'}") - player.show(f"§f§l2. 攻击玩家: {'§a开启' if abilities.attack_players else '§c关闭'}") - player.show(f"§f§l3. 攻击生物: {'§a开启' if abilities.attack_mobs else '§c关闭'}") - - choice_str = player.input("请输入要修改的能力序号(1-3): ") - choice = int(choice_str) - 1 - - if 0 <= choice < 3: - ability_names = ["mine", "attack_players", "attack_mobs"] - ability_name = ability_names[choice] - current_value = getattr(abilities, ability_name) - - setattr(abilities, ability_name, not current_value) - selected_player.setAbilities(abilities) #更新能力 - fmts.print_inf(f"协管:{playername} 修改了玩家能力: {selected_player.name} {['破坏方块', '攻击玩家', '攻击生物'][choice]}为: {'§a开启' if not current_value else '§c关闭'}") - player.show(f"§a§l{['破坏方块', '攻击玩家', '攻击生物'][choice]}已{'§a开启' if not current_value else '§c关闭'}") - else: - player.show("§c§l无效的序号。") - - else: - player.show("§c§l无效的序号。") - - except Exception as e: - player.show(f"§c§l发生错误: {str(e)}") - entry = plugin_entry(Auxiliary) \ No newline at end of file From 578423d035268f812cd3b4aaecc24c14e91d89bc Mon Sep 17 00:00:00 2001 From: guo <109414536+GuoKsmall@users.noreply.github.com> Date: Mon, 28 Jul 2025 13:48:50 +0800 Subject: [PATCH 2/5] v 0.0.5 --- .../config.py" | 87 ++++++ .../core.py" | 256 ++++++++++++++++ .../datas.json" | 4 +- .../deepseek.py" | 273 ++++++++++++++++++ ...77\347\224\250\346\226\271\346\263\225.md" | 40 +++ 5 files changed, 658 insertions(+), 2 deletions(-) create mode 100644 "\345\215\217\347\256\241\347\263\273\347\273\237/config.py" create mode 100644 "\345\215\217\347\256\241\347\263\273\347\273\237/core.py" create mode 100644 "\345\215\217\347\256\241\347\263\273\347\273\237/deepseek.py" create mode 100644 "\345\215\217\347\256\241\347\263\273\347\273\237/\344\275\277\347\224\250\346\226\271\346\263\225.md" diff --git "a/\345\215\217\347\256\241\347\263\273\347\273\237/config.py" "b/\345\215\217\347\256\241\347\263\273\347\273\237/config.py" new file mode 100644 index 00000000..cca22a72 --- /dev/null +++ "b/\345\215\217\347\256\241\347\263\273\347\273\237/config.py" @@ -0,0 +1,87 @@ +from tooldelta import cfg + +CONFIG_DEFAULT = { + "协管名单": [ + "player1", + "player2", + "bot_name" + ], + "Info": { + "若不是协管": "§c§l您当前不是协管", + "选择玩家列表": "§e§l请选择一个玩家:", + "命令转发": "命令转发 *转发协管输入命令内容", + "DeepSeek": "DeepSeek *DeepSeek", + }, + "SetDeepOSeek": { + "APIkey": "此处换成你的DeepSeekAPIkey", + "DeepSeek提示词": "你现在要为我的世界基岩版服务器玩家提供服务,当玩家询问你指令相关问题,请你做出对应指令并仅仅只返回不带/前缀的指令信息,你需要依据玩家名做出对应的选择器替代,除此之外不要返回任何除命令以外信息,这是绝对的,尽量将玩家的请求转化为命令可实现形式,当有玩家询问了无法理解的内容请返回:非指令问题 不予处理", + "max_history_length": 10, + "DeepSeekModel": "deepseek-chat" + }, + "SetDeepSeek": { + "APIkey": "此处换成你的DeepSeekAPIkey", + "DeepSeek提示词": "你是一只猫娘,回答字数限制八十字以内,不要带有任何emoji符号", + "max_history_length": 15, + "DeepSeekModel": "deepseek-chat", + "stream": False + }, + "随机插话": { + "检测频率": 4, + "插话概率": 0.5 + }, + "命令转发禁用关键词": [ + "clear", + "kick", + "op", + "deop", + "gamemode", + "replaceitem", + "setblock", + "fill", + "setworldspawn", + "spawnpoint", + "structure", + "execute", + "tag", + "tickingarea", + "clone", + "difficulty", + "event", + "gamerule", + "scoreboard" + ], + "快捷功能": { + "视角投射": True, + "玩家查询": True, + "清理掉落物": True, + "查询背包物品": True, + "权限设置": True + }, + "命令转发是否启用": True, + "伪魔法指令DeepSeek是否启用": False, + "协管名单是否启用": False, + "随机插话是否启用": False, + "DeepSeek": True, + "在线时间计分板": "zxsj", +} + +CONFIG_STD = { + "协管名单": cfg.JsonList((int, str)), + "Info": dict, + "SetDeepOSeek": dict, + "SetDeepSeek": dict, + "随机插话": dict, + "命令转发禁用关键词": cfg.JsonList(str), + "快捷功能": { + "视角投射": bool, + "玩家查询": bool, + "清理掉落物": bool, + "查询背包物品": bool, + "权限设置": bool + }, + "命令转发是否启用": bool, + "伪魔法指令DeepSeek是否启用": bool, + "协管名单是否启用": bool, + "随机插话是否启用": bool, + "在线时间计分板": str, +} \ No newline at end of file diff --git "a/\345\215\217\347\256\241\347\263\273\347\273\237/core.py" "b/\345\215\217\347\256\241\347\263\273\347\273\237/core.py" new file mode 100644 index 00000000..86805fac --- /dev/null +++ "b/\345\215\217\347\256\241\347\263\273\347\273\237/core.py" @@ -0,0 +1,256 @@ +from tooldelta import Player, fmts +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from . import Auxiliary + +class Core: + def __init__(self, plugin: "Auxiliary"): + self.plugin = plugin + self.game_ctrl = plugin.game_ctrl + self.players = plugin.players + + def GMmenu(self, player: Player, args: tuple): + playername = player.name + if playername in self.plugin.GMlist: + player.show("§b§l当前协管功能包含") + player.show(f"§a§l + {self.plugin.Info['命令转发']}") + player.show("§a§l您已是协管") + else: + player.show(self.plugin.Info['若不是协管']) + + def CMDpost(self, player: Player, args: tuple): + playername = player.name + if playername not in self.plugin.GMlist: + player.show(self.plugin.Info['若不是协管']) + return True + + if not args: + player.show("§c§l没有参数") + return True + + try: + if isinstance(args[0], list): # 拼接参数 + cmd = ' '.join(args[0]) + else: + cmd = ' '.join(args) + + for keyword in self.plugin.NO_CMDsend: # 筛选指令内容 + if keyword in cmd: + player.show(f"§c§l命令包含禁止关键词:§e§l {keyword}§c§l,无法执行。") + return True + + full_cmd = f"/execute as {playername} at {playername} run {cmd}" + self.game_ctrl.sendwocmd(full_cmd) + fmts.print_inf(f"协管:{playername} 执行了指令: {cmd}") + + except IndexError as e: + player.show(f"§c§l参数索引错误: {str(e)}") + except Exception as e: + player.show(f"§c§l发生未知错误: {str(e)}") + return True + + def GM_focus(self, player: Player, args: tuple): + playername = player.name + if playername not in self.plugin.GMlist: + player.show(self.plugin.Info['若不是协管']) + return True + + enabled_features = [key for key, value in self.plugin.Focus.items() if value is True] # 获取可用功能列表 + if not enabled_features: + player.show("§c§l当前没有可用的快捷功能。") + return True + + feature_handlers = { + "视角投射": self.PosProjection, + "玩家查询": self.PlayerQuery, + "清理掉落物": self.ClearDropItems, + "查询背包物品": self.QueryInventory, + "权限设置": self.AbilitiesSet, + } + + if args: # 支持参数快速选中功能 + try: + choice_str = args[0] if isinstance(args[0], str) else str(args[0]) + choice = int(choice_str) - 1 + + if 0 <= choice < len(enabled_features): + selected_feature = enabled_features[choice] + handler = feature_handlers.get(selected_feature) # 获取映射的函数 + if handler: + handler(player, playername) # 调用对应的功能处理函数 + else: + player.show("§c§l该功能尚未实现。") + else: + player.show("§c§l无效的序号。") + except ValueError: + player.show("§c§l请输入一个有效的数字。") + else: + player.show("§b§l请选择一个功能:") + for idx, feature in enumerate(enabled_features, 1): + player.show(f"§f§l{idx}. {feature}") + try: + choice_str = player.input("请输入功能序号: ") + choice = int(choice_str) - 1 + + if 0 <= choice < len(enabled_features): + selected_feature = enabled_features[choice] + handler = feature_handlers.get(selected_feature) + if handler: + handler(player, playername) + else: + player.show("§c§l该功能尚未实现。") + else: + player.show("§c§l无效的序号。") + except ValueError: + player.show("§c§l请输入一个有效的数字。") + return True + + def GMuser(self, player: Player, args: tuple): + if not self.plugin.GMlist: + player.show("§c§l当前没有协管玩家。") + return True + + player.show("§e§l当前协管名单如下: ") + for idx, gm in enumerate(self.plugin.GMlist, 1): + player.show(f"§f§l{idx}. {gm}") + return True + + def PosProjection(self, player, playername: str): + online_players = self.players.getAllPlayers() + player.show(self.plugin.Info['选择玩家列表']) + for idx, p in enumerate(online_players, 1): + player.show(f"§f§l{idx}. {p.name}") + try: + choice_str = player.input("请输入玩家序号: ") + choice = int(choice_str) - 1 + + if 0 <= choice < len(online_players): + selected_player = online_players[choice] + player.show(f"§a§l您选择了: {selected_player.name}") + _, x1, y1, z1 = player.getPos() + dim, x, y, z = selected_player.getPos() + fmts.print_inf(f"协管:{playername} 使用了视角投射对: {selected_player.name} 至 {x} {y} {z}") + self.game_ctrl.sendwocmd(f"/gamemode spectator {playername}") + self.game_ctrl.sendwocmd(f"/execute as {playername} at {selected_player.name} run tp {x} {y} {z}") + player.show(f"§a§l已传送至: {selected_player.name} 维度 {dim}") + player.input("§a§l输入任意返回至原位置", -1) + self.game_ctrl.sendwocmd(f"/gamemode survival {playername}") + self.game_ctrl.sendwocmd(f"/execute as {playername} at {playername} run tp {x1} {y1} {z1}") + + else: + player.show("§c§l无效的序号。") + except ValueError: + player.show("§c§l请输入一个有效的数字。") + + def PlayerQuery(self, player, playername: str): + online_players = self.players.getAllPlayers() + player.show(self.plugin.Info['选择玩家列表']) + for idx, p in enumerate(online_players, 1): + player.show(f"§f§l{idx}. {p.name}") + try: + choice_str = player.input("请输入玩家序号: ") + choice = int(choice_str) - 1 + + if 0 <= choice < len(online_players): + selected_player = online_players[choice] + fmts.print_inf(f"协管:{playername} 使用了玩家查询: {selected_player.name}") + dim, x, y, z = selected_player.getPos() + onlinetinme = selected_player.getScore(self.plugin.online_time_scoreboard) + player.show(f"§a§l您选择了: {selected_player.name}") + player.show(f"§b§l游戏时长: {onlinetinme}") + player.show(f"§b§lUUID: {selected_player.uuid}") + player.show(f"§b§lUnique_id: {selected_player.unique_id}") + player.show(f"§b§l游戏平台: {selected_player.build_platform}") + player.show(f"§b§l玩家当前维度与坐标: {dim} {x} {y} {z}") + else: + player.show("§c§l无效的序号。") + except ValueError: + player.show("§c§l请输入一个有效的数字。") + + def ClearDropItems(self, player, playername: str): + self.game_ctrl.sendwocmd("/kill @e[type=item]") + fmts.print_inf(f"协管:{playername} 使用了清理掉落物") + player.show("§a§l已清理掉落物") + + def QueryInventory(self, player, playername: str): + online_players = self.players.getAllPlayers() + player.show(self.plugin.Info['选择玩家列表']) + for idx, p in enumerate(online_players, 1): + player.show(f"§f§l{idx}. {p.name}") + try: + choice_str = player.input("请输入玩家序号: ") + choice = int(choice_str) - 1 + + if 0 <= choice < len(online_players): + dim, x, y, z = player.getPos() + self.game_ctrl.sendwocmd(f"/setblock {x} {y} {z} shulker_box") # 创建容器 + selected_player = online_players[choice] + inventory = selected_player.queryInventory() + for i in range(27): # 0 到 26的物品 + slot = inventory.slots[i] if i < len(inventory.slots) else None + if slot is not None: + item_id = getattr(slot, 'id', '未知ID') + stack_size = getattr(slot, 'stackSize', 0) + player.show(f"§f§l槽位 {i}: ID={item_id}, 数量={stack_size}") + self.game_ctrl.sendwocmd(f"/replaceitem block {x} {y} {z} slot.container {i} {item_id} {stack_size}") + else: + player.show(f"§f§l槽位 {i}: 空") + self.game_ctrl.sendwocmd(f"/setblock {x} {y + 1} {z} shulker_box") # 在上方放置第二个容器 + + for i in range(27, 36): # 将27 到 35 格位的物品放到另一个容器 + slot = inventory.slots[i] if i < len(inventory.slots) else None + if slot is not None: + item_id = getattr(slot, 'id', '未知ID') + stack_size = getattr(slot, 'stackSize', 0) + player.show(f"§f§l槽位 {i}: ID={item_id}, 数量={stack_size}") + self.game_ctrl.sendwocmd(f"/replaceitem block {x} {y + 1} {z} slot.container {i - 27} {item_id} {stack_size}") + else: + player.show(f"§f§l槽位 {i}: 空") + self.game_ctrl.sendwocmd(f"/setblock {x} {y} {z} air 0 destroy") + self.game_ctrl.sendwocmd(f"/setblock {x} {y + 1} {z} air 0 destroy") + fmts.print_inf(f"协管:{playername} 使用了查询玩家背包: {selected_player.name}") + else: + player.show("§c§l无效的序号。") + except ValueError: + player.show("§c§l请输入一个有效的数字。") + + def AbilitiesSet(self, player, playername: str): + online_players = self.players.getAllPlayers() + filtered_players = [p for p in online_players if p.name not in self.plugin.GMlist] + player.show(self.plugin.Info['选择玩家列表']) + for idx, p in enumerate(filtered_players, 1): + player.show(f"§f§l{idx}. {p.name}") + try: + choice_str = player.input("请输入玩家序号: ") + choice = int(choice_str) - 1 + + if 0 <= choice < len(filtered_players): + selected_player = filtered_players[choice] + abilities = selected_player.abilities + + player.show("§b§l当前能力设置:") + player.show(f"§f§l1. 破坏方块: {'§a开启' if abilities.mine else '§c关闭'}") + player.show(f"§f§l2. 攻击玩家: {'§a开启' if abilities.attack_players else '§c关闭'}") + player.show(f"§f§l3. 攻击生物: {'§a开启' if abilities.attack_mobs else '§c关闭'}") + + choice_str = player.input("请输入要修改的能力序号(1-3): ") + choice = int(choice_str) - 1 + + if 0 <= choice < 3: + ability_names = ["mine", "attack_players", "attack_mobs"] + ability_name = ability_names[choice] + current_value = getattr(abilities, ability_name) + + setattr(abilities, ability_name, not current_value) + selected_player.setAbilities(abilities) # 更新能力 + fmts.print_inf(f"协管:{playername} 修改了玩家能力: {selected_player.name} {['破坏方块', '攻击玩家', '攻击生物'][choice]}为: {'§a开启' if not current_value else '§c关闭'}") + player.show(f"§a§l{['破坏方块', '攻击玩家', '攻击生物'][choice]}已{'§a开启' if not current_value else '§c关闭'}") + else: + player.show("§c§l无效的序号。") + + else: + player.show("§c§l无效的序号。") + + except Exception as e: + player.show(f"§c§l发生错误: {str(e)}") \ No newline at end of file diff --git "a/\345\215\217\347\256\241\347\263\273\347\273\237/datas.json" "b/\345\215\217\347\256\241\347\263\273\347\273\237/datas.json" index b707eeb4..c1a21e0d 100644 --- "a/\345\215\217\347\256\241\347\263\273\347\273\237/datas.json" +++ "b/\345\215\217\347\256\241\347\263\273\347\273\237/datas.json" @@ -1,8 +1,8 @@ { "author": "果_k", - "version": "0.0.2", + "version": "0.0.5", "plugin-type": "classic", - "description": "使协管可以执行部分功能 包括/n视角投射 查询玩家背包物品 设置玩家权限 命令转发等", + "description": "使协管可以执行部分功能 包括/n视角投射 查询玩家背包物品 设置玩家权限 命令转发 DeepSeek多轮对话等", "pre-plugins": {}, "plugin-id": "协管系统" } \ No newline at end of file diff --git "a/\345\215\217\347\256\241\347\263\273\347\273\237/deepseek.py" "b/\345\215\217\347\256\241\347\263\273\347\273\237/deepseek.py" new file mode 100644 index 00000000..616c07ad --- /dev/null +++ "b/\345\215\217\347\256\241\347\263\273\347\273\237/deepseek.py" @@ -0,0 +1,273 @@ +import requests +import random +from tooldelta import Player, fmts, utils + +class DeepSeek: + def __init__(self, plugin): + self.plugin = plugin + self.conversations_O = {} + self.conversations = {} + self.packet_count = 0 + + def calculate_output_cost(self, total_output_tokens): + cost_per_million = 8 # 计算价格 这里需要根据对应不同模型实际token价格进行修改 + return (total_output_tokens / 1_000_000) * cost_per_million + + def format_text_with_newlines(self, text: str, chars_per_line: int) -> str: + if not text: + return text + + # 每chars_per_line个字符添加一个换行符 + formatted_text = "" + for i in range(0, len(text), chars_per_line): + if i > 0: + formatted_text += "\n" + formatted_text += text[i:i + chars_per_line] + + return formatted_text + + + @utils.thread_func("RandomRemark") + def RandomRemark(self, packet: dict): + # 过滤选择玩家发送的信息 + if packet.get('TextType') != 1 or not packet.get('SourceName'): + return + self.packet_count += 1 + trigger_frequency = self.plugin.RandomRemark['检测频率'] + + if self.packet_count >= trigger_frequency: + self.packet_count = 0 # 重置计数器 + remark_probability = self.plugin.RandomRemark['插话概率'] + + if random.random() < remark_probability: + p = packet + print(p['Message']) + result = self.reDeepSeek( p['Message'], p['SourceName']) + reply = result['choices'][0]['message']['content'] + command = '/tellraw @a {{"rawtext":[{{"text":"<§d小猫娘§f>:§e@{}§a {}"}}]}}'.format(p['SourceName'],reply) + self.plugin.game_ctrl.sendwocmd(command) + def reDeepOSeek(self, Message: str, playername: str): + base_url = "https://api.deepseek.com" + api_key = self.plugin.SetDeepOSeek['APIkey'] + if not api_key: + return "错误:插件管理者未提供 DeepSeek API 密钥。" + headers = { + "Content-Type": "application/json", + "Authorization": f"Bearer {api_key}" + } + if playername not in self.conversations_O: + self.conversations_O[playername] = [] + messages = self.conversations_O[playername] + + if not messages or messages[0]["role"] != "system": + messages.insert(0, { + "role": "system", + "content": self.plugin.SetDeepOSeek['DeepSeek提示词'] + }) + messages.append({ + "role": "user", + "content": f"{playername}: {Message}", + "name": playername + }) + payload = { + "model": self.plugin.SetDeepOSeek['DeepSeekModel'], + "messages": messages, + "stream": False + } + try: + response = requests.post( + f"{base_url}/v1/chat/completions", + headers=headers, + json=payload + ) + if response.status_code != 200: + return f"请求失败,状态码:{response.status_code},响应内容:{response.text}" + result = response.json() + assistant_response = result['choices'][0]['message']['content'] + messages.append({ + "role": "assistant", + "content": assistant_response + }) + if len(messages) > self.plugin.SetDeepOSeek['max_history_length'] * 2: + self.conversations_O[playername] = messages[-self.plugin.SetDeepOSeek['max_history_length'] * 2:] + return result + except requests.exceptions.RequestException as e: + return f"网络请求异常:{str(e)}" + except UnicodeError as e: + return f"编码异常:{str(e)}" + + def reDeepSeek(self, Message: str, playername: str): + base_url = "https://api.deepseek.com" + api_key = self.plugin.SetDeepSeek['APIkey'] + if not api_key: + return "错误:插件管理者未提供 DeepSeek API 密钥。" + headers = { + "Content-Type": "application/json", + "Authorization": f"Bearer {api_key}" + } + if playername not in self.conversations: + self.conversations[playername] = [] + messages = self.conversations[playername] + + if not messages or messages[0]["role"] != "system": + messages.insert(0, { + "role": "system", + "content": self.plugin.SetDeepSeek['DeepSeek提示词'] + }) + messages.append({ + "role": "user", + "content": f"{playername}: {Message}", + "name": playername + }) + payload = { + "model": self.plugin.SetDeepSeek['DeepSeekModel'], + "messages": messages, + "stream": self.plugin.SetDeepSeek.get('stream', False) + } + + # 如果启用流式输出 + if self.plugin.SetDeepSeek.get('stream', False): + try: + response = requests.post( + f"{base_url}/v1/chat/completions", + headers=headers, + json=payload, + stream=True + ) + if response.status_code != 200: + return f"请求失败,状态码:{response.status_code},响应内容:{response.text}" + + # 返回流式响应对象 + return response + except requests.exceptions.RequestException as e: + return f"网络请求异常:{str(e)}" + except UnicodeError as e: + return f"编码异常:{str(e)}" + else: + # 非流式输出 + try: + response = requests.post( + f"{base_url}/v1/chat/completions", + headers=headers, + json=payload + ) + if response.status_code != 200: + return f"请求失败,状态码:{response.status_code},响应内容:{response.text}" + result = response.json() + assistant_response = result['choices'][0]['message']['content'] + messages.append({ + "role": "assistant", + "content": assistant_response + }) + if len(messages) > self.plugin.SetDeepSeek['max_history_length'] * 2: + self.conversations[playername] = messages[-self.plugin.SetDeepSeek['max_history_length'] * 2:] + return result + except requests.exceptions.RequestException as e: + return f"网络请求异常:{str(e)}" + except UnicodeError as e: + return f"编码异常:{str(e)}" + + @utils.thread_func("DeepOSeek") + def DeepOSeek(self, player: Player, args: tuple): + playername = player.name + if playername not in self.plugin.GMlist: + player.show(self.plugin.Info['若不是协管']) + return True + + # 获取用户输入内容 + if args: + try: + param = ' '.join(args) if isinstance(args, (list, tuple)) else str(args) + except ValueError: + player.show("§c§l参数格式错误") + return True + else: + try: + param = player.input("§a§l请输入要问的指令: ") + except Exception as e: + player.show(f"§c§l输入无效{str(e)}") + return True + + player.show(f"§a§l内容: {param},正在转发") + DS_result = self.reDeepOSeek(param, player.name) + for keyword in self.plugin.NO_CMDsend: + if keyword in DS_result['choices'][0]['message']['content']: + player.show(f"§c§l命令包含禁止关键词:§e§l {keyword}§c§l,无法执行。") + return True + player.show(f"§f§l原始指令内容为:§u{DS_result['choices'][0]['message']['content']} §f本次消耗Token: {DS_result['usage']['total_tokens']} §a共花费{self.calculate_output_cost(DS_result['usage']['total_tokens'])}元") + self.plugin.game_ctrl.sendwocmd(f"/{DS_result['choices'][0]['message']['content']}") + fmts.print(f"§f§l玩家:§e§l {player.name}§f§l 请求了§e§l {param}") + fmts.print(f"§f§l实际执行结果为§e§l {DS_result['choices'][0]['message']['content']} §f本次消耗Token: {DS_result['usage']['total_tokens']} §a共花费{self.calculate_output_cost(DS_result['usage']['total_tokens'])}元") + return True + + @utils.thread_func("DeepSeek") + def DeepSeek(self, player: Player, args: tuple): + # 获取用户输入内容 + if args: + try: + param = ' '.join(args) if isinstance(args, (list, tuple)) else str(args) + except ValueError: + player.show("§c§l参数格式错误") + return True + else: + try: + param = player.input("§a§l请输入内容: ") + except Exception as e: + player.show(f"§c§l输入无效: {str(e)}") + return True + + player.show(f"§a§l内容: {param},正在转发") + DS_result = self.reDeepSeek(param, player.name) + + # 检查是否为错误消息 + if isinstance(DS_result, str): + if DS_result.startswith("错误:") or "请求失败" in DS_result or "网络请求异常" in DS_result or "编码异常" in DS_result: + player.show(f"§c§l{DS_result}") + return True + + # 检查是否为流式响应对象 + if hasattr(DS_result, 'iter_lines'): + full_response = "" + player.show("§a§l正在流式接收响应...") + + # 处理流式响应 + try: + for line in DS_result.iter_lines(): + if line: + decoded_line = line.decode('utf-8') + if decoded_line.startswith("data: "): + data = decoded_line[6:] # 移除 "data: " 前缀 + if data != "[DONE]": + try: + import json + chunk_data = json.loads(data) + if 'choices' in chunk_data and len(chunk_data['choices']) > 0: + delta = chunk_data['choices'][0].get('delta', {}) + content = delta.get('content', '') + if content: + full_response += content + formatted_text = self.format_text_with_newlines(full_response, 20) + command = '/titleraw @a[name="{}"] actionbar {{"rawtext":[{{"text":":{}"}}]}}'.format(player.name, formatted_text) + self.plugin.game_ctrl.sendwocmd(command) + except json.JSONDecodeError: + pass + # 处理包含usage信息的最终数据块 + elif data == "[DONE]": + command = '/titleraw @a[name="{}"] actionbar {{"rawtext":[{{"text":":{}"}}]}}'.format(player.name, formatted_text) + self.plugin.game_ctrl.sendwocmd(command) + + # 显示完整响应 + player.show(f"§f§l原始内容为:§e{full_response}") + fmts.print(f"§f§l玩家:§e§l {player.name}§f§l 请求了§e§l {param}") + fmts.print(f"§f§l实际执行结果为§e§l {full_response}") + except Exception as e: + player.show(f"§c§l流式传输过程中发生错误: {str(e)}") + else: + # 非流式输出处理 + if isinstance(DS_result, dict) and 'choices' in DS_result: + player.show(f"§f§l原始内容为:§e{DS_result['choices'][0]['message']['content']} §f本次消耗Token: {DS_result['usage']['total_tokens']} §a共花费{self.calculate_output_cost(DS_result['usage']['total_tokens'])}元") + fmts.print(f"§f§l玩家:§e§l {player.name}§f§l 请求了§e§l {param}") + fmts.print(f"§f§l实际执行结果为§e§l {DS_result['choices'][0]['message']['content']} §f本次消耗Token: {DS_result['usage']['total_tokens']} §a共花费{self.calculate_output_cost(DS_result['usage']['total_tokens'])}元") + else: + player.show(f"§c§l响应格式错误: {DS_result}") + return True \ No newline at end of file diff --git "a/\345\215\217\347\256\241\347\263\273\347\273\237/\344\275\277\347\224\250\346\226\271\346\263\225.md" "b/\345\215\217\347\256\241\347\263\273\347\273\237/\344\275\277\347\224\250\346\226\271\346\263\225.md" new file mode 100644 index 00000000..03ef53a1 --- /dev/null +++ "b/\345\215\217\347\256\241\347\263\273\347\273\237/\344\275\277\347\224\250\346\226\271\346\263\225.md" @@ -0,0 +1,40 @@ +# 协管系统插件使用文档 +## 一、插件基本信息 +版本 0.0.5 +主要功能
1.管理协管人员名单
2.提供命令转发功能
3.DeepSeek AI生成指令
4.快捷管理功能 +## 2. 配置项说明 +**·协管名单**: 可以使用协管功能的玩家名称列表 +**·Info**: 系统提示信息配置 +**·SetDeepSeek**: DeepSeek AI相关配置 +**·APIkey**: DeepSeek的API密钥 +**·DeepSeek提示词**: AI的提示词 +**·max_history_length**: 历史对话最多保留轮次 +**·DeepSeekModel**: 使用的DeepSeek模型 +**·命令转发禁用关键词**: 包含这些关键词的命令将被拦截 +**·快捷功能**: 各项快捷管理功能是否启用 +**·命令转发是否启用**: 是否启用命令转发功能 +**·DeepSeek是否启用**: 是否启用DeepSeek AI功能 +**·协管名单是否启用**: 是否启用协管名单验证 +**·在线时间计分板**: 记录玩家在线时间的计分板名称 +### 命令转发 +协管通过聊天栏输入 ` [命令转发/转发] + [cmd] ` 输入命令 +会根据配置项中的禁用关键词列表进行校验 +验证通过后,系统会执行该命令并返回结果 +### DeepSeek AI指令生成(伪魔法指令) +协管在聊天栏输入 ` [DeepSeek/ds] + [问题]` +系统会将问题发送给DeepSeek AI 会返回对应的指令 +使Tool执行这些指令 该功能可以帮助协管快速生成简单的指令 +**注意 : 该功能同样受配置项中的禁用关键词列表限制** +*如果你不知道如何获取DeepSeekAPIkey 请查看* +[DeepSeek APIkey获取教程](https://www.bilibili.com/video/BV1JsopY5E3g) +## 快捷功能菜单 +协管在聊天栏输入 `快捷功能` ,可以查看可用的快捷功能列表: + +**视角投射**: 可以瞬移到在线玩家的位置观察 +**玩家查询**: 查看在线玩家的基本信息和位置 +**清理掉落物**: 清理服务器中所有掉落物 +**查询背包物品**: 查看在线玩家的背包内容 +**权限设置**: 修改其他玩家的能力权限 + +### 如有BUG请在Tooldelta群内反馈 + From d8951a4f614f3dd5ba0ca39e3007ba8060ad49d0 Mon Sep 17 00:00:00 2001 From: guo <109414536+GuoKsmall@users.noreply.github.com> Date: Mon, 28 Jul 2025 14:08:21 +0800 Subject: [PATCH 3/5] fix v0.0.5 --- .../__init__.py" | 4 +++- .../config.py" | 1 + .../core.py" | 3 ++- .../deepseek.py" | 11 ++++++----- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git "a/\345\215\217\347\256\241\347\263\273\347\273\237/__init__.py" "b/\345\215\217\347\256\241\347\263\273\347\273\237/__init__.py" index 378da57a..573e0924 100644 --- "a/\345\215\217\347\256\241\347\263\273\347\273\237/__init__.py" +++ "b/\345\215\217\347\256\241\347\263\273\347\273\237/__init__.py" @@ -5,6 +5,7 @@ from .core import Core from .deepseek import DeepSeek + class Auxiliary(Plugin): name = "协管系统" author = "果_k" @@ -32,9 +33,10 @@ def __init__(self, frame): self.core = Core(self) self.deepseek = DeepSeek(self) - #监听数据包与注册 + # 监听数据包与注册 if config.get("随机插话是否启用", False): self.ListenPacket(PacketIDS.Text, self.deepseek.RandomRemark) + def on_def(self): self.chatbar = self.GetPluginAPI("聊天栏菜单") self.deepseek.conversations_O.clear() # 清除所有的历史对话 diff --git "a/\345\215\217\347\256\241\347\263\273\347\273\237/config.py" "b/\345\215\217\347\256\241\347\263\273\347\273\237/config.py" index cca22a72..693a77b1 100644 --- "a/\345\215\217\347\256\241\347\263\273\347\273\237/config.py" +++ "b/\345\215\217\347\256\241\347\263\273\347\273\237/config.py" @@ -1,5 +1,6 @@ from tooldelta import cfg + CONFIG_DEFAULT = { "协管名单": [ "player1", diff --git "a/\345\215\217\347\256\241\347\263\273\347\273\237/core.py" "b/\345\215\217\347\256\241\347\263\273\347\273\237/core.py" index 86805fac..8a1be17e 100644 --- "a/\345\215\217\347\256\241\347\263\273\347\273\237/core.py" +++ "b/\345\215\217\347\256\241\347\263\273\347\273\237/core.py" @@ -1,6 +1,7 @@ from tooldelta import Player, fmts from typing import TYPE_CHECKING + if TYPE_CHECKING: from . import Auxiliary @@ -183,7 +184,7 @@ def QueryInventory(self, player, playername: str): choice = int(choice_str) - 1 if 0 <= choice < len(online_players): - dim, x, y, z = player.getPos() + _, x, y, z = player.getPos() self.game_ctrl.sendwocmd(f"/setblock {x} {y} {z} shulker_box") # 创建容器 selected_player = online_players[choice] inventory = selected_player.queryInventory() diff --git "a/\345\215\217\347\256\241\347\263\273\347\273\237/deepseek.py" "b/\345\215\217\347\256\241\347\263\273\347\273\237/deepseek.py" index 616c07ad..83a96b4f 100644 --- "a/\345\215\217\347\256\241\347\263\273\347\273\237/deepseek.py" +++ "b/\345\215\217\347\256\241\347\263\273\347\273\237/deepseek.py" @@ -2,6 +2,7 @@ import random from tooldelta import Player, fmts, utils + class DeepSeek: def __init__(self, plugin): self.plugin = plugin @@ -25,8 +26,7 @@ def format_text_with_newlines(self, text: str, chars_per_line: int) -> str: formatted_text += text[i:i + chars_per_line] return formatted_text - - + @utils.thread_func("RandomRemark") def RandomRemark(self, packet: dict): # 过滤选择玩家发送的信息 @@ -42,10 +42,11 @@ def RandomRemark(self, packet: dict): if random.random() < remark_probability: p = packet print(p['Message']) - result = self.reDeepSeek( p['Message'], p['SourceName']) + result = self.reDeepSeek(p['Message'], p['SourceName']) reply = result['choices'][0]['message']['content'] command = '/tellraw @a {{"rawtext":[{{"text":"<§d小猫娘§f>:§e@{}§a {}"}}]}}'.format(p['SourceName'],reply) self.plugin.game_ctrl.sendwocmd(command) + def reDeepOSeek(self, Message: str, playername: str): base_url = "https://api.deepseek.com" api_key = self.plugin.SetDeepOSeek['APIkey'] @@ -166,7 +167,7 @@ def reDeepSeek(self, Message: str, playername: str): return f"网络请求异常:{str(e)}" except UnicodeError as e: return f"编码异常:{str(e)}" - + @utils.thread_func("DeepOSeek") def DeepOSeek(self, player: Player, args: tuple): playername = player.name @@ -199,7 +200,7 @@ def DeepOSeek(self, player: Player, args: tuple): fmts.print(f"§f§l玩家:§e§l {player.name}§f§l 请求了§e§l {param}") fmts.print(f"§f§l实际执行结果为§e§l {DS_result['choices'][0]['message']['content']} §f本次消耗Token: {DS_result['usage']['total_tokens']} §a共花费{self.calculate_output_cost(DS_result['usage']['total_tokens'])}元") return True - + @utils.thread_func("DeepSeek") def DeepSeek(self, player: Player, args: tuple): # 获取用户输入内容 From bf5415cf05c8273a005df8eb769b743587338fcf Mon Sep 17 00:00:00 2001 From: guo <109414536+GuoKsmall@users.noreply.github.com> Date: Thu, 2 Oct 2025 22:03:56 +0800 Subject: [PATCH 4/5] Update __init__.py Update ListenDeath PyRpc --- .../__init__.py" | 111 ++++++++++++------ 1 file changed, 76 insertions(+), 35 deletions(-) diff --git "a/\346\255\273\344\272\241\347\202\271\350\277\224\345\233\236/__init__.py" "b/\346\255\273\344\272\241\347\202\271\350\277\224\345\233\236/__init__.py" index 93407459..1f972533 100644 --- "a/\346\255\273\344\272\241\347\202\271\350\277\224\345\233\236/__init__.py" +++ "b/\346\255\273\344\272\241\347\202\271\350\277\224\345\233\236/__init__.py" @@ -1,65 +1,106 @@ -from tooldelta import Plugin, plugin_entry, Player, Chat, FrameExit, cfg, game_utils, utils, Config, fmts, TYPE_CHECKING +from tooldelta import Plugin, plugin_entry, Player, utils, Config from tooldelta.constants import PacketIDS + class DeathBack(Plugin): name = "死亡点返回" author = "果_k" - version = (0, 0, 2) + version = (0, 0, 3) def __init__(self, frame): super().__init__(frame) CONFIG_DEFAULT = { "取消返回": "§c§l已自动取消返回", + "成功返回提示词": "§a§l已返回死亡点", "等待输入时间": 20, "是否启用标签限制": False, "标签名称": "backdeath", + "返回询问消息": "§a§l你死于 {dim} 维度的 {x} {y} {z} 是否返回?§b输入y同意 输入n拒绝" } CONFIG_STD = { "取消返回": str, + "成功返回提示词": str, "等待输入时间": Config.NNInt, "是否启用标签限制": bool, "标签名称": str, + "返回询问消息": str } - cfg, cfg_version = Config.get_plugin_config_and_version( + cfg, _ = Config.get_plugin_config_and_version( self.name, CONFIG_STD, CONFIG_DEFAULT, self.version ) self.BackInfo = cfg["取消返回"] + self.SuccessInfo = cfg["成功返回提示词"] self.TimeOut = cfg["等待输入时间"] self.TagCheck = cfg["是否启用标签限制"] - self.PlayerTag= cfg["标签名称"] - self.ListenPacket(PacketIDS.Text, self.TextDeath) + self.PlayerTag = cfg["标签名称"] + self.DeathMessage = cfg["返回询问消息"] + self.ListenPacket(PacketIDS.PyRpc, self.on_pyrpc) - def TextDeath(self,packet): - Message = packet['Message'] - if isinstance(Message, str) and "death" in Message.lower(): #筛选触发条件 - self.GetPos(packet['Parameters'][0]) + def on_pyrpc(self, packet): + """使用PyRpc监听死亡事件""" + try: + self._test_die(packet) + except: + pass + return False - def GetPos(self,playername): - players = self.frame.get_players() - player = players.getPlayerByName(playername) - if not player: + def _test_die(self, pk: dict): + values = pk["Value"] + if len(values) != 3: return - dim, x, y, z = player.getPos() - if self.TagCheck: #标签筛选玩家 - backCmd = self.game_ctrl.sendcmd(f"/testfor @a[name={player.name},tag={self.PlayerTag}]",waitForResp=True) - Check = backCmd.OutputMessages[0].Success - if Check == True: - self.AskPlayer(player,dim,x,y,z) - else: + eventType, contents = values[0:2] + if eventType != "ModEventS2C": + return + elif len(contents) != 4: + return + eventName, eventData = contents[2:4] + if eventName != "OnPlayerDie": + return + die = eventData["die"] + # 死亡时为True,重生时为False + if die: + playerUniqueID = int(eventData["pid"]) + player = self.game_ctrl.players.getPlayerByUniqueID(playerUniqueID) + if player is None: return - else: - self.AskPlayer(player,dim,x,y,z) - - def AskPlayer(self,player,dim,x,y,z): - reply = player.input(f"§a§l你死于 {dim} 维度的 {x} {y} {z} 是否返回?§b输入y同意 输入n拒绝",self.TimeOut) - if reply == "y": - player.show("返回") - if dim == 0: #不同维度的判断 - self.game_ctrl.sendwscmd(f"/execute in overworld run tp {player.name} {x} {y} {z}") - elif dim == 1: - self.game_ctrl.sendwscmd(f"/execute in nether run tp {player.name} {x} {y} {z}") - elif dim == 2: - self.game_ctrl.sendwscmd(f"/execute in the_end run tp {player.name} {x} {y} {z}") - else: - player.show(self.BackInfo) + self.on_player_die(player) + + def on_player_die(self, player: Player): + """当玩家死亡时调用""" + try: + dim, x, y, z = player.getPos() + if self.TagCheck: + backCmd = self.game_ctrl.sendcmd(f"/testfor @a[name={player.name},tag={self.PlayerTag}]", waitForResp=True) + Check = backCmd.OutputMessages[0].Success + if Check == True: + self.AskPlayer(player, dim, x, y, z) + else: + self.AskPlayer(player, dim, x, y, z) + except: + pass + + def AskPlayer(self, player, dim, x, y, z): + try: + message = utils.simple_fmt({ + "{dim}": dim, + "{x}": round(x, 2), + "{y}": round(y, 2), + "{z}": round(z, 2) + }, self.DeathMessage) + + reply = player.input(message, self.TimeOut) + if reply == "y": + player.show(self.SuccessInfo) + if dim == 0: + self.game_ctrl.sendwscmd(f"/execute in overworld run tp {player.name} {x} {y} {z}") + elif dim == 1: + self.game_ctrl.sendwscmd(f"/execute in nether run tp {player.name} {x} {y} {z}") + elif dim == 2: + self.game_ctrl.sendwscmd(f"/execute in the_end run tp {player.name} {x} {y} {z}") + else: + self.game_ctrl.sendwscmd(f"/execute in dm{dim} run tp {player.name} {x} {y} {z}") + else: + player.show(self.BackInfo) + except: + pass entry = plugin_entry(DeathBack) From 69dfb942043a711b6a62a8d85b40a74fe93e0049 Mon Sep 17 00:00:00 2001 From: guo <109414536+GuoKsmall@users.noreply.github.com> Date: Thu, 2 Oct 2025 22:06:36 +0800 Subject: [PATCH 5/5] Update ListenDeath PyRpc --- .../datas.json" | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git "a/\346\255\273\344\272\241\347\202\271\350\277\224\345\233\236/datas.json" "b/\346\255\273\344\272\241\347\202\271\350\277\224\345\233\236/datas.json" index 8b0f32ea..64e41cc2 100644 --- "a/\346\255\273\344\272\241\347\202\271\350\277\224\345\233\236/datas.json" +++ "b/\346\255\273\344\272\241\347\202\271\350\277\224\345\233\236/datas.json" @@ -1,6 +1,6 @@ { "author": "果_k", - "version": "0.0.2", + "version": "0.0.3", "plugin-type": "classic", "description": "支持多维度的返回死亡点", "pre-plugins": {},