pymud 0.21.3__tar.gz → 0.21.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. {pymud-0.21.3 → pymud-0.21.4}/PKG-INFO +55 -46
  2. {pymud-0.21.3 → pymud-0.21.4}/README.md +54 -45
  3. {pymud-0.21.3 → pymud-0.21.4}/pyproject.toml +1 -1
  4. {pymud-0.21.3 → pymud-0.21.4}/src/pymud/decorators.py +1 -1
  5. {pymud-0.21.3 → pymud-0.21.4}/src/pymud/dialogs.py +5 -5
  6. {pymud-0.21.3 → pymud-0.21.4}/src/pymud/extras.py +102 -58
  7. {pymud-0.21.3 → pymud-0.21.4}/src/pymud/logger.py +1 -1
  8. {pymud-0.21.3 → pymud-0.21.4}/src/pymud/modules.py +2 -2
  9. {pymud-0.21.3 → pymud-0.21.4}/src/pymud/objects.py +3 -4
  10. {pymud-0.21.3 → pymud-0.21.4}/src/pymud/protocol.py +1 -1
  11. {pymud-0.21.3 → pymud-0.21.4}/src/pymud/pymud.py +27 -32
  12. {pymud-0.21.3 → pymud-0.21.4}/src/pymud/session.py +2 -2
  13. {pymud-0.21.3 → pymud-0.21.4}/src/pymud/settings.py +2 -2
  14. {pymud-0.21.3 → pymud-0.21.4}/src/pymud.egg-info/PKG-INFO +55 -46
  15. {pymud-0.21.3 → pymud-0.21.4}/LICENSE.txt +0 -0
  16. {pymud-0.21.3 → pymud-0.21.4}/setup.cfg +0 -0
  17. {pymud-0.21.3 → pymud-0.21.4}/src/pymud/__init__.py +0 -0
  18. {pymud-0.21.3 → pymud-0.21.4}/src/pymud/__main__.py +0 -0
  19. {pymud-0.21.3 → pymud-0.21.4}/src/pymud/i18n.py +0 -0
  20. {pymud-0.21.3 → pymud-0.21.4}/src/pymud/lang/i18n_chs.py +0 -0
  21. {pymud-0.21.3 → pymud-0.21.4}/src/pymud/lang/i18n_eng.py +0 -0
  22. {pymud-0.21.3 → pymud-0.21.4}/src/pymud/main.py +0 -0
  23. {pymud-0.21.3 → pymud-0.21.4}/src/pymud/pkuxkx.py +0 -0
  24. {pymud-0.21.3 → pymud-0.21.4}/src/pymud.egg-info/SOURCES.txt +0 -0
  25. {pymud-0.21.3 → pymud-0.21.4}/src/pymud.egg-info/dependency_links.txt +0 -0
  26. {pymud-0.21.3 → pymud-0.21.4}/src/pymud.egg-info/entry_points.txt +0 -0
  27. {pymud-0.21.3 → pymud-0.21.4}/src/pymud.egg-info/requires.txt +0 -0
  28. {pymud-0.21.3 → pymud-0.21.4}/src/pymud.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pymud
3
- Version: 0.21.3
3
+ Version: 0.21.4
4
4
  Summary: a MUD Client written in Python
5
5
  Author-email: "newstart@pkuxkx" <crapex@hotmail.com>
6
6
  Maintainer-email: "newstart@pkuxkx" <crapex@hotmail.com>
@@ -33,13 +33,13 @@ Dynamic: license-file
33
33
  # PyMUD - Python原生MUD客户端
34
34
  ## 简介
35
35
 
36
- ### 北侠WIKI: https://www.pkuxkx.net/wiki/tools/pymud
37
- ### 源代码地址: https://github.com/crapex/pymud
38
- ### 帮助文档地址: https://pymud.readthedocs.org
39
- ### PyPi项目地址: https://pypi.org/project/pymud
40
- ### 由deepwiki自动生成的项目理解文档地址: https://deepwiki.com/crapex/pymud
41
- ### PyMUD用户shanghua写的入门教程文档:https://www.pkuxkx.net/forum/forum.php?mod=viewthread&tid=49999&forumuid=12067
42
- ### 交流QQ群:554672580
36
+ + 北侠WIKI: https://www.pkuxkx.net/wiki/tools/pymud
37
+ + 源代码地址: https://github.com/crapex/pymud
38
+ + 帮助文档地址: https://pymud.readthedocs.io
39
+ + PyPi项目地址: https://pypi.org/project/pymud
40
+ + 由deepwiki自动生成的项目理解文档地址: https://deepwiki.com/crapex/pymud
41
+ + PyMUD用户shanghua写的入门教程文档:https://www.pkuxkx.net/forum/forum.php?mod=viewthread&tid=49999&forumuid=12067
42
+ + 交流QQ群:554672580
43
43
 
44
44
 
45
45
  ### 北大侠客行Mud (www.pkuxkx.net),最好的中文Mud游戏!
@@ -55,7 +55,7 @@ Dynamic: license-file
55
55
  + 一次脚本开发,多平台运行。只要能在该平台上运行python,就可以运行PyMUD客户端
56
56
  + 脚本所有语法均采用Python原生语法,因此你只要会用Python,就可以自己写脚本,免去了再去学习lua、熟悉各类APP的使用的难处
57
57
  + Python拥有极为强大的文字处理能力,用于处理文本的MUD最为合适
58
- + Python拥有极为丰富的第三方库,能支持的第三方库,就能在PyMud中支持
58
+ + Python拥有极为丰富的第三方库,能支持的第三方库,就能在PyMUD中支持
59
59
  + 我自己还在玩,所以本客户端会持续进行更新:)
60
60
 
61
61
  ### 哪些人适合使用PyMUD
@@ -66,7 +66,15 @@ Dynamic: license-file
66
66
 
67
67
  ## 版本更新信息
68
68
 
69
- ### 0.21.3 2025-06-02)
69
+ ### 0.21.4 (2025-06-08)
70
+
71
+ + 问题修复: 修复了在多行选择碰到空行时,会导致从头开始全选的问题。
72
+ + 问题修复: 修复了从后往前选择时,若再往后选择,会导致起始位置错误的问题。
73
+ + 问题修复: 修复了纯文本行单行部分复制时,最后一个字符无法被选中和复制的问题。
74
+ + 问题修复: 修复了美化模式下,当服务器使用tab对齐在本地显示中无法对齐的问题。为配合对齐,默认tabstop已修改为8。
75
+ + 功能调整: 当上下分屏显示时,上半屏也能显示到最后一行,而不是仅显示到分屏处位置。
76
+
77
+ ### 0.21.3 (2025-06-02)
70
78
 
71
79
  + 问题修复: 修复了当缓存行数超过规定值时,由于代码错误导致清屏的问题。
72
80
  + 功能增强: 增加清除任务时的异常保护,此时产生asyncio.CancelledError异常为正常现象,因此捕获后禁止异常处理。
@@ -75,7 +83,7 @@ Dynamic: license-file
75
83
  ### 0.21.2 (2025-06-01)
76
84
 
77
85
  + 问题修复: 修复了当自动重连启动时,即使会话关闭了,也会自动重连的问题。
78
- + 实现调整: 重写了专用的会话缓冲、记录缓冲与PyMud缓冲显示控制器,在prompt_toolkit的原Buffer和BufferControl的基础仅提供了PYMUD所需的基础功能,以降低内存占用。
86
+ + 实现调整: 重写了专用的会话缓冲、记录缓冲与PyMUD缓冲显示控制器,在prompt_toolkit的原Buffer和BufferControl的基础仅提供了PyMUD所需的基础功能,以降低内存占用。
79
87
  经测试,当前内存基本稳定,视会话数量和脚本情况差异,维持在几百兆左右(500M以下),且不会有大幅波动。重写后,低配置的VPS也可以稳定运行PyMUD。
80
88
 
81
89
  ### 0.21.0 (2025-05-20)
@@ -92,19 +100,19 @@ Dynamic: license-file
92
100
 
93
101
  + 功能新增: 新增了deleteGroup函数,用于删除指定的对象组。可同时指定组名、是否包含子组名、有效类型范围。函数定义和示例代码如下:
94
102
 
95
- ``` Python
96
- def deleteGroup(self, group: str, subgroup = True, types: Union[Type, Union[Tuple, List]] = (Alias, Trigger, Command, Timer, GMCPTrigger)):
97
- pass
98
- # 各参数含义:
99
- # group: 要删除的组名,可以是完整的组名,也可以是部分组名。例如:"group1" 或 "group1.subgroup1" 等。
100
- # subgroup: 是否包含子组。如果为True,则删除指定组及其所有子组的对象。如果为False,则仅删除指定组的对象,不包括子组。
101
- # types: 要删除的对象类型范围。可以是单个类型,也可以是类型的元组或列表。例如:Trigger, Alias, Command, Timer, GMCPTrigger 等。
102
- # 示例代码:
103
- # 删除所有属于group1的Trigger和Alias对象,包括子组如 group1.subgroup1 和 group1.subgroup2 等
104
- self.session.deleteGroup("group1", True, [Trigger, Alias])
105
- # 删除所有属于group1的Trigger对象,但不包括子组
106
- self.session.deleteGroup("group1", False, [Trigger])
107
- ```
103
+ ``` Python
104
+ def deleteGroup(self, group: str, subgroup = True, types: Union[Type, Union[Tuple, List]] = (Alias, Trigger, Command, Timer, GMCPTrigger)):
105
+ pass
106
+ # 各参数含义:
107
+ # group: 要删除的组名,可以是完整的组名,也可以是部分组名。例如:"group1" 或 "group1.subgroup1" 等。
108
+ # subgroup: 是否包含子组。如果为True,则删除指定组及其所有子组的对象。如果为False,则仅删除指定组的对象,不包括子组。
109
+ # types: 要删除的对象类型范围。可以是单个类型,也可以是类型的元组或列表。例如:Trigger, Alias, Command, Timer, GMCPTrigger 等。
110
+ # 示例代码:
111
+ # 删除所有属于group1的Trigger和Alias对象,包括子组如 group1.subgroup1 和 group1.subgroup2 等
112
+ self.session.deleteGroup("group1", True, [Trigger, Alias])
113
+ # 删除所有属于group1的Trigger对象,但不包括子组
114
+ self.session.deleteGroup("group1", False, [Trigger])
115
+ ```
108
116
 
109
117
  + 功能新增: 对 #trigger, #alias, #timer, #gmcp, #command, #t+, #t- 等命令新增了组处理选项,用于对整组对象进行处理。各命令的语法格式类似。
110
118
  处理组时,组名应以大于号>或者等于号=开头,紧跟组名(无空格)。当使用>时,表示操作针对当前组及所有所属子组,当使用=时,表示操作仅针对当前组。
@@ -144,7 +152,7 @@ Dynamic: license-file
144
152
  + 功能新增: #load / #unload 现在支持当前会话对插件的临时启用和禁用,实现方式为调用插件里的PLUGIN_SESSION_CREATE和PLUGIN_SESSION_DESTROYE函数。群文件的moving.py插件写法可以支持。
