插件主体部分
创建插件完成后, 可以开始编写插件主类。
Step 1: 导入相关库
先导入插件所需的相关库。
python
from tooldelta import plugin_entry, Plugin, ToolDelta, Player, Chat, FrameExit
from tooldelta.constants import PacketIDS1
2
2
Step 2: 编写插件主类
然后开始编写插件主类:
先编写插件基本信息:插件名,作者名,版本号。
python
class NewPlugin(Plugin):
name = "插件名" # 必须设置插件名
author = "作者名" # 作者名, 可选
version = (0, 0, 1) # 插件版本号, 可选, 是一个三元整数元组1
2
3
4
2
3
4
Step 3: 编写插件初始化方法
然后编写 __init__ 方法用于在读取插件时初始化插件类实例:
python
def __init__(self, frame: ToolDelta):
super().__init__(frame)
# 如果你的插件需要读取配置文件
# 那就在这里编写插件配置文件的读取
# ...1
2
3
4
5
2
3
4
5
Step 4:编写和注册回调事件
编写相应的事件回调:
python
def on_preload(self):
# 在此写获取插件API (self.GetPluginAPI) 方法
# 例如:
# 插件API接口 = self.GetPluginAPI("插件API名")
pass
def on_active(self):
# 会在 ToolDelta 初始化成功 (成功接入服务器) 后执行
print("已与游戏建立连接")
# 发送一条 Minecraft 指令 在游戏里说 hello
self.game_ctrl.sendcmd("/say hello everybody! tooldelta is ok!")
def on_chat(self, chat: Chat):
# 在玩家发言后执行
player = chat.player
msg = chat.msg
player.show(f"你刚刚说: {msg}")
def on_player_join(self, player: Player):
# 在玩家加入后执行
self.game_ctrl.say_to("@a", f"§a§l Hi, @{player.name}")
def on_player_leave(self, player: Player):
# 在玩家退出游戏时执行
self.game_ctrl.say_to("@a", f"§a§l Bye, @{player.name}")
def on_frame_exit(self, evt: FrameExit):
print(f"框架退出或插件重载 状态码={evt.signal} 原因={evt.reason}")
def on_pkt_text(self, packet: dict):
# 接收游戏数据包
# 接收到 Text 数据包后执行
print(packet)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
我们回到 __init__ 方法,
将这些监听回调方法注入到框架主类中:
python
def __init__(self, frame: ToolDelta):
super().__init__(frame)
self.ListenPreload(self.on_preload)
self.ListenActive(self.on_active)
self.ListenPlayerJoin(self.on_player_join)
self.ListenPlayerLeave(self.on_player_leave)
self.ListenChat(self.on_chat)
self.ListenPacket(PacketIDS.Text, self.on_pkt_text)
self.ListenFrameExit(self.on_frame_exit)1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
对于事件监听方法,下方列出了它们的功能和使用方法:
| 监听方法 | 回调类型 | 触发条件 |
|---|---|---|
| ListenPreload | () -> None | 插件读取完成、进入租赁服前 |
| ListenActive | () -> None | 进入租赁服,初始化完成时 |
| ListenPlayerJoin | (Player) -> None | 玩家加入(Text 判据) |
| ListenPlayerPreJoin | (Player) -> None | 玩家预加入(PlayerList 判据) |
| ListenPlayerLeave | (Player) -> None | 玩家退出 |
| ListenChat | (Chat) -> None | 玩家发送聊天栏消息 |
| ListenDeath | (Death) -> None | 玩家死亡 |
| ListenAttack | (Attack) -> None | 玩家击杀玩家 |
| ListenSleep | (Player) -> None | 玩家睡觉 |
| ListenWeather | (int) -> None | 天气变化(1 开始下雨 / 2 开始雷暴 / 3 停止下雨 / 4 停止雷暴) |
| ListenFrameExit | (FrameExit) -> None | 系统退出/重载 |
| ListenPacket | (dict) -> bool | 监听字典数据包,返回代表是否拦截 |
| ListenBytesPacket | (BaseBytesPacket) -> bool | 监听二进制数据包,返回代表是否拦截 |
| ListenInternalBroadcast | (InternalBroadcast) -> Any | 监听框架内广播,回调返回值会回传给发送者 |
所有 Listen* 方法均可选填第二个参数 priority: int = 0,数值越大越先执行。
特殊说明
- ListenPacket 的回调可以在进入租赁服完成初始化之前被执行, 这意味着你无法保证数据包处理回调一定会在 ListenActive 方法之后才被执行。
- 当系统初始化未完成但发生崩溃时,ListenFrameExit 的回调同样也会被执行。
- 如果需要使用 GetPluginAPI,请 不要 将其写在插件类的 __init__ 方法中,可能会因为加载顺序而无法正常获取到接口。
回调参数类型:
Chat:
成员 类型 说明 player Player 玩家实例对象 msg str 消息内容 Player:
成员 类型 说明 name str 玩家名 ... 详细介绍 FrameExit:
成员 类型 说明 signal int 退出原因ID reason str 退出原因 Death:
成员 类型 说明 player Player 死亡的玩家 death_type str 死亡类型 Attack:
成员 类型 说明 origin_player Player 发起攻击的玩家 target_player Player 被击杀的玩家 weapon_name str 使用的武器名称 InternalBroadcast:
成员 类型 说明 evt_name str 广播事件名 data Any 附加数据
事件类的导入
顶层 tooldelta 只导出了 Player、Chat、FrameExit、InternalBroadcast。 如果需要为回调标注 Death、Attack 类型,请从 tooldelta.internal.types 导入:
python
from tooldelta.internal.types import Death, Attack1
事件的广播与插件间通信
除监听框架事件外,插件还可以互相通信:
self.BroadcastEvent(evt: InternalBroadcast) -> list[Any]:向所有监听者广播一个事件,返回收集到的、各回调返回的非None结果列表。self.ListenInternalBroadcast(broadcast_name, cb, priority=0):监听指定名字的广播事件。self.GetPluginAPI(api_name, min_version=(0, 0, 0), force=True):获取其他作为 API 注册的插件实例。
详细用法与完整示例请见 插件间通信与事件广播。
Step 5:创建插件执行入口
python
entry = plugin_entry(NewPlugin)1
完成!
至此,插件框架已经完成了!让我们回顾一下整体代码:
python
from tooldelta import plugin_entry, Plugin, ToolDelta, Player, Chat, FrameExit
from tooldelta.constants import PacketIDS
class NewPlugin(Plugin):
name = "插件名" # 必须设置插件名
author = "作者名" # 作者名, 可选
version = (0, 0, 1) # 插件版本号, 可选, 是一个三元整数元组
def __init__(self, frame: ToolDelta):
super().__init__(frame)
self.ListenPreload(self.on_preload)
self.ListenActive(self.on_active)
self.ListenPlayerJoin(self.on_player_join)
self.ListenPlayerLeave(self.on_player_leave)
self.ListenChat(self.on_chat)
self.ListenPacket(PacketIDS.Text, self.on_pkt_text)
self.ListenFrameExit(self.on_frame_exit)
def on_preload(self):
# 在此写获取插件API (self.GetPluginAPI) 方法
# 例如:
# 插件API接口 = self.GetPluginAPI("插件API名")
pass
def on_active(self):
# 会在 ToolDelta 初始化成功 (成功接入服务器) 后执行
print("已与游戏建立连接")
# 发送一条 Minecraft 指令 在游戏里说 hello
self.game_ctrl.sendcmd("/say hello everybody! tooldelta is ok!")
def on_chat(self, chat: Chat):
# 在玩家发言后执行
player = chat.player
msg = chat.msg
player.show(f"你刚刚说: {msg}")
def on_player_join(self, player: Player):
# 在玩家加入后执行
self.game_ctrl.say_to("@a", f"§a§l Hi, @{player.name}")
def on_player_leave(self, player: Player):
# 在玩家退出游戏时执行
self.game_ctrl.say_to("@a", f"§a§l Bye, @{player.name}")
def on_frame_exit(self, evt: FrameExit):
print(f"框架退出或插件重载 状态码={evt.signal} 原因={evt.reason}")
def on_pkt_text(self, packet: dict):
# 接收游戏数据包
# 接收到 Text 数据包后执行
print(packet)
return False
entry = plugin_entry(NewPlugin)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54