145
153
  + 功能调整: 各会话变量保存的.mud文件,统一移到save子目录下。原来当前目录下的.mud文件,在对应会话重新加载时会自动移动,无需人工处理。
146
154
  + 功能新增: 增加了国际化(i18n)支持,原生开发语言为中文简体,目前使用AI翻译生成了英文。应用语言通过Settings中新增的language配置来控制,默认为"chs",可以在pymud.cfg中覆盖该配置。其值目前可以为"chs"、"eng"。自行翻译的语言可以在pymud/lang目录下下新增语言文件,文件名为i18n_加语言代码,例如"i18n_chs.py"表示可以使用"chs"语言,其中使用Python字典方式定义了所有需动态显示的文本内容。
147
- + 功能新增: 新增了使用元类型及装饰器来管理Pymud对象,包括Alias, Trigger, Timer, GMCPTrigger四种可以使用对应的装饰器,@alias, @trigger, @timer, @gmcp来直接在标记函数上创建。可以参考本版本中的pkuxkx.py文件写法和注意事项。
155
+ + 功能新增: 新增了使用元类型及装饰器来管理PyMUD对象,包括Alias, Trigger, Timer, GMCPTrigger四种可以使用对应的装饰器,@alias, @trigger, @timer, @gmcp来直接在标记函数上创建。可以参考本版本中的pkuxkx.py文件写法和注意事项。
148
156
  + 功能新增: 新增了两个装饰器,@exception和@async_exception,用于捕获异常并调用session.error进行显示。@exception用于捕获同步异常,@async_exception用于捕获异步异常。参考如下:
149
157
  ``` Python
150
158
  from pymud import Command, Trigger, IConfig, exception, async_exception
@@ -180,7 +188,7 @@ Dynamic: license-file
180
188
  + 功能增强: 对几乎所有函数的参数进行了类型标注,增加了类型检查,提高了代码的可读性和可维护性,也便于自行编写脚本时的提示。
181
189
  + 功能增强: 为Session类型增加了commandHistory属性,用于查询发送到服务器的命令历史。保存的命令历史的数量由pymud.cfg中的client["history_records"]控制,默认为500。当该值为0时,不会保存命令历史。为-1时,会保存所有命令历史。
182
190
  + 功能调整: #help命令时,增加了上下两行分隔符显示,以便明显区分帮助输出和游戏输出。
183
- + 功能增强: 当前pymud界面中显示的版本号会自动从pyproject.toml中读取,以确保版本号的准确性和唯一性。
191
+ + 功能增强: 当前PyMUD界面中显示的版本号会自动从pyproject.toml中读取,以确保版本号的准确性和唯一性。
184
192
  + 问题修复: 修复了代码中的部分编码错误。新版Python中能容忍一些错误,但老版本不行。经修复,当前代码支持的Python版本已测试3.8确保可用。建议使用3.10或更高版本的Python。
185
193
  + 问题修复: 删除了extras.py中多余的MenuItem类型定义,该定义与prompt_toolkit中的MenuItem定义冲突。
186
194
  + 问题修复: 调整了众多代码中未检查对象是否为None即调用、使用的局部变量可能未经过初始化和赋值路径等的情况,保证程序运行的健壮性。
@@ -210,7 +218,7 @@ Dynamic: license-file
210
218
  ### 0.20.1 (2024-11-16)
211
219
  + 功能调整: 会话中触发器匹配实现进行部分调整,减少循环次数以提高响应速度
212
220
  + 功能调整: #test / #show 触发器测试功能调整,现在会对使能的和未使能的触发器均进行匹配测试。其中,#show 命令仅测试,而 #test 命令会导致触发器真正响应。
213
- + 功能新增: pymud对象新增了一个持续运行的1s的周期定时任务。该任务中会刷新页面显示。可以使用 session.application.addTimerTickCallback 和 session.application.removeTimerTickCallback 来注册和解除定时器回调。
221
+ + 功能新增: PyMUD对象新增了一个持续运行的1s的周期定时任务。该任务中会刷新页面显示。可以使用 session.application.addTimerTickCallback 和 session.application.removeTimerTickCallback 来注册和解除定时器回调。
214
222
 
215
223
  ### 0.20.0 (2024-08-25)
216
224
  + 功能调整: 将模块主入口函数从__main__.py中移动到main.py中,以使可以在当前目录下,可直接使用pymud,也可使用python -m pymud启动
@@ -233,16 +241,16 @@ Dynamic: license-file
233
241
  + 问题修复: 修复部分正则表达式书写错误问题
234
242
  + 功能新增: Session类新增waitfor函数,用于执行一段代码后立即等待某个触发器的情况,简化原三行代码写法
235
243
 
236
- ``` Python
237
- # 原来为确保await triggered的任务在输入前等待,有时候需要这么写:
238
- task = self.create_task(self.tri1.triggered())
239
- await asyncio.sleep(0.05)
240
- self.session.writeline('dazuo')
241
- await task
242
-
243
- # 现在可以一句话简写:
244
- await self.session.waitfor('dazuo', self.create_task(self.tri1.triggered()))
245
- ```
244
+ ``` Python
245
+ # 原来为确保await triggered的任务在输入前等待,有时候需要这么写:
246
+ task = self.create_task(self.tri1.triggered())
247
+ await asyncio.sleep(0.05)
248
+ self.session.writeline('dazuo')
249
+ await task
250
+
251
+ # 现在可以一句话简写:
252
+ await self.session.waitfor('dazuo', self.create_task(self.tri1.triggered()))
253
+ ```
246
254
 
247
255
  + 功能调整: Session类的addTriggers等方法接受的dict中,会将对象本身id作为会话处理id。当该id与key不一致时,会同时显示警告。
248
256
  + 功能新增: Session类新增addObject, addObjects, delObject, delObjects用于操作别名、定时器、触发器、GMCP触发器、命令等对象。
@@ -379,7 +387,7 @@ Dynamic: license-file
379
387
  + 功能修改:Timer实现进行修改,以确保一个定时器仅创建一个任务。
380
388
  + 功能调整:Timer对象在复位Session对象时,也同时复位。目的是确保reload时不重新创建定时器任务。
381
389
  + 功能调整:在会话连接时,不再复位Session有关对象信息。该复位活动仅在连接断开时和脚本重新加载时进行。
382
- + 功能调整:启动PYMUD时,会将控制台标题设置为PYMUD+版本号。
390
+ + 功能调整:启动PyMUD时,会将控制台标题设置为PyMUD+版本号。
383
391
  + 问题修复:修复会话特定脚本模块会被其他会话加载的bug。
384
392
  + 问题修复:修复定时器Timer中的bug。
385
393
 
@@ -387,7 +395,7 @@ Dynamic: license-file
387
395
  本版对模块功能进行了整体调整,支持加载/卸载/重载/预加载多个模块,具体内容如下:
388
396
  + 当模块中存在名为Configuration类时,以主模块形式加载,即:自动创建该Configuration类的实例(与原脚本相同)
389
397
  + 当模块中不存在名为Configuration类时,以子模块形式加载,即:仅加载该模块,但不会创建Configuration的实例
390
- + #load命令支持同时加载多个模块,模块名以半角逗号(,)隔开即可。此时按给定的名称顺序逐一加载。如:#load mod1,mod2
398
+ + \#load命令支持同时加载多个模块,模块名以半角逗号(,)隔开即可。此时按给定的名称顺序逐一加载。如:#load mod1,mod2
391
399
  + 增加#unload命令,卸载卸载名称模块,同时卸载多个模块时,模块名以半角逗号(,)隔开即可。卸载时,如果该模块有Configuration类,会自动调用其__del__方法
392
400
  + 修改reload命令功能,当不带参数时,重新加载所有已加载模块,带参数时,首先尝试重新加载指定名称模块,若模块中不存在该名称模块,则重新加载指定名称的插件。若存在同名模块和插件,则仅重新加载插件(建议不要让插件和模块同名)
393
401
  + 增加#modules(简写为#mods)命令,可以列出所有已经加载的模块清单
@@ -438,13 +446,14 @@ Dynamic: license-file
438
446
  + 功能增加:增加#py指令,可以直接在命令行中写代码并执行。执行的上下文环境为当前环境,即self代表当前session。例如,#py self.writeline("xixi")相当于直接在脚本会话中调用发送xixi指令
439
447
  + 功能新增:新增插件(Plugins)功能。将自动读取pymud模块目录的plugins子目录以及当前脚本目录的plugins子目录下的.py文件,若发现遵照插件规范脚本,将自动加载该模块到pymud。可以使用#plugins查看所有被加载的插件,可以直接带参数插件名(如#plugins myplugin)查看插件的详细信息(自动打印插件的__doc__属性,即写在文件最前面的字符串常量)插件文件中必须有以下定义:
440
448
 
441
- |名称|类型|状态|含义|
442
- |-|-|-|-|
443
- |PLUGIN_NAME|str|必须有|插件唯一名称|
444
- |PLUGIN_DESC|dict|必须有|插件描述信息的详情,必要关键字包含VERSION(版本)、AUTHOR(作者)、RELEASE_DATE(发布日期)、DESCRIPTION(简要描述)|
445
- |PLUGIN_PYMUD_START|func(app)|函数定义必须有,函数体可以为空|PYMUD自动读取并加载插件时自动调用的函数, app为PyMudApp(pymud管理类)。该函数仅会在程序运行时,自动加载一次|
446
- |PLUGIN_SESSION_CREATE|func(session)|函数定义必须有,函数体可以为空|在会话中加载插件时自动调用的函数, session为加载插件的会话。该函数在每一个会话创建时均被自动加载一次|
447
- |PLUGIN_SESSION_DESTROY|func(session)|函数定义必须有,函数体可以为空|在会话中卸载插件时自动调用的函数, session为卸载插件的会话。卸载在每一个会话关闭时均被自动运行一次。|
449
+ |名称|类型|状态|含义|
450
+ |-|-|-|-|
451
+ |PLUGIN_NAME|str|必须有|插件唯一名称|
452
+ |PLUGIN_DESC|dict|必须有|插件描述信息的详情,必要关键字包含VERSION(版本)、AUTHOR(作者)、RELEASE_DATE(发布日期)、DESCRIPTION(简要描述)|
453
+ |PLUGIN_PYMUD_START|func(app)|函数定义必须有,函数体可以为空|PyMUD自动读取并加载插件时自动调用的函数, app为PyMudApp(PyMUD管理类)。该函数仅会在程序运行时,自动加载一次|
454
+ |PLUGIN_SESSION_CREATE|func(session)|函数定义必须有,函数体可以为空|在会话中加载插件时自动调用的函数, session为加载插件的会话。该函数在每一个会话创建时均被自动加载一次|
455
+ |PLUGIN_SESSION_DESTROY|func(session)|函数定义必须有,函数体可以为空|在会话中卸载插件时自动调用的函数, session为卸载插件的会话。卸载在每一个会话关闭时均被自动运行一次。|
456
+ |PLUGIN_PYMUD_DESTROY|func(app)|函数定义必须有,函数体可以为空|在PyMUD程序退出时自动调用的函数, app为PyMUD管理类。该函数仅会在程序运行时,自动卸载一次|
448
457
 
449
458
  + 功能修改:对session自动加载mud文件中变量失败时的异常进行管理,此时将不加载变量,自动继续进行
450
459
  + 功能修改:所有匹配类对象的匹配模式patterns支持动态修改,涉及Alias,Trigger,Command。修改方式为直接对其patterns属性赋值。如tri.patterns = aNewPattern
@@ -1,13 +1,13 @@
1
1
  # PyMUD - Python原生MUD客户端
2
2
  ## 简介
3
3
 
4
- ### 北侠WIKI: https://www.pkuxkx.net/wiki/tools/pymud
5
- ### 源代码地址: https://github.com/crapex/pymud
6
- ### 帮助文档地址: https://pymud.readthedocs.org
7
- ### PyPi项目地址: https://pypi.org/project/pymud
8
- ### 由deepwiki自动生成的项目理解文档地址: https://deepwiki.com/crapex/pymud
9
- ### PyMUD用户shanghua写的入门教程文档:https://www.pkuxkx.net/forum/forum.php?mod=viewthread&tid=49999&forumuid=12067
10
- ### 交流QQ群:554672580
4
+ + 北侠WIKI: https://www.pkuxkx.net/wiki/tools/pymud
5
+ + 源代码地址: https://github.com/crapex/pymud
6
+ + 帮助文档地址: https://pymud.readthedocs.io
7
+ + PyPi项目地址: https://pypi.org/project/pymud
8
+ + 由deepwiki自动生成的项目理解文档地址: https://deepwiki.com/crapex/pymud
9
+ + PyMUD用户shanghua写的入门教程文档:https://www.pkuxkx.net/forum/forum.php?mod=viewthread&tid=49999&forumuid=12067
10
+ + 交流QQ群:554672580
11
11
 
12
12
 
13
13
  ### 北大侠客行Mud (www.pkuxkx.net),最好的中文Mud游戏!
@@ -23,7 +23,7 @@
23
23
  + 一次脚本开发,多平台运行。只要能在该平台上运行python,就可以运行PyMUD客户端
24
24
  + 脚本所有语法均采用Python原生语法,因此你只要会用Python,就可以自己写脚本,免去了再去学习lua、熟悉各类APP的使用的难处
25
25
  + Python拥有极为强大的文字处理能力,用于处理文本的MUD最为合适
26
- + Python拥有极为丰富的第三方库,能支持的第三方库,就能在PyMud中支持
26
+ + Python拥有极为丰富的第三方库,能支持的第三方库,就能在PyMUD中支持
27
27
  + 我自己还在玩,所以本客户端会持续进行更新:)
28
28
 
29
29
  ### 哪些人适合使用PyMUD
@@ -34,7 +34,15 @@
34
34
 
35
35
  ## 版本更新信息
36
36
 
37
- ### 0.21.3 2025-06-02)
37
+ ### 0.21.4 (2025-06-08)
38
+
39
+ + 问题修复: 修复了在多行选择碰到空行时,会导致从头开始全选的问题。
40
+ + 问题修复: 修复了从后往前选择时,若再往后选择,会导致起始位置错误的问题。
41
+ + 问题修复: 修复了纯文本行单行部分复制时,最后一个字符无法被选中和复制的问题。
42
+ + 问题修复: 修复了美化模式下,当服务器使用tab对齐在本地显示中无法对齐的问题。为配合对齐,默认tabstop已修改为8。
43
+ + 功能调整: 当上下分屏显示时,上半屏也能显示到最后一行,而不是仅显示到分屏处位置。
44
+
45
+ ### 0.21.3 (2025-06-02)
38
46
 
39
47
  + 问题修复: 修复了当缓存行数超过规定值时,由于代码错误导致清屏的问题。
40
48
  + 功能增强: 增加清除任务时的异常保护,此时产生asyncio.CancelledError异常为正常现象,因此捕获后禁止异常处理。
@@ -43,7 +51,7 @@
43
51
  ### 0.21.2 (2025-06-01)
44
52
 
45
53
  + 问题修复: 修复了当自动重连启动时,即使会话关闭了,也会自动重连的问题。
46
- + 实现调整: 重写了专用的会话缓冲、记录缓冲与PyMud缓冲显示控制器,在prompt_toolkit的原Buffer和BufferControl的基础仅提供了PYMUD所需的基础功能,以降低内存占用。
54
+ + 实现调整: 重写了专用的会话缓冲、记录缓冲与PyMUD缓冲显示控制器,在prompt_toolkit的原Buffer和BufferControl的基础仅提供了PyMUD所需的基础功能,以降低内存占用。
47
55
  经测试,当前内存基本稳定,视会话数量和脚本情况差异,维持在几百兆左右(500M以下),且不会有大幅波动。重写后,低配置的VPS也可以稳定运行PyMUD。
48
56
 
49
57
  ### 0.21.0 (2025-05-20)
@@ -60,19 +68,19 @@
60
68
 
61
69
  + 功能新增: 新增了deleteGroup函数,用于删除指定的对象组。可同时指定组名、是否包含子组名、有效类型范围。函数定义和示例代码如下:
62
70
 
63
- ``` Python
64
- def deleteGroup(self, group: str, subgroup = True, types: Union[Type, Union[Tuple, List]] = (Alias, Trigger, Command, Timer, GMCPTrigger)):
65
- pass
66
- # 各参数含义:
67
- # group: 要删除的组名,可以是完整的组名,也可以是部分组名。例如:"group1" 或 "group1.subgroup1" 等。
68
- # subgroup: 是否包含子组。如果为True,则删除指定组及其所有子组的对象。如果为False,则仅删除指定组的对象,不包括子组。
69
- # types: 要删除的对象类型范围。可以是单个类型,也可以是类型的元组或列表。例如:Trigger, Alias, Command, Timer, GMCPTrigger 等。
70
- # 示例代码:
71
- # 删除所有属于group1的Trigger和Alias对象,包括子组如 group1.subgroup1 和 group1.subgroup2 等
72
- self.session.deleteGroup("group1", True, [Trigger, Alias])
73
- # 删除所有属于group1的Trigger对象,但不包括子组
74
- self.session.deleteGroup("group1", False, [Trigger])
75
- ```
71
+ ``` Python
72
+ def deleteGroup(self, group: str, subgroup = True, types: Union[Type, Union[Tuple, List]] = (Alias, Trigger, Command, Timer, GMCPTrigger)):
73
+ pass
74
+ # 各参数含义:
75
+ # group: 要删除的组名,可以是完整的组名,也可以是部分组名。例如:"group1" 或 "group1.subgroup1" 等。
76
+ # subgroup: 是否包含子组。如果为True,则删除指定组及其所有子组的对象。如果为False,则仅删除指定组的对象,不包括子组。
77
+ # types: 要删除的对象类型范围。可以是单个类型,也可以是类型的元组或列表。例如:Trigger, Alias, Command, Timer, GMCPTrigger 等。
78
+ # 示例代码:
79
+ # 删除所有属于group1的Trigger和Alias对象,包括子组如 group1.subgroup1 和 group1.subgroup2 等
80
+ self.session.deleteGroup("group1", True, [Trigger, Alias])
81
+ # 删除所有属于group1的Trigger对象,但不包括子组
82
+ self.session.deleteGroup("group1", False, [Trigger])
83
+ ```
76
84
 
77
85
  + 功能新增: 对 #trigger, #alias, #timer, #gmcp, #command, #t+, #t- 等命令新增了组处理选项,用于对整组对象进行处理。各命令的语法格式类似。
78
86
  处理组时,组名应以大于号>或者等于号=开头,紧跟组名(无空格)。当使用>时,表示操作针对当前组及所有所属子组,当使用=时,表示操作仅针对当前组。
@@ -112,7 +120,7 @@
112
120
  + 功能新增: #load / #unload 现在支持当前会话对插件的临时启用和禁用,实现方式为调用插件里的PLUGIN_SESSION_CREATE和PLUGIN_SESSION_DESTROYE函数。群文件的moving.py插件写法可以支持。
113
121
  + 功能调整: 各会话变量保存的.mud文件,统一移到save子目录下。原来当前目录下的.mud文件,在对应会话重新加载时会自动移动,无需人工处理。
114
122
  + 功能新增: 增加了国际化(i18n)支持,原生开发语言为中文简体,目前使用AI翻译生成了英文。应用语言通过Settings中新增的language配置来控制,默认为"chs",可以在pymud.cfg中覆盖该配置。其值目前可以为"chs"、"eng"。自行翻译的语言可以在pymud/lang目录下下新增语言文件,文件名为i18n_加语言代码,例如"i18n_chs.py"表示可以使用"chs"语言,其中使用Python字典方式定义了所有需动态显示的文本内容。
115
- + 功能新增: 新增了使用元类型及装饰器来管理Pymud对象,包括Alias, Trigger, Timer, GMCPTrigger四种可以使用对应的装饰器,@alias, @trigger, @timer, @gmcp来直接在标记函数上创建。可以参考本版本中的pkuxkx.py文件写法和注意事项。
123
+ + 功能新增: 新增了使用元类型及装饰器来管理PyMUD对象,包括Alias, Trigger, Timer, GMCPTrigger四种可以使用对应的装饰器,@alias, @trigger, @timer, @gmcp来直接在标记函数上创建。可以参考本版本中的pkuxkx.py文件写法和注意事项。
116
124
  + 功能新增: 新增了两个装饰器,@exception和@async_exception,用于捕获异常并调用session.error进行显示。@exception用于捕获同步异常,@async_exception用于捕获异步异常。参考如下:
117
125
  ``` Python
118
126
  from pymud import Command, Trigger, IConfig, exception, async_exception
@@ -148,7 +156,7 @@
148
156
  + 功能增强: 对几乎所有函数的参数进行了类型标注,增加了类型检查,提高了代码的可读性和可维护性,也便于自行编写脚本时的提示。
149
157
  + 功能增强: 为Session类型增加了commandHistory属性,用于查询发送到服务器的命令历史。保存的命令历史的数量由pymud.cfg中的client["history_records"]控制,默认为500。当该值为0时,不会保存命令历史。为-1时,会保存所有命令历史。
150
158
  + 功能调整: #help命令时,增加了上下两行分隔符显示,以便明显区分帮助输出和游戏输出。
151
- + 功能增强: 当前pymud界面中显示的版本号会自动从pyproject.toml中读取,以确保版本号的准确性和唯一性。
159
+ + 功能增强: 当前PyMUD界面中显示的版本号会自动从pyproject.toml中读取,以确保版本号的准确性和唯一性。
152
160
  + 问题修复: 修复了代码中的部分编码错误。新版Python中能容忍一些错误,但老版本不行。经修复,当前代码支持的Python版本已测试3.8确保可用。建议使用3.10或更高版本的Python。
153
161
  + 问题修复: 删除了extras.py中多余的MenuItem类型定义,该定义与prompt_toolkit中的MenuItem定义冲突。
154
162
  + 问题修复: 调整了众多代码中未检查对象是否为None即调用、使用的局部变量可能未经过初始化和赋值路径等的情况,保证程序运行的健壮性。
@@ -178,7 +186,7 @@
178
186
  ### 0.20.1 (2024-11-16)
179
187
  + 功能调整: 会话中触发器匹配实现进行部分调整,减少循环次数以提高响应速度
180
188
  + 功能调整: #test / #show 触发器测试功能调整,现在会对使能的和未使能的触发器均进行匹配测试。其中,#show 命令仅测试,而 #test 命令会导致触发器真正响应。
181
- + 功能新增: pymud对象新增了一个持续运行的1s的周期定时任务。该任务中会刷新页面显示。可以使用 session.application.addTimerTickCallback 和 session.application.removeTimerTickCallback 来注册和解除定时器回调。
189
+ + 功能新增: PyMUD对象新增了一个持续运行的1s的周期定时任务。该任务中会刷新页面显示。可以使用 session.application.addTimerTickCallback 和 session.application.removeTimerTickCallback 来注册和解除定时器回调。
182
190
 
183
191
  ### 0.20.0 (2024-08-25)
184
192
  + 功能调整: 将模块主入口函数从__main__.py中移动到main.py中,以使可以在当前目录下,可直接使用pymud,也可使用python -m pymud启动
@@ -201,16 +209,16 @@
201
209
  + 问题修复: 修复部分正则表达式书写错误问题
202
210
  + 功能新增: Session类新增waitfor函数,用于执行一段代码后立即等待某个触发器的情况,简化原三行代码写法
203
211
 
204
- ``` Python
205
- # 原来为确保await triggered的任务在输入前等待,有时候需要这么写:
206
- task = self.create_task(self.tri1.triggered())
207
- await asyncio.sleep(0.05)
208
- self.session.writeline('dazuo')
209
- await task
210
-
211
- # 现在可以一句话简写:
212
- await self.session.waitfor('dazuo', self.create_task(self.tri1.triggered()))
213
- ```
212
+ ``` Python
213
+ # 原来为确保await triggered的任务在输入前等待,有时候需要这么写:
214
+ task = self.create_task(self.tri1.triggered())
215
+ await asyncio.sleep(0.05)
216
+ self.session.writeline('dazuo')
217
+ await task
218
+
219
+ # 现在可以一句话简写:
220
+ await self.session.waitfor('dazuo', self.create_task(self.tri1.triggered()))
221
+ ```
214
222
 
215
223
  + 功能调整: Session类的addTriggers等方法接受的dict中,会将对象本身id作为会话处理id。当该id与key不一致时,会同时显示警告。
216
224
  + 功能新增: Session类新增addObject, addObjects, delObject, delObjects用于操作别名、定时器、触发器、GMCP触发器、命令等对象。
@@ -347,7 +355,7 @@
347
355
  + 功能修改:Timer实现进行修改,以确保一个定时器仅创建一个任务。
348
356
  + 功能调整:Timer对象在复位Session对象时,也同时复位。目的是确保reload时不重新创建定时器任务。
349
357
  + 功能调整:在会话连接时,不再复位Session有关对象信息。该复位活动仅在连接断开时和脚本重新加载时进行。
350
- + 功能调整:启动PYMUD时,会将控制台标题设置为PYMUD+版本号。
358
+ + 功能调整:启动PyMUD时,会将控制台标题设置为PyMUD+版本号。
351
359
  + 问题修复:修复会话特定脚本模块会被其他会话加载的bug。
352
360
  + 问题修复:修复定时器Timer中的bug。
353
361
 
@@ -355,7 +363,7 @@
355
363
  本版对模块功能进行了整体调整,支持加载/卸载/重载/预加载多个模块,具体内容如下:
356
364
  + 当模块中存在名为Configuration类时,以主模块形式加载,即:自动创建该Configuration类的实例(与原脚本相同)
357
365
  + 当模块中不存在名为Configuration类时,以子模块形式加载,即:仅加载该模块,但不会创建Configuration的实例
358
- + #load命令支持同时加载多个模块,模块名以半角逗号(,)隔开即可。此时按给定的名称顺序逐一加载。如:#load mod1,mod2
366
+ + \#load命令支持同时加载多个模块,模块名以半角逗号(,)隔开即可。此时按给定的名称顺序逐一加载。如:#load mod1,mod2
359
367
  + 增加#unload命令,卸载卸载名称模块,同时卸载多个模块时,模块名以半角逗号(,)隔开即可。卸载时,如果该模块有Configuration类,会自动调用其__del__方法
360
368
  + 修改reload命令功能,当不带参数时,重新加载所有已加载模块,带参数时,首先尝试重新加载指定名称模块,若模块中不存在该名称模块,则重新加载指定名称的插件。若存在同名模块和插件,则仅重新加载插件(建议不要让插件和模块同名)
361
369
  + 增加#modules(简写为#mods)命令,可以列出所有已经加载的模块清单
@@ -406,13 +414,14 @@
406
414
  + 功能增加:增加#py指令,可以直接在命令行中写代码并执行。执行的上下文环境为当前环境,即self代表当前session。例如,#py self.writeline("xixi")相当于直接在脚本会话中调用发送xixi指令
407
415
  + 功能新增:新增插件(Plugins)功能。将自动读取pymud模块目录的plugins子目录以及当前脚本目录的plugins子目录下的.py文件,若发现遵照插件规范脚本,将自动加载该模块到pymud。可以使用#plugins查看所有被加载的插件,可以直接带参数插件名(如#plugins myplugin)查看插件的详细信息(自动打印插件的__doc__属性,即写在文件最前面的字符串常量)插件文件中必须有以下定义:
408
416
 
409
- |名称|类型|状态|含义|
410
- |-|-|-|-|
411
- |PLUGIN_NAME|str|必须有|插件唯一名称|
412
- |PLUGIN_DESC|dict|必须有|插件描述信息的详情,必要关键字包含VERSION(版本)、AUTHOR(作者)、RELEASE_DATE(发布日期)、DESCRIPTION(简要描述)|
413
- |PLUGIN_PYMUD_START|func(app)|函数定义必须有,函数体可以为空|PYMUD自动读取并加载插件时自动调用的函数, app为PyMudApp(pymud管理类)。该函数仅会在程序运行时,自动加载一次|
414
- |PLUGIN_SESSION_CREATE|func(session)|函数定义必须有,函数体可以为空|在会话中加载插件时自动调用的函数, session为加载插件的会话。该函数在每一个会话创建时均被自动加载一次|
415
- |PLUGIN_SESSION_DESTROY|func(session)|函数定义必须有,函数体可以为空|在会话中卸载插件时自动调用的函数, session为卸载插件的会话。卸载在每一个会话关闭时均被自动运行一次。|
417
+ |名称|类型|状态|含义|
418
+ |-|-|-|-|
419
+ |PLUGIN_NAME|str|必须有|插件唯一名称|
420
+ |PLUGIN_DESC|dict|必须有|插件描述信息的详情,必要关键字包含VERSION(版本)、AUTHOR(作者)、RELEASE_DATE(发布日期)、DESCRIPTION(简要描述)|
421
+ |PLUGIN_PYMUD_START|func(app)|函数定义必须有,函数体可以为空|PyMUD自动读取并加载插件时自动调用的函数, app为PyMudApp(PyMUD管理类)。该函数仅会在程序运行时,自动加载一次|
422
+ |PLUGIN_SESSION_CREATE|func(session)|函数定义必须有,函数体可以为空|在会话中加载插件时自动调用的函数, session为加载插件的会话。该函数在每一个会话创建时均被自动加载一次|
423
+ |PLUGIN_SESSION_DESTROY|func(session)|函数定义必须有,函数体可以为空|在会话中卸载插件时自动调用的函数, session为卸载插件的会话。卸载在每一个会话关闭时均被自动运行一次。|
424
+ |PLUGIN_PYMUD_DESTROY|func(app)|函数定义必须有,函数体可以为空|在PyMUD程序退出时自动调用的函数, app为PyMUD管理类。该函数仅会在程序运行时,自动卸载一次|
416
425
 
417
426
  + 功能修改:对session自动加载mud文件中变量失败时的异常进行管理,此时将不加载变量,自动继续进行
418
427
  + 功能修改:所有匹配类对象的匹配模式patterns支持动态修改,涉及Alias,Trigger,Command。修改方式为直接对其patterns属性赋值。如tri.patterns = aNewPattern
@@ -1,7 +1,7 @@
1
1
  [project]
2
2
 
3
3
  name = "pymud" # Required
4
- version = "0.21.3" # Required
4
+ version = "0.21.4" # Required
5
5
  description = "a MUD Client written in Python" # Optional
6
6
  readme = "README.md" # Optional
7
7
  requires-python = ">=3.8"
@@ -1,4 +1,4 @@
1
- import sys, functools, traceback
1
+ import functools, traceback
2
2
  from typing import Union, Optional, List
3
3
 
4
4
  def print_exception(session, e: Exception):
@@ -1,9 +1,9 @@
1
1
  import asyncio, webbrowser
2
- from typing import Any, Callable, Iterable, List, Tuple, Union
3
- from prompt_toolkit.layout import AnyContainer, ConditionalContainer, Float, VSplit, HSplit, Window, WindowAlign, ScrollablePane, ScrollOffsets
4
- from prompt_toolkit.widgets import Button, Dialog, Label, MenuContainer, MenuItem, TextArea, SystemToolbar, Frame, RadioList
5
- from prompt_toolkit.layout.dimension import Dimension, D
6
- from prompt_toolkit import ANSI, HTML
2
+ from typing import Any
3
+ from prompt_toolkit.layout import AnyContainer, VSplit, HSplit, Window, WindowAlign
4
+ from prompt_toolkit.widgets import Dialog, Label, TextArea, Frame, RadioList
5
+ from prompt_toolkit.layout.dimension import D
6
+ from prompt_toolkit import HTML
7
7
  from prompt_toolkit.mouse_events import MouseEvent, MouseEventType
8
8
  from prompt_toolkit.formatted_text import FormattedText, AnyFormattedText
9
9
  from prompt_toolkit.application.current import get_app
@@ -1,39 +1,24 @@
1
1
  # External Libraries
2
- from functools import lru_cache
3
2
  from unicodedata import east_asian_width
4
3
  from wcwidth import wcwidth
5
4
  from dataclasses import dataclass
6
- import time, re, logging, linecache, os
5
+ import time, re, linecache, os
7
6
  from typing import Optional, List, Dict
8
- from typing import Iterable, NamedTuple, Optional, Union, Tuple
7
+ from typing import Iterable, Optional, Tuple
9
8
  from prompt_toolkit import ANSI
10
9
  from prompt_toolkit.application import get_app
11
- from prompt_toolkit.buffer import Buffer
12
- from prompt_toolkit.formatted_text import to_formatted_text, fragment_list_to_text
10
+ from prompt_toolkit.formatted_text import to_formatted_text
13
11
  from prompt_toolkit.formatted_text.base import OneStyleAndTextTuple
14
12
  from prompt_toolkit.layout.controls import UIContent, UIControl
15
- from prompt_toolkit.layout.processors import Processor, Transformation
16
13
  from prompt_toolkit.application.current import get_app
17
- from prompt_toolkit.buffer import Buffer
18
- from prompt_toolkit.document import Document
19
14
  from prompt_toolkit.data_structures import Point
20
15
  from prompt_toolkit.layout.controls import UIContent, FormattedTextControl
21
- from prompt_toolkit.lexers import Lexer
22
16
  from prompt_toolkit.mouse_events import MouseButton, MouseEvent, MouseEventType
23
- from prompt_toolkit.selection import SelectionType
24
- from prompt_toolkit.buffer import Buffer, ValidationState
25
- from prompt_toolkit.utils import Event
26
17
 
27
- from prompt_toolkit.filters import (
28
- FilterOrBool,
29
- )
30
18
  from prompt_toolkit.formatted_text import (
31
19
  StyleAndTextTuples,
32
20
  to_formatted_text,
33
21
  )
34
- from prompt_toolkit.formatted_text.utils import fragment_list_to_text
35
- from prompt_toolkit.history import InMemoryHistory
36
- from prompt_toolkit.key_binding.key_bindings import KeyBindingsBase
37
22
  from prompt_toolkit.layout.containers import (
38
23
  Window,
39
24
  WindowAlign,
@@ -42,12 +27,6 @@ from prompt_toolkit.layout.controls import (
42
27
 
43
28
  FormattedTextControl,
44
29
  )
45
- from prompt_toolkit.layout.processors import (
46
- Processor,
47
- TransformationInput,
48
- Transformation
49
- )
50
- from prompt_toolkit.lexers import Lexer
51
30
  from prompt_toolkit.mouse_events import MouseEvent, MouseEventType
52
31
  from prompt_toolkit.utils import get_cwidth
53
32
  from prompt_toolkit.widgets import Button, MenuContainer, MenuItem
@@ -56,7 +35,6 @@ from prompt_toolkit.widgets.base import Border
56
35
  from prompt_toolkit.layout.screen import _CHAR_CACHE, Screen, WritePosition
57
36
  from prompt_toolkit.layout.utils import explode_text_fragments
58
37
  from prompt_toolkit.formatted_text.utils import (
59
- fragment_list_to_text,
60
38
  fragment_list_width,
61
39
  )
62
40
 
@@ -236,7 +214,6 @@ class VSplitWindow(Window):
236
214
  upper = (total - 1) // 2
237
215
  below = total - upper - 1
238
216
 
239
- #if isNotMargin:
240
217
  if isinstance(self.content, PyMudBufferControl):
241
218
  b = self.content.buffer
242
219
  if not b:
@@ -261,7 +238,6 @@ class VSplitWindow(Window):
261
238
 
262
239
  else:
263
240
  # 若内容行数大于屏幕行数,则倒序复制,确保即使有自动折行时,最后一行也保持在屏幕最底部
264
-
265
241
  y = total
266
242
  lineno = line_count
267
243
 
@@ -273,13 +249,10 @@ class VSplitWindow(Window):
273
249
  line = ui_content.get_line(lineno)
274
250
  visible_line_to_row_col[y] = (lineno, horizontal_scroll)
275
251
  copy_line(line, lineno, 0, y, is_input=True)
276
-
277
252
 
278
253
  else:
279
254
  # 有split window
280
255
 
281
-
282
-
283
256
  # 先复制下半部分,倒序复制,确保即使有自动折行时,最后一行也保持在屏幕最底部
284
257
  y = total
285
258
  lineno = line_count
@@ -298,7 +271,7 @@ class VSplitWindow(Window):
298
271
  # 复制上半部分,正序复制,确保即使有自动折行时,第一行也保持在屏幕最顶部
299
272
  y = -vertical_scroll_2
300
273
  lineno = start_lineno
301
- while y <= below and lineno < line_count:
274
+ while y < below and lineno < line_count:
302
275
  line = ui_content.get_line(lineno)
303
276
  visible_line_to_row_col[y] = (lineno, horizontal_scroll)
304
277
  x = 0
@@ -385,7 +358,7 @@ class VSplitWindow(Window):
385
358
  if not b:
386
359
  return
387
360
  start_lineno = b.start_lineno
388
- if (start_lineno >= 0) and (start_lineno < b.lineCount - len(info.displayed_lines)):
361
+ if (start_lineno >= 0) and (start_lineno < b.lineCount - (len(info.displayed_lines) - 1) // 2):
389
362
  b.start_lineno = b.start_lineno + 1
390
363
  else:
391
364
  b.start_lineno = -1
@@ -409,7 +382,8 @@ class VSplitWindow(Window):
409
382
  b.start_lineno = 0
410
383
 
411
384
  else:
412
- b.start_lineno = b.lineCount - len(info.displayed_lines) - 1
385
+ b.start_lineno = b.lineCount - (len(info.displayed_lines) - 1) // 2
386
+
413
387
 
414
388
 
415
389
  class EasternButton(Button):
@@ -532,14 +506,14 @@ class SessionSelectionState:
532
506
  if self.start_row >= 0 and self.end_row >= 0 and self.start_col >= 0 and self.end_col >= 0:
533
507
  if (self.start_row == self.end_row) and (self.start_col == self.end_col):
534
508
  return False
535
- elif self.start_row > self.end_row:
536
- srow, scol = self.end_row, self.end_col
537
- erow, ecol = self.start_row, self.start_col
538
- self.start_row, self.end_row = srow, erow
539
- self.start_col, self.end_col = scol, ecol
540
- elif self.start_row == self.end_row and self.start_col > self.end_col:
541
- scol, ecol = self.end_col, self.start_col
542
- self.start_col, self.end_col = scol, ecol
509
+ # elif self.start_row > self.end_row:
510
+ # srow, scol = self.end_row, self.end_col
511
+ # erow, ecol = self.start_row, self.start_col
512
+ # self.start_row, self.end_row = srow, erow
513
+ # self.start_col, self.end_col = scol, ecol
514
+ # elif self.start_row == self.end_row and self.start_col > self.end_col:
515
+ # scol, ecol = self.end_col, self.start_col
516
+ # self.start_col, self.end_col = scol, ecol
543
517
 
544
518
  return True
545
519
 
@@ -548,10 +522,50 @@ class SessionSelectionState:
548
522
  @property
549
523
  def rows(self):
550
524
  if self.is_valid():
551
- return self.end_row - self.start_row + 1
525
+ return abs(self.end_row - self.start_row) + 1
552
526
  else:
553
527
  return 0
554
528
 
529
+ @property
530
+ def actual_start_row(self):
531
+ if self.is_valid():
532
+ if self.start_row <= self.end_row:
533
+ return self.start_row
534
+ else:
535
+ return self.end_row
536
+
537
+ return -1
538
+
539
+ @property
540
+ def actual_start_col(self):
541
+ if self.is_valid():
542
+ if self.start_row <= self.end_row:
543
+ return self.start_col
544
+ else:
545
+ return self.end_col
546
+
547
+ return -1
548
+
549
+ @property
550
+ def actual_end_row(self):
551
+ if self.is_valid():
552
+ if self.start_row <= self.end_row:
553
+ return self.end_row
554
+ else:
555
+ return self.start_row
556
+
557
+ return -1
558
+
559
+ @property
560
+ def actual_end_col(self):
561
+ if self.is_valid():
562
+ if self.start_row <= self.end_row:
563
+ return self.end_col
564
+ else:
565
+ return self.start_col
566
+
567
+ return -1
568
+
555
569
 
556
570
  class BufferBase:
557
571
  def __init__(self, name, newline = "\n", max_buffered_lines = 10000) -> None:
@@ -561,6 +575,8 @@ class BufferBase:
561
575
  self.start_lineno = -1
562
576
  self.selection = SessionSelectionState(-1, -1, -1, -1)
563
577
 
578
+ self.mouse_point = Point(-1, -1)
579
+
564
580
  def clear(self):
565
581
  pass
566
582
 
@@ -576,12 +592,13 @@ class BufferBase:
576
592
  def selection_range_at_line(self, lineno: int) -> Optional[Tuple[int, int]]:
577
593
  if self.selection.is_valid():
578
594
  if self.selection.rows > 1:
579
- if lineno == self.selection.start_row:
580
- return (self.selection.start_col, len(self.getLine(lineno)) - 1)
581
- elif lineno == self.selection.end_row:
582
- return (0, self.selection.end_col)
583
- elif lineno > self.selection.start_row and lineno < self.selection.end_row:
584
- return (0, len(self.getLine(lineno)) - 1)
595
+
596
+ if lineno == self.selection.actual_start_row:
597
+ return (self.selection.actual_start_col, len(self.getLine(lineno)))
598
+ elif lineno == self.selection.actual_end_row:
599
+ return (0, self.selection.actual_end_col)
600
+ elif lineno > self.selection.actual_start_row and lineno < self.selection.actual_end_row:
601
+ return (0, len(self.getLine(lineno)))
585
602
 
586
603
  elif self.selection.rows == 1:
587
604
  if lineno == self.selection.start_row:
@@ -705,8 +722,9 @@ class PyMudBufferControl(UIControl):
705
722
  self.buffer = buffer
706
723
 
707
724
  # 为MUD显示进行校正的处理,包括对齐校正,换行颜色校正等
708
- self.FULL_BLOCKS = set("▂▃▅▆▇▄█")
709
- self.SINGLE_LINES = set("┌└├┬┼┴╭╰─")
725
+ self.FULL_BLOCKS = set("▂▃▅▆▇▄█━◇◆")
726
+ self.SINGLE_LINES = set("┠┌└├┬┼┴╭╰─")
727
+ self.SINGLE_LINES_LEFT = set("┨┘┐┤")
710
728
  self.DOUBLE_LINES = set("╔╚╠╦╪╩═")
711
729
  self.ALL_COLOR_REGX = re.compile(r"(?:\[[\d;]+m)+")
712
730
  self.AVAI_COLOR_REGX = re.compile(r"(?:\[[\d;]+m)+(?!$)")
@@ -732,17 +750,32 @@ class PyMudBufferControl(UIControl):
732
750
 
733
751
  def width_correction(self, line: str) -> str:
734
752
  new_str = []
735
- for ch in line:
736
- new_str.append(ch)
753
+ for idx, ch in enumerate(line):
754
+
737
755
  if (east_asian_width(ch) in "FWA") and (wcwidth(ch) == 1):
756
+
738
757
  if ch in self.FULL_BLOCKS:
739
- new_str.append(ch)
758
+ new_str.append(ch * 2)
740
759
  elif ch in self.SINGLE_LINES:
760
+ new_str.append(ch)
741
761
  new_str.append("─")
742
762
  elif ch in self.DOUBLE_LINES:
763
+ new_str.append(ch)
743
764
  new_str.append("═")
765
+ elif ch in self.SINGLE_LINES_LEFT:
766
+ new_str.append("─")
767
+ new_str.append(ch)
768
+ elif idx == len(line) - 1:
769
+ new_str.append(" ")
770
+ new_str.append(ch)
744
771
  else:
772
+ new_str.append(ch)
745
773
  new_str.append(' ')
774
+ else:
775
+ new_str.append(ch)
776
+
777
+ # 添加一个不可见字符,用于允许选择行时选到最后一个字符
778
+ new_str.append("\u200B")
746
779
 
747
780
  return "".join(new_str)
748
781
 
@@ -750,18 +783,27 @@ class PyMudBufferControl(UIControl):
750
783
  return line.replace("\r", "").replace("\x00", "")
751
784
 
752
785
  def tab_correction(self, line: str):
753
- return line.replace("\t", " " * Settings.client["tabstop"])
786
+ from .session import Session
787
+ while "\t" in line:
788
+ tab_index = line.find("\t")
789
+ left, right = line[:tab_index], line[tab_index + 1:]
790
+ left_width = get_cwidth(Session.PLAIN_TEXT_REGX.sub("", left))
791
+ tab_width = Settings.client["tabstop"] - (left_width % Settings.client["tabstop"])
792
+ line = left + " " * tab_width + right
793
+
794
+ return line
754
795
 
755
796
  def line_correction(self, line: str):
756
797
  # 处理\r符号(^M)
757
798
  line = self.return_correction(line)
758
- # 处理Tab(\r)符号(^I)
759
- line = self.tab_correction(line)
760
799
 
761
800
  # 美化(解决中文英文在Console中不对齐的问题)
762
801
  if Settings.client["beautify"]:
763
802
  line = self.width_correction(line)
764
803
 
804
+ # 处理Tab(\r)符号(^I)对齐
805
+ line = self.tab_correction(line)
806
+
765
807
  return line
766
808
 
767
809
  def create_content(self, width: int, height: int) -> UIContent:
@@ -876,6 +918,7 @@ class PyMudBufferControl(UIControl):
876
918
 
877
919
  if buffer:
878
920
  # Set the selection position.
921
+ buffer.mouse_point = position
879
922
  if mouse_event.event_type == MouseEventType.MOUSE_DOWN:
880
923
  buffer.exit_selection()
881
924
  buffer.selection.start_row = position.y
@@ -886,9 +929,10 @@ class PyMudBufferControl(UIControl):
886
929
  and mouse_event.button == MouseButton.LEFT
887
930
  ):
888
931
  # Click and drag to highlight a selection
889
- if buffer.selection.start_row >= 0:
932
+ if buffer.selection.start_row >= 0 and not (position.y == 0 and position.x == 0):
890
933
  buffer.selection.end_row = position.y
891
934
  buffer.selection.end_col = position.x
935
+
892
936
 
893
937
  elif mouse_event.event_type == MouseEventType.MOUSE_UP:
894
938
  # When the cursor was moved to another place, select the text.
@@ -897,7 +941,7 @@ class PyMudBufferControl(UIControl):
897
941
  # the cursor can never be after the text, so the cursor
898
942
  # will be repositioned automatically.)
899
943
 
900
- if buffer.selection.start_row >= 0:
944
+ if buffer.selection.start_row >= 0 and position.y >= 0:
901
945
  buffer.selection.end_row = position.y
902
946
  buffer.selection.end_col = position.x
903
947
 
@@ -1,4 +1,4 @@
1
- import os, re, datetime, threading, pathlib
1
+ import datetime, threading
2
2
  from queue import SimpleQueue, Empty
3
3
  from pathlib import Path
4
4
  from .settings import Settings
@@ -1,9 +1,9 @@
1
1
 
2
- import importlib, importlib.util, traceback
2
+ import importlib, importlib.util
3
3
  from typing import Any
4
4
  from .settings import Settings
5
5
  from .extras import DotDict
6
- from .decorators import exception, async_exception, PymudDecorator, print_exception
6
+ from .decorators import PymudDecorator, print_exception
7
7
 
8
8
  class PymudMeta(type):
9
9
  def __new__(cls, name, bases, attrs):
@@ -2,14 +2,13 @@
2
2
  MUD会话(session)中, 支持的对象列表
3
3
  """
4
4
 
5
- import asyncio, logging, re, importlib
6
- from abc import ABC, ABCMeta, abstractmethod
7
- from typing import Optional, Union, List, Dict, Tuple
5
+ import asyncio, logging, re
6
+ from typing import Union, List, Tuple
8
7
  from collections.abc import Iterable
9
8
  from collections import namedtuple
10
9
  from typing import Any
11
10
  from .settings import Settings
12
- from .decorators import exception, async_exception, print_exception
11
+ from .decorators import print_exception
13
12
 
14
13
  class CodeLine:
15
14
  """
@@ -1,4 +1,4 @@
1
- import logging, asyncio, datetime, traceback
1
+ import logging, datetime
2
2
  from asyncio import BaseTransport, Protocol
3
3
  from .settings import Settings
4
4
 
@@ -1,15 +1,13 @@
1
- import asyncio, functools, re, os, webbrowser, threading
1
+ import asyncio, functools, os, webbrowser, threading
2
2
  from datetime import datetime
3
- from pathlib import Path
4
- from prompt_toolkit.shortcuts import set_title, radiolist_dialog
3
+ from prompt_toolkit.shortcuts import set_title
5
4
  from prompt_toolkit.output import ColorDepth
6
5
  from prompt_toolkit.clipboard.pyperclip import PyperclipClipboard
7
6
  from prompt_toolkit import HTML
8
7
  from prompt_toolkit.buffer import Buffer
9
8
  from prompt_toolkit.application import Application
10
- from prompt_toolkit.filters import Condition
11
- from prompt_toolkit.key_binding import KeyBindings
12
- from prompt_toolkit.layout import ConditionalContainer, Float, VSplit, HSplit, Window, WindowAlign, ScrollbarMargin, NumberedMargin, to_dimension
9
+ from prompt_toolkit.filters import Condition, is_true, to_filter
10
+ from prompt_toolkit.layout import ConditionalContainer, Float, VSplit, HSplit, Window, WindowAlign
13
11
  from prompt_toolkit.layout.layout import Layout
14
12
  from prompt_toolkit.layout.controls import FormattedTextControl
15
13
  from prompt_toolkit.layout.dimension import D
@@ -18,26 +16,14 @@ from prompt_toolkit.styles import Style
18
16
  from prompt_toolkit.widgets import Label, TextArea
19
17
  from prompt_toolkit.mouse_events import MouseEvent, MouseEventType
20
18
  from prompt_toolkit.cursor_shapes import CursorShape
21
- from prompt_toolkit.key_binding import KeyPress, KeyPressEvent
19
+ from prompt_toolkit.key_binding import KeyBindings, KeyPress, KeyPressEvent
22
20
  from prompt_toolkit.keys import Keys
23
- from prompt_toolkit.filters import (
24
- Condition,
25
- is_true,
26
- to_filter,
27
- )
28
- from prompt_toolkit.formatted_text import (
29
- Template,
30
- )
31
- from prompt_toolkit.layout.processors import (
32
- DisplayMultipleCursors,
33
- HighlightSearchProcessor,
34
- HighlightSelectionProcessor,
35
- )
21
+ from prompt_toolkit.formatted_text import Template
36
22
  from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
37
- from wcwidth import wcwidth, wcswidth
23
+ from wcwidth import wcswidth
38
24
 
39
25
  from .objects import CodeBlock
40
- from .extras import BufferBase, LogFileBuffer, SessionBuffer, PyMudBufferControl, EasternMenuContainer, VSplitWindow, DotDict, MenuItem
26
+ from .extras import BufferBase, LogFileBuffer, PyMudBufferControl, EasternMenuContainer, VSplitWindow, DotDict, MenuItem
41
27
  from .modules import Plugin
42
28
  from .session import Session
43
29
  from .settings import Settings
@@ -516,16 +502,17 @@ class PyMudApp:
516
502
  b = self.consoleView.buffer
517
503
  if b and b.selection.is_valid():
518
504
  if not raw:
519
- if b.selection.start_row == b.selection.end_row:
520
- if b.selection.end_col - b.selection.start_col == len(b.getLine(b.selection.start_row)):
505
+ #if b.selection.start_row == b.selection.end_row:
506
+ if b.selection.rows == 1:
507
+ if b.selection.actual_end_col - b.selection.actual_start_col >= len(b.getLine(b.selection.start_row)):
521
508
  # 单行且选中了整行,此时不校正显示位置匹配
522
- line = b.getLine(b.selection.start_row)
509
+ line = b.getLine(b.selection.actual_start_row)
523
510
  else:
524
511
  # 单行且选中了部分内容,此时校正显示位置匹配
525
- line = self.consoleView.line_correction(b.getLine(b.selection.start_row))
512
+ line = self.consoleView.line_correction(b.getLine(b.selection.actual_start_row))
526
513
 
527
- start = max(0, b.selection.start_col)
528
- end = min(len(line), b.selection.end_col)
514
+ start = max(0, b.selection.actual_start_col)
515
+ end = min(len(line) + 1, b.selection.actual_end_col)
529
516
  line_plain = Session.PLAIN_TEXT_REGX.sub("", line).replace("\r", "").replace("\x00", "")
530
517
  selection = line_plain[start:end]
531
518
  self.app.clipboard.set_text(selection)
@@ -535,7 +522,7 @@ class PyMudApp:
535
522
  else:
536
523
  # 多行只认行
537
524
  lines = []
538
- for row in range(b.selection.start_row, b.selection.end_row + 1):
525
+ for row in range(b.selection.actual_start_row, b.selection.actual_end_row + 1):
539
526
  line = b.getLine(row)
540
527
  line_plain = Session.PLAIN_TEXT_REGX.sub("", line).replace("\r", "").replace("\x00", "")
541
528
  lines.append(line_plain)
@@ -545,9 +532,10 @@ class PyMudApp:
545
532
 
546
533
  else:
547
534
  # RAW模式,直接复制原始内容
548
- if b.selection.start_row == b.selection.end_row:
535
+ #if b.selection.start_row == b.selection.end_row:
536
+ if b.selection.rows == 1:
549
537
  # 单行情况
550
- line = b.getLine(b.selection.start_row)
538
+ line = b.getLine(b.selection.actual_start_row)
551
539
  self.app.clipboard.set_text(line)
552
540
  self.set_status(Settings.gettext("msg_copy", line))
553
541
  if self.current_session:
@@ -556,7 +544,7 @@ class PyMudApp:
556
544
  else:
557
545
  # 多行只认行
558
546
  lines = []
559
- for row in range(b.selection.start_row, b.selection.end_row + 1):
547
+ for row in range(b.selection.actual_start_row, b.selection.actual_end_row + 1):
560
548
  line = b.getLine(row)
561
549
  lines.append(line)
562
550
  copy_raw_text = "\n".join(lines)
@@ -924,7 +912,14 @@ class PyMudApp:
924
912
  if not self._mouse_support:
925
913
  mouse_support = Settings.gettext("status_mouseinh") + " "
926
914
 
915
+ mouse = "0, 0"
916
+
927
917
  if self.current_session:
918
+ buffer = self.current_session.buffer
919
+ if buffer:
920
+ position = buffer.mouse_point
921
+ mouse = f"{position.y}, {position.x}"
922
+
928
923
  if self.current_session._ignore:
929
924
  tri_status = Settings.gettext("status_ignore") + " "
930
925
 
@@ -3,7 +3,7 @@ from pathlib import Path
3
3
  from collections.abc import Iterable
4
4
  from collections import OrderedDict
5
5
  from prompt_toolkit.utils import get_cwidth
6
- from wcwidth import wcswidth, wcwidth
6
+ from wcwidth import wcswidth
7
7
  from typing import Union, Optional, Any, List, Tuple, Dict, Type
8
8
  from .logger import Logger
9
9
  from .extras import DotDict, SessionBuffer
@@ -11,7 +11,7 @@ from .protocol import MudClientProtocol
11
11
  from .modules import ModuleInfo, Plugin
12
12
  from .objects import BaseObject, Trigger, Alias, Command, Timer, SimpleAlias, SimpleTrigger, SimpleTimer, GMCPTrigger, CodeBlock, CodeLine
13
13
  from .settings import Settings
14
- from .decorators import exception, async_exception
14
+ from .decorators import exception
15
15
 
16
16
  class Session:
17
17
  """
@@ -15,7 +15,7 @@ class Settings:
15
15
  "APP 简要描述"
16
16
  __version__ = importlib.metadata.version("pymud")
17
17
  "APP 当前版本"
18
- __release__ = "2025-05-27"
18
+ __release__ = "2025-06-08"
19
19
  "APP 当前版本发布日期"
20
20
  __author__ = "本牛(newstart)@北侠"
21
21
  "APP 作者"
@@ -58,7 +58,7 @@ class Settings:
58
58
  "naws_width" : 150, # 客户端NAWS宽度
59
59
  "naws_height" : 40, # 客户端NAWS高度
60
60
  "newline" : "\n", # 客户端换行符
61
- "tabstop" : 4, # 制表符改成空格
61
+ "tabstop" : 8, # 制表符改成空格
62
62
  "seperator" : ";", # 多个命令分隔符(默认;)
63
63
  "appcmdflag" : "#", # app命令标记(默认#)
64
64
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pymud
3
- Version: 0.21.3
3
+ Version: 0.21.4
4
4
  Summary: a MUD Client written in Python
5
5
  Author-email: "newstart@pkuxkx" <crapex@hotmail.com>
6
6
  Maintainer-email: "newstart@pkuxkx" <crapex@hotmail.com>
@@ -33,13 +33,13 @@ Dynamic: license-file
33
33
  # PyMUD - Python原生MUD客户端
34
34
  ## 简介
35
35
 
36
- ### 北侠WIKI: https://www.pkuxkx.net/wiki/tools/pymud
37
- ### 源代码地址: https://github.com/crapex/pymud
38
- ### 帮助文档地址: https://pymud.readthedocs.org
39
- ### PyPi项目地址: https://pypi.org/project/pymud
40
- ### 由deepwiki自动生成的项目理解文档地址: https://deepwiki.com/crapex/pymud
41
- ### PyMUD用户shanghua写的入门教程文档:https://www.pkuxkx.net/forum/forum.php?mod=viewthread&tid=49999&forumuid=12067
42
- ### 交流QQ群:554672580
36
+ + 北侠WIKI: https://www.pkuxkx.net/wiki/tools/pymud
37
+ + 源代码地址: https://github.com/crapex/pymud
38
+ + 帮助文档地址: https://pymud.readthedocs.io
39
+ + PyPi项目地址: https://pypi.org/project/pymud
40
+ + 由deepwiki自动生成的项目理解文档地址: https://deepwiki.com/crapex/pymud
41
+ + PyMUD用户shanghua写的入门教程文档:https://www.pkuxkx.net/forum/forum.php?mod=viewthread&tid=49999&forumuid=12067
42
+ + 交流QQ群:554672580
43
43
 
44
44
 
45
45
  ### 北大侠客行Mud (www.pkuxkx.net),最好的中文Mud游戏!
@@ -55,7 +55,7 @@ Dynamic: license-file
55
55
  + 一次脚本开发,多平台运行。只要能在该平台上运行python,就可以运行PyMUD客户端
56
56
  + 脚本所有语法均采用Python原生语法,因此你只要会用Python,就可以自己写脚本,免去了再去学习lua、熟悉各类APP的使用的难处
57
57
  + Python拥有极为强大的文字处理能力,用于处理文本的MUD最为合适
58
- + Python拥有极为丰富的第三方库,能支持的第三方库,就能在PyMud中支持
58
+ + Python拥有极为丰富的第三方库,能支持的第三方库,就能在PyMUD中支持
59
59
  + 我自己还在玩,所以本客户端会持续进行更新:)
60
60
 
61
61
  ### 哪些人适合使用PyMUD
@@ -66,7 +66,15 @@ Dynamic: license-file
66
66
 
67
67
  ## 版本更新信息
68
68
 
69
- ### 0.21.3 2025-06-02)
69
+ ### 0.21.4 (2025-06-08)
70
+
71
+ + 问题修复: 修复了在多行选择碰到空行时,会导致从头开始全选的问题。
72
+ + 问题修复: 修复了从后往前选择时,若再往后选择,会导致起始位置错误的问题。
73
+ + 问题修复: 修复了纯文本行单行部分复制时,最后一个字符无法被选中和复制的问题。
74
+ + 问题修复: 修复了美化模式下,当服务器使用tab对齐在本地显示中无法对齐的问题。为配合对齐,默认tabstop已修改为8。
75
+ + 功能调整: 当上下分屏显示时,上半屏也能显示到最后一行,而不是仅显示到分屏处位置。
76
+
77
+ ### 0.21.3 (2025-06-02)
70
78
 
71
79
  + 问题修复: 修复了当缓存行数超过规定值时,由于代码错误导致清屏的问题。
72
80
  + 功能增强: 增加清除任务时的异常保护,此时产生asyncio.CancelledError异常为正常现象,因此捕获后禁止异常处理。
@@ -75,7 +83,7 @@ Dynamic: license-file
75
83
  ### 0.21.2 (2025-06-01)
76
84
 
77
85
  + 问题修复: 修复了当自动重连启动时,即使会话关闭了,也会自动重连的问题。
78
- + 实现调整: 重写了专用的会话缓冲、记录缓冲与PyMud缓冲显示控制器,在prompt_toolkit的原Buffer和BufferControl的基础仅提供了PYMUD所需的基础功能,以降低内存占用。
86
+ + 实现调整: 重写了专用的会话缓冲、记录缓冲与PyMUD缓冲显示控制器,在prompt_toolkit的原Buffer和BufferControl的基础仅提供了PyMUD所需的基础功能,以降低内存占用。
79
87
  经测试,当前内存基本稳定,视会话数量和脚本情况差异,维持在几百兆左右(500M以下),且不会有大幅波动。重写后,低配置的VPS也可以稳定运行PyMUD。
80
88
 
81
89
  ### 0.21.0 (2025-05-20)
@@ -92,19 +100,19 @@ Dynamic: license-file
92
100
 
93
101
  + 功能新增: 新增了deleteGroup函数,用于删除指定的对象组。可同时指定组名、是否包含子组名、有效类型范围。函数定义和示例代码如下:
94
102
 
95
- ``` Python
96
- def deleteGroup(self, group: str, subgroup = True, types: Union[Type, Union[Tuple, List]] = (Alias, Trigger, Command, Timer, GMCPTrigger)):
97
- pass
98
- # 各参数含义:
99
- # group: 要删除的组名,可以是完整的组名,也可以是部分组名。例如:"group1" 或 "group1.subgroup1" 等。
100
- # subgroup: 是否包含子组。如果为True,则删除指定组及其所有子组的对象。如果为False,则仅删除指定组的对象,不包括子组。
101
- # types: 要删除的对象类型范围。可以是单个类型,也可以是类型的元组或列表。例如:Trigger, Alias, Command, Timer, GMCPTrigger 等。
102
- # 示例代码:
103
- # 删除所有属于group1的Trigger和Alias对象,包括子组如 group1.subgroup1 和 group1.subgroup2 等
104
- self.session.deleteGroup("group1", True, [Trigger, Alias])
105
- # 删除所有属于group1的Trigger对象,但不包括子组
106
- self.session.deleteGroup("group1", False, [Trigger])
107
- ```
103
+ ``` Python
104
+ def deleteGroup(self, group: str, subgroup = True, types: Union[Type, Union[Tuple, List]] = (Alias, Trigger, Command, Timer, GMCPTrigger)):
105
+ pass
106
+ # 各参数含义:
107
+ # group: 要删除的组名,可以是完整的组名,也可以是部分组名。例如:"group1" 或 "group1.subgroup1" 等。
108
+ # subgroup: 是否包含子组。如果为True,则删除指定组及其所有子组的对象。如果为False,则仅删除指定组的对象,不包括子组。
109
+ # types: 要删除的对象类型范围。可以是单个类型,也可以是类型的元组或列表。例如:Trigger, Alias, Command, Timer, GMCPTrigger 等。
110
+ # 示例代码:
111
+ # 删除所有属于group1的Trigger和Alias对象,包括子组如 group1.subgroup1 和 group1.subgroup2 等
112
+ self.session.deleteGroup("group1", True, [Trigger, Alias])
113
+ # 删除所有属于group1的Trigger对象,但不包括子组
114
+ self.session.deleteGroup("group1", False, [Trigger])
115
+ ```
108
116
 
109
117
  + 功能新增: 对 #trigger, #alias, #timer, #gmcp, #command, #t+, #t- 等命令新增了组处理选项,用于对整组对象进行处理。各命令的语法格式类似。
110
118
  处理组时,组名应以大于号>或者等于号=开头,紧跟组名(无空格)。当使用>时,表示操作针对当前组及所有所属子组,当使用=时,表示操作仅针对当前组。
@@ -144,7 +152,7 @@ Dynamic: license-file
144
152
  + 功能新增: #load / #unload 现在支持当前会话对插件的临时启用和禁用,实现方式为调用插件里的PLUGIN_SESSION_CREATE和PLUGIN_SESSION_DESTROYE函数。群文件的moving.py插件写法可以支持。
145
153
  + 功能调整: 各会话变量保存的.mud文件,统一移到save子目录下。原来当前目录下的.mud文件,在对应会话重新加载时会自动移动,无需人工处理。
146
154
  + 功能新增: 增加了国际化(i18n)支持,原生开发语言为中文简体,目前使用AI翻译生成了英文。应用语言通过Settings中新增的language配置来控制,默认为"chs",可以在pymud.cfg中覆盖该配置。其值目前可以为"chs"、"eng"。自行翻译的语言可以在pymud/lang目录下下新增语言文件,文件名为i18n_加语言代码,例如"i18n_chs.py"表示可以使用"chs"语言,其中使用Python字典方式定义了所有需动态显示的文本内容。
147
- + 功能新增: 新增了使用元类型及装饰器来管理Pymud对象,包括Alias, Trigger, Timer, GMCPTrigger四种可以使用对应的装饰器,@alias, @trigger, @timer, @gmcp来直接在标记函数上创建。可以参考本版本中的pkuxkx.py文件写法和注意事项。
155
+ + 功能新增: 新增了使用元类型及装饰器来管理PyMUD对象,包括Alias, Trigger, Timer, GMCPTrigger四种可以使用对应的装饰器,@alias, @trigger, @timer, @gmcp来直接在标记函数上创建。可以参考本版本中的pkuxkx.py文件写法和注意事项。
148
156
  + 功能新增: 新增了两个装饰器,@exception和@async_exception,用于捕获异常并调用session.error进行显示。@exception用于捕获同步异常,@async_exception用于捕获异步异常。参考如下:
149
157
  ``` Python
150
158
  from pymud import Command, Trigger, IConfig, exception, async_exception
@@ -180,7 +188,7 @@ Dynamic: license-file
180
188
  + 功能增强: 对几乎所有函数的参数进行了类型标注,增加了类型检查,提高了代码的可读性和可维护性,也便于自行编写脚本时的提示。
181
189
  + 功能增强: 为Session类型增加了commandHistory属性,用于查询发送到服务器的命令历史。保存的命令历史的数量由pymud.cfg中的client["history_records"]控制,默认为500。当该值为0时,不会保存命令历史。为-1时,会保存所有命令历史。
182
190
  + 功能调整: #help命令时,增加了上下两行分隔符显示,以便明显区分帮助输出和游戏输出。
183
- + 功能增强: 当前pymud界面中显示的版本号会自动从pyproject.toml中读取,以确保版本号的准确性和唯一性。
191
+ + 功能增强: 当前PyMUD界面中显示的版本号会自动从pyproject.toml中读取,以确保版本号的准确性和唯一性。
184
192
  + 问题修复: 修复了代码中的部分编码错误。新版Python中能容忍一些错误,但老版本不行。经修复,当前代码支持的Python版本已测试3.8确保可用。建议使用3.10或更高版本的Python。
185
193
  + 问题修复: 删除了extras.py中多余的MenuItem类型定义,该定义与prompt_toolkit中的MenuItem定义冲突。
186
194
  + 问题修复: 调整了众多代码中未检查对象是否为None即调用、使用的局部变量可能未经过初始化和赋值路径等的情况,保证程序运行的健壮性。
@@ -210,7 +218,7 @@ Dynamic: license-file
210
218
  ### 0.20.1 (2024-11-16)
211
219
  + 功能调整: 会话中触发器匹配实现进行部分调整,减少循环次数以提高响应速度
212
220
  + 功能调整: #test / #show 触发器测试功能调整,现在会对使能的和未使能的触发器均进行匹配测试。其中,#show 命令仅测试,而 #test 命令会导致触发器真正响应。
213
- + 功能新增: pymud对象新增了一个持续运行的1s的周期定时任务。该任务中会刷新页面显示。可以使用 session.application.addTimerTickCallback 和 session.application.removeTimerTickCallback 来注册和解除定时器回调。
221
+ + 功能新增: PyMUD对象新增了一个持续运行的1s的周期定时任务。该任务中会刷新页面显示。可以使用 session.application.addTimerTickCallback 和 session.application.removeTimerTickCallback 来注册和解除定时器回调。
214
222
 
215
223
  ### 0.20.0 (2024-08-25)
216
224
  + 功能调整: 将模块主入口函数从__main__.py中移动到main.py中,以使可以在当前目录下,可直接使用pymud,也可使用python -m pymud启动
@@ -233,16 +241,16 @@ Dynamic: license-file
233
241
  + 问题修复: 修复部分正则表达式书写错误问题
234
242
  + 功能新增: Session类新增waitfor函数,用于执行一段代码后立即等待某个触发器的情况,简化原三行代码写法
235
243
 
236
- ``` Python
237
- # 原来为确保await triggered的任务在输入前等待,有时候需要这么写:
238
- task = self.create_task(self.tri1.triggered())
239
- await asyncio.sleep(0.05)
240
- self.session.writeline('dazuo')
241
- await task
242
-
243
- # 现在可以一句话简写:
244
- await self.session.waitfor('dazuo', self.create_task(self.tri1.triggered()))
245
- ```
244
+ ``` Python
245
+ # 原来为确保await triggered的任务在输入前等待,有时候需要这么写:
246
+ task = self.create_task(self.tri1.triggered())
247
+ await asyncio.sleep(0.05)
248
+ self.session.writeline('dazuo')
249
+ await task
250
+
251
+ # 现在可以一句话简写:
252
+ await self.session.waitfor('dazuo', self.create_task(self.tri1.triggered()))
253
+ ```
246
254
 
247
255
  + 功能调整: Session类的addTriggers等方法接受的dict中,会将对象本身id作为会话处理id。当该id与key不一致时,会同时显示警告。
248
256
  + 功能新增: Session类新增addObject, addObjects, delObject, delObjects用于操作别名、定时器、触发器、GMCP触发器、命令等对象。
@@ -379,7 +387,7 @@ Dynamic: license-file
379
387
  + 功能修改:Timer实现进行修改,以确保一个定时器仅创建一个任务。
380
388
  + 功能调整:Timer对象在复位Session对象时,也同时复位。目的是确保reload时不重新创建定时器任务。
381
389
  + 功能调整:在会话连接时,不再复位Session有关对象信息。该复位活动仅在连接断开时和脚本重新加载时进行。
382
- + 功能调整:启动PYMUD时,会将控制台标题设置为PYMUD+版本号。
390
+ + 功能调整:启动PyMUD时,会将控制台标题设置为PyMUD+版本号。
383
391
  + 问题修复:修复会话特定脚本模块会被其他会话加载的bug。
384
392
  + 问题修复:修复定时器Timer中的bug。
385
393
 
@@ -387,7 +395,7 @@ Dynamic: license-file
387
395
  本版对模块功能进行了整体调整,支持加载/卸载/重载/预加载多个模块,具体内容如下:
388
396
  + 当模块中存在名为Configuration类时,以主模块形式加载,即:自动创建该Configuration类的实例(与原脚本相同)
389
397
  + 当模块中不存在名为Configuration类时,以子模块形式加载,即:仅加载该模块,但不会创建Configuration的实例
390
- + #load命令支持同时加载多个模块,模块名以半角逗号(,)隔开即可。此时按给定的名称顺序逐一加载。如:#load mod1,mod2
398
+ + \#load命令支持同时加载多个模块,模块名以半角逗号(,)隔开即可。此时按给定的名称顺序逐一加载。如:#load mod1,mod2
391
399
  + 增加#unload命令,卸载卸载名称模块,同时卸载多个模块时,模块名以半角逗号(,)隔开即可。卸载时,如果该模块有Configuration类,会自动调用其__del__方法
392
400
  + 修改reload命令功能,当不带参数时,重新加载所有已加载模块,带参数时,首先尝试重新加载指定名称模块,若模块中不存在该名称模块,则重新加载指定名称的插件。若存在同名模块和插件,则仅重新加载插件(建议不要让插件和模块同名)
393
401
  + 增加#modules(简写为#mods)命令,可以列出所有已经加载的模块清单
@@ -438,13 +446,14 @@ Dynamic: license-file
438
446
  + 功能增加:增加#py指令,可以直接在命令行中写代码并执行。执行的上下文环境为当前环境,即self代表当前session。例如,#py self.writeline("xixi")相当于直接在脚本会话中调用发送xixi指令
439
447
  + 功能新增:新增插件(Plugins)功能。将自动读取pymud模块目录的plugins子目录以及当前脚本目录的plugins子目录下的.py文件,若发现遵照插件规范脚本,将自动加载该模块到pymud。可以使用#plugins查看所有被加载的插件,可以直接带参数插件名(如#plugins myplugin)查看插件的详细信息(自动打印插件的__doc__属性,即写在文件最前面的字符串常量)插件文件中必须有以下定义:
440
448
 
441
- |名称|类型|状态|含义|
442
- |-|-|-|-|
443
- |PLUGIN_NAME|str|必须有|插件唯一名称|
444
- |PLUGIN_DESC|dict|必须有|插件描述信息的详情,必要关键字包含VERSION(版本)、AUTHOR(作者)、RELEASE_DATE(发布日期)、DESCRIPTION(简要描述)|
445
- |PLUGIN_PYMUD_START|func(app)|函数定义必须有,函数体可以为空|PYMUD自动读取并加载插件时自动调用的函数, app为PyMudApp(pymud管理类)。该函数仅会在程序运行时,自动加载一次|
446
- |PLUGIN_SESSION_CREATE|func(session)|函数定义必须有,函数体可以为空|在会话中加载插件时自动调用的函数, session为加载插件的会话。该函数在每一个会话创建时均被自动加载一次|
447
- |PLUGIN_SESSION_DESTROY|func(session)|函数定义必须有,函数体可以为空|在会话中卸载插件时自动调用的函数, session为卸载插件的会话。卸载在每一个会话关闭时均被自动运行一次。|
449
+ |名称|类型|状态|含义|
450
+ |-|-|-|-|
451
+ |PLUGIN_NAME|str|必须有|插件唯一名称|
452
+ |PLUGIN_DESC|dict|必须有|插件描述信息的详情,必要关键字包含VERSION(版本)、AUTHOR(作者)、RELEASE_DATE(发布日期)、DESCRIPTION(简要描述)|
453
+ |PLUGIN_PYMUD_START|func(app)|函数定义必须有,函数体可以为空|PyMUD自动读取并加载插件时自动调用的函数, app为PyMudApp(PyMUD管理类)。该函数仅会在程序运行时,自动加载一次|
454
+ |PLUGIN_SESSION_CREATE|func(session)|函数定义必须有,函数体可以为空|在会话中加载插件时自动调用的函数, session为加载插件的会话。该函数在每一个会话创建时均被自动加载一次|
455
+ |PLUGIN_SESSION_DESTROY|func(session)|函数定义必须有,函数体可以为空|在会话中卸载插件时自动调用的函数, session为卸载插件的会话。卸载在每一个会话关闭时均被自动运行一次。|
456
+ |PLUGIN_PYMUD_DESTROY|func(app)|函数定义必须有,函数体可以为空|在PyMUD程序退出时自动调用的函数, app为PyMUD管理类。该函数仅会在程序运行时,自动卸载一次|
448
457
 
449
458
  + 功能修改:对session自动加载mud文件中变量失败时的异常进行管理,此时将不加载变量,自动继续进行
450
459
  + 功能修改:所有匹配类对象的匹配模式patterns支持动态修改,涉及Alias,Trigger,Command。修改方式为直接对其patterns属性赋值。如tri.patterns = aNewPattern
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes