pymud 0.20.0a5__tar.gz → 0.20.1__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.
- {pymud-0.20.0a5/src/pymud.egg-info → pymud-0.20.1}/PKG-INFO +20 -13
- {pymud-0.20.0a5 → pymud-0.20.1}/README.md +16 -9
- {pymud-0.20.0a5 → pymud-0.20.1}/pyproject.toml +5 -5
- {pymud-0.20.0a5 → pymud-0.20.1}/src/pymud/dialogs.py +11 -6
- {pymud-0.20.0a5 → pymud-0.20.1}/src/pymud/extras.py +32 -75
- {pymud-0.20.0a5 → pymud-0.20.1}/src/pymud/logger.py +9 -3
- pymud-0.20.1/src/pymud/modules.py +188 -0
- {pymud-0.20.0a5 → pymud-0.20.1}/src/pymud/objects.py +32 -18
- {pymud-0.20.0a5 → pymud-0.20.1}/src/pymud/pymud.py +43 -8
- {pymud-0.20.0a5 → pymud-0.20.1}/src/pymud/session.py +144 -56
- {pymud-0.20.0a5 → pymud-0.20.1}/src/pymud/settings.py +2 -2
- {pymud-0.20.0a5 → pymud-0.20.1/src/pymud.egg-info}/PKG-INFO +20 -13
- pymud-0.20.0a5/src/pymud/modules.py +0 -80
- {pymud-0.20.0a5 → pymud-0.20.1}/LICENSE.txt +0 -0
- {pymud-0.20.0a5 → pymud-0.20.1}/setup.cfg +0 -0
- {pymud-0.20.0a5 → pymud-0.20.1}/src/pymud/__init__.py +0 -0
- {pymud-0.20.0a5 → pymud-0.20.1}/src/pymud/__main__.py +0 -0
- {pymud-0.20.0a5 → pymud-0.20.1}/src/pymud/main.py +0 -0
- {pymud-0.20.0a5 → pymud-0.20.1}/src/pymud/pkuxkx.py +0 -0
- {pymud-0.20.0a5 → pymud-0.20.1}/src/pymud/protocol.py +0 -0
- {pymud-0.20.0a5 → pymud-0.20.1}/src/pymud.egg-info/SOURCES.txt +0 -0
- {pymud-0.20.0a5 → pymud-0.20.1}/src/pymud.egg-info/dependency_links.txt +0 -0
- {pymud-0.20.0a5 → pymud-0.20.1}/src/pymud.egg-info/entry_points.txt +0 -0
- {pymud-0.20.0a5 → pymud-0.20.1}/src/pymud.egg-info/requires.txt +0 -0
- {pymud-0.20.0a5 → pymud-0.20.1}/src/pymud.egg-info/top_level.txt +0 -0
@@ -1,9 +1,9 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: pymud
|
3
|
-
Version: 0.20.
|
3
|
+
Version: 0.20.1
|
4
4
|
Summary: a MUD Client written in Python
|
5
|
-
Author-email: "newstart@pkuxkx" <crapex@
|
6
|
-
Maintainer-email: "newstart@pkuxkx" <crapex@
|
5
|
+
Author-email: "newstart@pkuxkx" <crapex@hotmail.com>
|
6
|
+
Maintainer-email: "newstart@pkuxkx" <crapex@hotmail.com>
|
7
7
|
License: GNU GENERAL PUBLIC LICENSE
|
8
8
|
Version 3, 29 June 2007
|
9
9
|
|
@@ -684,7 +684,7 @@ Project-URL: Bug Reports, https://github.com/crapex/pymud/issues
|
|
684
684
|
Project-URL: Source, https://github.com/crapex/pymud/
|
685
685
|
Project-URL: document, https://pymud.readthedocs.io/
|
686
686
|
Keywords: MUD,multi-user dungeon,client
|
687
|
-
Classifier: Development Status ::
|
687
|
+
Classifier: Development Status :: 5 - Production/Stable
|
688
688
|
Classifier: Intended Audience :: End Users/Desktop
|
689
689
|
Classifier: Topic :: Games/Entertainment :: Multi-User Dungeons (MUD)
|
690
690
|
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
@@ -707,7 +707,7 @@ Requires-Dist: prompt-toolkit
|
|
707
707
|
# PyMUD - Python原生MUD客户端
|
708
708
|
## 简介
|
709
709
|
|
710
|
-
###
|
710
|
+
### 北侠WIKI: https://www.pkuxkx.net/wiki/tools/pymud
|
711
711
|
### 源代码地址: https://github.com/crapex/pymud
|
712
712
|
### 帮助文档地址: https://pymud.readthedocs.org
|
713
713
|
### PyPi项目地址: https://pypi.org/project/pymud
|
@@ -729,9 +729,20 @@ Requires-Dist: prompt-toolkit
|
|
729
729
|
+ Python拥有极为丰富的第三方库,能支持的第三方库,就能在PyMud中支持
|
730
730
|
+ 我自己还在玩,所以本客户端会持续进行更新:)
|
731
731
|
|
732
|
+
### 哪些人适合使用PyMUD
|
733
|
+
+ 比较熟悉Python语言,会使用Python写代码的 -> PyMUD是纯Python原生开发,不会有其他客户端对Python的支持能比得过PyMUD
|
734
|
+
+ 虽不太熟悉Python语言,但有想法想学习Python语言的 -> 正好使用PyMUD玩北侠写脚本的过程中学习Python语言
|
735
|
+
+ 觉得还有些功能现在所有客户端都没有的 -> 你有需求,我来增加,就是这么方便
|
736
|
+
+ 觉得也想自己整一个定制客户端玩玩的 -> PyMUD完全开源,且除ui框架外全部都是一行一行代码自己写的,可以直接参考PyMUD的设计
|
737
|
+
|
732
738
|
## 版本更新信息
|
733
739
|
|
734
|
-
|
740
|
+
### 0.20.1 (2024-11-16)
|
741
|
+
+ 功能调整: 会话中触发器匹配实现进行部分调整,减少循环次数以提高响应速度
|
742
|
+
+ 功能调整: #test / #show 触发器测试功能调整,现在会对使能的和未使能的触发器均进行匹配测试。其中,#show 命令仅测试,而 #test 命令会导致触发器真正响应。
|
743
|
+
+ 功能新增: pymud对象新增了一个持续运行的1s的周期定时任务。该任务中会刷新页面显示。可以使用 session.application.addTimerTickCallback 和 session.application.removeTimerTickCallback 来注册和解除定时器回调。
|
744
|
+
|
745
|
+
## 0.20.0 (2024-08-25)
|
735
746
|
+ 功能调整: 将模块主入口函数从__main__.py中移动到main.py中,以使可以在当前目录下,可直接使用pymud,也可使用python -m pymud启动
|
736
747
|
+ 功能调整: 使用argsparser标准模块来配置命令行,可以使用 pymud -h 查看命令行具体参数及说明
|
737
748
|
+ 功能新增: 命令行参数增加指定启动目录的功能,参数为 -s, --startup_dir。即可以从任意目录通过指定脚本目录方式启动PyMUD了。
|
@@ -768,11 +779,6 @@ Requires-Dist: prompt-toolkit
|
|
768
779
|
- 使用示例:
|
769
780
|
|
770
781
|
```Python
|
771
|
-
# 所有对象均可以使用 addObject 直接添加到会话中,而不用管是什么具体类型
|
772
|
-
session.addObject(Timer(...))
|
773
|
-
session.addObject(Trigger(...))
|
774
|
-
session.addObject(Alias(...))
|
775
|
-
|
776
782
|
# 所有对象均可以使用 delObject 直接从会话中移除,会自动根据对象类型推断,无需通过函数名区分
|
777
783
|
session.delObject(self.tri1)
|
778
784
|
session.delObject(self.ali1)
|
@@ -786,7 +792,6 @@ Requires-Dist: prompt-toolkit
|
|
786
792
|
GMCPTrigger(session, xxx)
|
787
793
|
]
|
788
794
|
|
789
|
-
session.addObjects(objs) # 可以直接将一个数组中所有对象添加到会话中,会自动判断各对象类别
|
790
795
|
session.delObjects(objs) # 可以直接从会话中移除一个数组中的所有对象,会自动判断对象类别
|
791
796
|
```
|
792
797
|
|
@@ -795,7 +800,9 @@ Requires-Dist: prompt-toolkit
|
|
795
800
|
- 例如, result = await self.session.cmds.cmd_runto.execute('rt yz') 与 result = await self.session.exec_async('rt yz') 等价,返回值相同
|
796
801
|
- 但 result = await self.session.exec_async('rt yz;dzt'),该返回的result 仅是 dzt 命令的 execute 的返回值。 rt yz 命令返回值被丢弃。
|
797
802
|
+ 功能新增: 增加临时变量概念,变量名以下划线开头的为临时变量,此类变量不会被保存到 .mud 文件中。
|
798
|
-
|
803
|
+
+ 功能新增: 为 BaseObject 基类的 self.session 增加了 Session 类型限定,现在自定义 Command 等时候,使用 self.session 时会有 IntelliSence 函数智能提示了,所有帮助说明已补全
|
804
|
+
+ 问题修复: 修复 #var 等命令中,若含有中文则等号位置不对齐的问题
|
805
|
+
+ 功能调整: 在 #tri 等命令中,当对象的 group 为空时,将不再显示 group 属性,减少无用信息
|
799
806
|
|
800
807
|
## 0.19.4 (2024-04-20)
|
801
808
|
+ 功能调整: info 现在 msg 恢复为可接受任何类型参数,不一定是 str
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# PyMUD - Python原生MUD客户端
|
2
2
|
## 简介
|
3
3
|
|
4
|
-
###
|
4
|
+
### 北侠WIKI: https://www.pkuxkx.net/wiki/tools/pymud
|
5
5
|
### 源代码地址: https://github.com/crapex/pymud
|
6
6
|
### 帮助文档地址: https://pymud.readthedocs.org
|
7
7
|
### PyPi项目地址: https://pypi.org/project/pymud
|
@@ -23,9 +23,20 @@
|
|
23
23
|
+ Python拥有极为丰富的第三方库,能支持的第三方库,就能在PyMud中支持
|
24
24
|
+ 我自己还在玩,所以本客户端会持续进行更新:)
|
25
25
|
|
26
|
+
### 哪些人适合使用PyMUD
|
27
|
+
+ 比较熟悉Python语言,会使用Python写代码的 -> PyMUD是纯Python原生开发,不会有其他客户端对Python的支持能比得过PyMUD
|
28
|
+
+ 虽不太熟悉Python语言,但有想法想学习Python语言的 -> 正好使用PyMUD玩北侠写脚本的过程中学习Python语言
|
29
|
+
+ 觉得还有些功能现在所有客户端都没有的 -> 你有需求,我来增加,就是这么方便
|
30
|
+
+ 觉得也想自己整一个定制客户端玩玩的 -> PyMUD完全开源,且除ui框架外全部都是一行一行代码自己写的,可以直接参考PyMUD的设计
|
31
|
+
|
26
32
|
## 版本更新信息
|
27
33
|
|
28
|
-
|
34
|
+
### 0.20.1 (2024-11-16)
|
35
|
+
+ 功能调整: 会话中触发器匹配实现进行部分调整,减少循环次数以提高响应速度
|
36
|
+
+ 功能调整: #test / #show 触发器测试功能调整,现在会对使能的和未使能的触发器均进行匹配测试。其中,#show 命令仅测试,而 #test 命令会导致触发器真正响应。
|
37
|
+
+ 功能新增: pymud对象新增了一个持续运行的1s的周期定时任务。该任务中会刷新页面显示。可以使用 session.application.addTimerTickCallback 和 session.application.removeTimerTickCallback 来注册和解除定时器回调。
|
38
|
+
|
39
|
+
## 0.20.0 (2024-08-25)
|
29
40
|
+ 功能调整: 将模块主入口函数从__main__.py中移动到main.py中,以使可以在当前目录下,可直接使用pymud,也可使用python -m pymud启动
|
30
41
|
+ 功能调整: 使用argsparser标准模块来配置命令行,可以使用 pymud -h 查看命令行具体参数及说明
|
31
42
|
+ 功能新增: 命令行参数增加指定启动目录的功能,参数为 -s, --startup_dir。即可以从任意目录通过指定脚本目录方式启动PyMUD了。
|
@@ -62,11 +73,6 @@
|
|
62
73
|
- 使用示例:
|
63
74
|
|
64
75
|
```Python
|
65
|
-
# 所有对象均可以使用 addObject 直接添加到会话中,而不用管是什么具体类型
|
66
|
-
session.addObject(Timer(...))
|
67
|
-
session.addObject(Trigger(...))
|
68
|
-
session.addObject(Alias(...))
|
69
|
-
|
70
76
|
# 所有对象均可以使用 delObject 直接从会话中移除,会自动根据对象类型推断,无需通过函数名区分
|
71
77
|
session.delObject(self.tri1)
|
72
78
|
session.delObject(self.ali1)
|
@@ -80,7 +86,6 @@
|
|
80
86
|
GMCPTrigger(session, xxx)
|
81
87
|
]
|
82
88
|
|
83
|
-
session.addObjects(objs) # 可以直接将一个数组中所有对象添加到会话中,会自动判断各对象类别
|
84
89
|
session.delObjects(objs) # 可以直接从会话中移除一个数组中的所有对象,会自动判断对象类别
|
85
90
|
```
|
86
91
|
|
@@ -89,7 +94,9 @@
|
|
89
94
|
- 例如, result = await self.session.cmds.cmd_runto.execute('rt yz') 与 result = await self.session.exec_async('rt yz') 等价,返回值相同
|
90
95
|
- 但 result = await self.session.exec_async('rt yz;dzt'),该返回的result 仅是 dzt 命令的 execute 的返回值。 rt yz 命令返回值被丢弃。
|
91
96
|
+ 功能新增: 增加临时变量概念,变量名以下划线开头的为临时变量,此类变量不会被保存到 .mud 文件中。
|
92
|
-
|
97
|
+
+ 功能新增: 为 BaseObject 基类的 self.session 增加了 Session 类型限定,现在自定义 Command 等时候,使用 self.session 时会有 IntelliSence 函数智能提示了,所有帮助说明已补全
|
98
|
+
+ 问题修复: 修复 #var 等命令中,若含有中文则等号位置不对齐的问题
|
99
|
+
+ 功能调整: 在 #tri 等命令中,当对象的 group 为空时,将不再显示 group 属性,减少无用信息
|
93
100
|
|
94
101
|
## 0.19.4 (2024-04-20)
|
95
102
|
+ 功能调整: info 现在 msg 恢复为可接受任何类型参数,不一定是 str
|
@@ -1,17 +1,17 @@
|
|
1
1
|
[project]
|
2
2
|
|
3
3
|
name = "pymud" # Required
|
4
|
-
version = "0.20.
|
4
|
+
version = "0.20.1" # Required
|
5
5
|
description = "a MUD Client written in Python" # Optional
|
6
6
|
readme = "README.md" # Optional
|
7
7
|
requires-python = ">=3.7"
|
8
8
|
license = {file = "LICENSE.txt"}
|
9
9
|
keywords = ["MUD", "multi-user dungeon", "client"] # Optional
|
10
10
|
authors = [
|
11
|
-
{name = "newstart@pkuxkx", email = "crapex@
|
11
|
+
{name = "newstart@pkuxkx", email = "crapex@hotmail.com" } # Optional
|
12
12
|
]
|
13
13
|
maintainers = [
|
14
|
-
{name = "newstart@pkuxkx", email = "crapex@
|
14
|
+
{name = "newstart@pkuxkx", email = "crapex@hotmail.com" } # Optional
|
15
15
|
]
|
16
16
|
|
17
17
|
classifiers = [ # Optional
|
@@ -19,8 +19,8 @@ classifiers = [ # Optional
|
|
19
19
|
# 3 - Alpha
|
20
20
|
# 4 - Beta
|
21
21
|
# 5 - Production/Stable
|
22
|
-
|
23
|
-
"Development Status :: 3 - Alpha",
|
22
|
+
"Development Status :: 5 - Production/Stable",
|
23
|
+
# "Development Status :: 3 - Alpha",
|
24
24
|
"Intended Audience :: End Users/Desktop",
|
25
25
|
"Topic :: Games/Entertainment :: Multi-User Dungeons (MUD)",
|
26
26
|
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import asyncio, webbrowser
|
2
2
|
|
3
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
|
4
|
+
from prompt_toolkit.widgets import Button, Dialog, Label, MenuContainer, MenuItem, TextArea, SystemToolbar, Frame, RadioList
|
5
5
|
from prompt_toolkit.layout.dimension import Dimension, D
|
6
6
|
from prompt_toolkit import ANSI, HTML
|
7
7
|
from prompt_toolkit.mouse_events import MouseEvent, MouseEventType
|
@@ -133,12 +133,14 @@ class LogSelectionDialog(BasicDialog):
|
|
133
133
|
def __init__(self, text, values, modal=True):
|
134
134
|
self._header_text = text
|
135
135
|
self._selection_values = values
|
136
|
-
self.
|
136
|
+
self._itemsCount = len(values)
|
137
|
+
if len(values) > 0:
|
138
|
+
self._radio_list = RadioList(values = self._selection_values)
|
139
|
+
else:
|
140
|
+
self._radio_list = Label('无记录'.center(13))
|
137
141
|
super().__init__('选择查看的记录', modal)
|
138
142
|
|
139
143
|
def create_body(self) -> AnyContainer:
|
140
|
-
|
141
|
-
|
142
144
|
body=HSplit([
|
143
145
|
Label(text = self._header_text, dont_extend_height=True),
|
144
146
|
self._radio_list
|
@@ -151,6 +153,9 @@ class LogSelectionDialog(BasicDialog):
|
|
151
153
|
return [ok_button, cancel_button]
|
152
154
|
|
153
155
|
def btn_ok_clicked(self):
|
154
|
-
|
155
|
-
|
156
|
+
if self._itemsCount:
|
157
|
+
result = self._radio_list.current_value
|
158
|
+
self.set_done(result)
|
159
|
+
else:
|
160
|
+
self.set_done(False)
|
156
161
|
|
@@ -1,8 +1,7 @@
|
|
1
1
|
# External Libraries
|
2
2
|
from unicodedata import east_asian_width
|
3
3
|
from wcwidth import wcwidth
|
4
|
-
|
5
|
-
import time, datetime
|
4
|
+
import time, re, logging
|
6
5
|
|
7
6
|
from typing import Iterable
|
8
7
|
from prompt_toolkit import ANSI
|
@@ -65,6 +64,10 @@ class MudFormatProcessor(Processor):
|
|
65
64
|
self.FULL_BLOCKS = set("▂▃▅▆▇▄█")
|
66
65
|
self.SINGLE_LINES = set("┌└├┬┼┴╭╰─")
|
67
66
|
self.DOUBLE_LINES = set("╔╚╠╦╪╩═")
|
67
|
+
self.START_COLOR_REGX = re.compile(r"^\[[\d;]+m")
|
68
|
+
self.COLOR_REGX = re.compile(r"\[[\d;]+m")
|
69
|
+
self._color_start = ""
|
70
|
+
self._color_correction = False
|
68
71
|
|
69
72
|
def width_correction(self, line: str) -> str:
|
70
73
|
new_str = []
|
@@ -88,11 +91,36 @@ class MudFormatProcessor(Processor):
|
|
88
91
|
def tab_correction(self, line: str):
|
89
92
|
return line.replace("\t", " " * Settings.client["tabstop"])
|
90
93
|
|
94
|
+
def color_correction(self, line: str):
|
95
|
+
# 注:发现processer处理并非自上而下逐行处理的,因此不能使用这种颜色校正方式。
|
96
|
+
if self._color_correction:
|
97
|
+
other = self.COLOR_REGX.findall(line)
|
98
|
+
|
99
|
+
line = f"{self._color_start}{line}"
|
100
|
+
logging.debug(f"已校正增加颜色标志 {self._color_start}: {line}")
|
101
|
+
|
102
|
+
if other:
|
103
|
+
self._color_correction = False
|
104
|
+
self._color_start = ""
|
105
|
+
logging.debug(f"颜色校正结束: {line}")
|
106
|
+
else:
|
107
|
+
color = self.START_COLOR_REGX.findall(line)
|
108
|
+
if color:
|
109
|
+
other = self.COLOR_REGX.findall(line)
|
110
|
+
if len(other) == 1:
|
111
|
+
self._color_correction = True
|
112
|
+
self._color_start = color[0]
|
113
|
+
logging.debug(f"获取到一个颜色开头 {color[0]}: {line}")
|
114
|
+
|
115
|
+
return line
|
116
|
+
|
91
117
|
def line_correction(self, line: str):
|
92
118
|
# 处理\r符号(^M)
|
93
119
|
line = self.return_correction(line)
|
94
120
|
# 处理Tab(\r)符号(^I)
|
95
121
|
line = self.tab_correction(line)
|
122
|
+
# 处理颜色跨行问题。发现processer处理并非自上而下逐行处理的,因此不能使用这种颜色校正方式。
|
123
|
+
# line = self.color_correction(line)
|
96
124
|
# 美化(解决中文英文在Console中不对齐的问题)
|
97
125
|
if Settings.client["beautify"]:
|
98
126
|
line = self.width_correction(line)
|
@@ -430,9 +458,9 @@ class SessionBufferControl(BufferControl):
|
|
430
458
|
|
431
459
|
if double_click:
|
432
460
|
start = buffer.document.translate_row_col_to_index(position.y, 0)
|
433
|
-
end = buffer.document.translate_row_col_to_index(position.y,
|
461
|
+
end = buffer.document.translate_row_col_to_index(position.y + 1, 0) - 1
|
434
462
|
buffer.cursor_position = start
|
435
|
-
buffer.start_selection(selection_type=SelectionType.
|
463
|
+
buffer.start_selection(selection_type=SelectionType.LINES)
|
436
464
|
buffer.cursor_position = end
|
437
465
|
|
438
466
|
else:
|
@@ -1008,76 +1036,5 @@ class DotDict(dict):
|
|
1008
1036
|
def __setstate__(self, state):
|
1009
1037
|
self.update(state)
|
1010
1038
|
|
1011
|
-
import importlib
|
1012
|
-
import importlib.util
|
1013
|
-
|
1014
|
-
class Plugin:
|
1015
|
-
"""
|
1016
|
-
插件管理类。对加载的插件文件进行管理。该类型由PyMudApp进行管理,无需人工创建。
|
1017
|
-
|
1018
|
-
有关插件的详细信息,请参见 `插件 <plugins.html>`_
|
1019
|
-
|
1020
|
-
:param name: 插件的文件名, 如'myplugin.py'
|
1021
|
-
:param location: 插件所在的目录。自动加载的插件包括PyMUD包目录下的plugins目录以及当前目录下的plugins目录
|
1022
|
-
|
1023
|
-
"""
|
1024
|
-
def __init__(self, name, location):
|
1025
|
-
self._plugin_file = name
|
1026
|
-
self._plugin_loc = location
|
1027
|
-
|
1028
|
-
self.reload()
|
1029
|
-
|
1030
|
-
def reload(self):
|
1031
|
-
"加载/重新加载插件对象"
|
1032
|
-
#del self.modspec, self.mod
|
1033
|
-
self.modspec = importlib.util.spec_from_file_location(self._plugin_file[:-3], self._plugin_loc)
|
1034
|
-
self.mod = importlib.util.module_from_spec(self.modspec)
|
1035
|
-
self.modspec.loader.exec_module(self.mod)
|
1036
|
-
|
1037
|
-
self._app_init = self.mod.__dict__["PLUGIN_PYMUD_START"]
|
1038
|
-
self._session_create = self.mod.__dict__["PLUGIN_SESSION_CREATE"]
|
1039
|
-
self._session_destroy = self.mod.__dict__["PLUGIN_SESSION_DESTROY"]
|
1040
|
-
|
1041
|
-
@property
|
1042
|
-
def name(self):
|
1043
|
-
"插件名称,由插件文件中的 PLUGIN_NAME 常量定义"
|
1044
|
-
return self.mod.__dict__["PLUGIN_NAME"]
|
1045
|
-
|
1046
|
-
@property
|
1047
|
-
def desc(self):
|
1048
|
-
"插件描述,由插件文件中的 PLUGIN_DESC 常量定义"
|
1049
|
-
return self.mod.__dict__["PLUGIN_DESC"]
|
1050
|
-
|
1051
|
-
@property
|
1052
|
-
def help(self):
|
1053
|
-
"插件帮助,由插件文件中的文档字符串定义"
|
1054
|
-
return self.mod.__doc__
|
1055
|
-
|
1056
|
-
def onAppInit(self, app):
|
1057
|
-
"""
|
1058
|
-
PyMUD应用启动时对插件执行的操作,由插件文件中的 PLUGIN_PYMUD_START 函数定义
|
1059
|
-
|
1060
|
-
:param app: 启动的 PyMudApp 对象实例
|
1061
|
-
"""
|
1062
|
-
self._app_init(app)
|
1063
|
-
|
1064
|
-
def onSessionCreate(self, session):
|
1065
|
-
"""
|
1066
|
-
新会话创建时对插件执行的操作,由插件文件中的 PLUGIN_SESSION_CREATE 函数定义
|
1067
|
-
|
1068
|
-
:param session: 新创建的会话对象实例
|
1069
|
-
"""
|
1070
|
-
self._session_create(session)
|
1071
|
-
|
1072
|
-
def onSessionDestroy(self, session):
|
1073
|
-
"""
|
1074
|
-
会话关闭时(注意不是断开)对插件执行的操作,由插件文件中的 PLUGIN_SESSION_DESTROY 函数定义
|
1075
|
-
|
1076
|
-
:param session: 所关闭的会话对象实例
|
1077
|
-
"""
|
1078
|
-
self._session_destroy(session)
|
1079
1039
|
|
1080
|
-
def __getattr__(self, __name: str) -> Any:
|
1081
|
-
if hasattr(self.mod, __name):
|
1082
|
-
return self.mod.__getattribute__(__name)
|
1083
1040
|
|
@@ -1,9 +1,10 @@
|
|
1
|
-
import os, re, datetime, threading
|
1
|
+
import os, re, datetime, threading, pathlib
|
2
2
|
from queue import SimpleQueue, Empty
|
3
|
+
from pathlib import Path
|
3
4
|
|
4
5
|
class Logger:
|
5
6
|
"""
|
6
|
-
PyMUD
|
7
|
+
PyMUD 的记录器类型,可用于会话中向文件记录数据。记录文件保存在当前目录下的 log 子目录中
|
7
8
|
|
8
9
|
:param name: 记录器名称,各记录器名称应保持唯一。记录器名称会作为记录文件名称的主要参数
|
9
10
|
:param mode: 记录模式。可选模式包括 a, w, n 三种。
|
@@ -56,7 +57,12 @@ class Logger:
|
|
56
57
|
now = datetime.datetime.now().strftime("%Y_%m_%d_%H_%M_%S")
|
57
58
|
filename = f"{self.name}.{now}.log"
|
58
59
|
|
59
|
-
|
60
|
+
logdir = Path.cwd().joinpath('log')
|
61
|
+
if not logdir.exists() or not logdir.is_dir():
|
62
|
+
logdir.mkdir()
|
63
|
+
|
64
|
+
filename = logdir.joinpath(filename)
|
65
|
+
#filename = os.path.abspath(filename)
|
60
66
|
self._stream = open(filename, mode = mode, encoding = self._encoding, errors = self._errors)
|
61
67
|
self._thread = t = threading.Thread(target=self._monitor)
|
62
68
|
t.daemon = True
|
@@ -0,0 +1,188 @@
|
|
1
|
+
|
2
|
+
import importlib, importlib.util
|
3
|
+
from abc import ABC, ABCMeta
|
4
|
+
from typing import Any
|
5
|
+
from .objects import BaseObject, Command
|
6
|
+
|
7
|
+
class ModuleInfo:
|
8
|
+
"""
|
9
|
+
模块管理类。对加载的模块文件进行管理。该类型由Session类进行管理,无需人工创建和干预。
|
10
|
+
|
11
|
+
有关模块的分类和使用的详细信息,请参见 `脚本 <scripts.html>`_
|
12
|
+
|
13
|
+
:param module_name: 模块的名称, 应与 import xxx 语法中的 xxx 保持一致
|
14
|
+
:param session: 加载/创建本模块的会话
|
15
|
+
|
16
|
+
"""
|
17
|
+
def __init__(self, module_name: str, session):
|
18
|
+
self.session = session
|
19
|
+
self._name = module_name
|
20
|
+
self._ismainmodule = False
|
21
|
+
self.load()
|
22
|
+
|
23
|
+
def _load(self, reload = False):
|
24
|
+
result = True
|
25
|
+
if reload:
|
26
|
+
self._module = importlib.reload(self._module)
|
27
|
+
else:
|
28
|
+
self._module = importlib.import_module(self.name)
|
29
|
+
self._config = {}
|
30
|
+
for attr_name in dir(self._module):
|
31
|
+
attr = getattr(self._module, attr_name)
|
32
|
+
if isinstance(attr, type) and attr.__module__ == self._module.__name__:
|
33
|
+
if (attr_name == "Configuration") or issubclass(attr, IConfig):
|
34
|
+
try:
|
35
|
+
self._config[f"{self.name}.{attr_name}"] = attr(self.session, reload = reload)
|
36
|
+
self.session.info(f"配置对象 {self.name}.{attr_name} {'重新' if reload else ''}创建成功.")
|
37
|
+
except Exception as e:
|
38
|
+
result = False
|
39
|
+
self.session.error(f"配置对象 {self.name}.{attr_name} 创建失败. 错误信息为: {e}")
|
40
|
+
self._ismainmodule = (self._config != {})
|
41
|
+
return result
|
42
|
+
|
43
|
+
def _unload(self):
|
44
|
+
for key, config in self._config.items():
|
45
|
+
if isinstance(config, Command):
|
46
|
+
# Command 对象在从会话中移除时,自动调用其 unload 系列方法,因此不能产生递归
|
47
|
+
self.session.delObject(config)
|
48
|
+
|
49
|
+
else:
|
50
|
+
|
51
|
+
if hasattr(config, "__unload__"):
|
52
|
+
unload = getattr(config, "__unload__", None)
|
53
|
+
if callable(unload): unload()
|
54
|
+
|
55
|
+
if hasattr(config, "unload"):
|
56
|
+
unload = getattr(config, "unload", None)
|
57
|
+
if callable(unload): unload()
|
58
|
+
|
59
|
+
if isinstance(config, BaseObject):
|
60
|
+
self.session.delObject(config)
|
61
|
+
|
62
|
+
del config
|
63
|
+
self._config.clear()
|
64
|
+
|
65
|
+
def load(self):
|
66
|
+
"加载模块内容"
|
67
|
+
if self._load():
|
68
|
+
self.session.info(f"{'主' if self.ismainmodule else '从'}配置模块 {self.name} 加载完成.")
|
69
|
+
else:
|
70
|
+
self.session.error(f"{'主' if self.ismainmodule else '从'}配置模块 {self.name} 加载失败.")
|
71
|
+
|
72
|
+
def unload(self):
|
73
|
+
"卸载模块内容"
|
74
|
+
self._unload()
|
75
|
+
self._loaded = False
|
76
|
+
self.session.info(f"{'主' if self.ismainmodule else '从'}配置模块 {self.name} 卸载完成.")
|
77
|
+
|
78
|
+
def reload(self):
|
79
|
+
"模块文件更新后调用,重新加载已加载的模块内容"
|
80
|
+
self._unload()
|
81
|
+
self._load(reload = True)
|
82
|
+
self.session.info(f"{'主' if self.ismainmodule else '从'}配置模块 {self.name} 重新加载完成.")
|
83
|
+
|
84
|
+
@property
|
85
|
+
def name(self):
|
86
|
+
"只读属性,模块名称"
|
87
|
+
return self._name
|
88
|
+
|
89
|
+
@property
|
90
|
+
def module(self):
|
91
|
+
"只读属性,模块文件的 ModuleType 对象"
|
92
|
+
return self._module
|
93
|
+
|
94
|
+
@property
|
95
|
+
def config(self):
|
96
|
+
"只读字典属性,根据模块文件 ModuleType 对象创建的其中名为 Configuration 的类型或继承自 IConfig 的子类型实例(若有)"
|
97
|
+
return self._config
|
98
|
+
|
99
|
+
@property
|
100
|
+
def ismainmodule(self):
|
101
|
+
"只读属性,区分是否主模块(即包含具体config的模块)"
|
102
|
+
return self._ismainmodule
|
103
|
+
|
104
|
+
class IConfig(metaclass = ABCMeta):
|
105
|
+
"""
|
106
|
+
用于提示PyMUD应用是否自动创建该配置类型的基础类(模拟接口)。
|
107
|
+
|
108
|
+
继承 IConfig 类型让应用自动管理该类型,唯一需要的是,构造函数中,仅存在一个必须指定的参数 Session。
|
109
|
+
|
110
|
+
在应用自动创建 IConfig 实例时,除 session 参数外,还会传递一个 reload 参数 (bool类型),表示是首次加载还是重新加载特性。
|
111
|
+
可以从kwargs 中获取该参数,并针对性的设计相应代码。例如,重新加载相关联的其他模块等。
|
112
|
+
"""
|
113
|
+
def __init__(self, session, *args, **kwargs):
|
114
|
+
self.session = session
|
115
|
+
|
116
|
+
def __unload__(self):
|
117
|
+
if self.session:
|
118
|
+
self.session.delObject(self)
|
119
|
+
|
120
|
+
class Plugin:
|
121
|
+
"""
|
122
|
+
插件管理类。对加载的插件文件进行管理。该类型由PyMudApp进行管理,无需人工创建。
|
123
|
+
|
124
|
+
有关插件的详细信息,请参见 `插件 <plugins.html>`_
|
125
|
+
|
126
|
+
:param name: 插件的文件名, 如'myplugin.py'
|
127
|
+
:param location: 插件所在的目录。自动加载的插件包括PyMUD包目录下的plugins目录以及当前目录下的plugins目录
|
128
|
+
|
129
|
+
"""
|
130
|
+
def __init__(self, name, location):
|
131
|
+
self._plugin_file = name
|
132
|
+
self._plugin_loc = location
|
133
|
+
|
134
|
+
self.reload()
|
135
|
+
|
136
|
+
def reload(self):
|
137
|
+
"加载/重新加载插件对象"
|
138
|
+
#del self.modspec, self.mod
|
139
|
+
self.modspec = importlib.util.spec_from_file_location(self._plugin_file[:-3], self._plugin_loc)
|
140
|
+
self.mod = importlib.util.module_from_spec(self.modspec)
|
141
|
+
self.modspec.loader.exec_module(self.mod)
|
142
|
+
|
143
|
+
self._app_init = self.mod.__dict__["PLUGIN_PYMUD_START"]
|
144
|
+
self._session_create = self.mod.__dict__["PLUGIN_SESSION_CREATE"]
|
145
|
+
self._session_destroy = self.mod.__dict__["PLUGIN_SESSION_DESTROY"]
|
146
|
+
|
147
|
+
@property
|
148
|
+
def name(self):
|
149
|
+
"插件名称,由插件文件中的 PLUGIN_NAME 常量定义"
|
150
|
+
return self.mod.__dict__["PLUGIN_NAME"]
|
151
|
+
|
152
|
+
@property
|
153
|
+
def desc(self):
|
154
|
+
"插件描述,由插件文件中的 PLUGIN_DESC 常量定义"
|
155
|
+
return self.mod.__dict__["PLUGIN_DESC"]
|
156
|
+
|
157
|
+
@property
|
158
|
+
def help(self):
|
159
|
+
"插件帮助,由插件文件中的文档字符串定义"
|
160
|
+
return self.mod.__doc__
|
161
|
+
|
162
|
+
def onAppInit(self, app):
|
163
|
+
"""
|
164
|
+
PyMUD应用启动时对插件执行的操作,由插件文件中的 PLUGIN_PYMUD_START 函数定义
|
165
|
+
|
166
|
+
:param app: 启动的 PyMudApp 对象实例
|
167
|
+
"""
|
168
|
+
self._app_init(app)
|
169
|
+
|
170
|
+
def onSessionCreate(self, session):
|
171
|
+
"""
|
172
|
+
新会话创建时对插件执行的操作,由插件文件中的 PLUGIN_SESSION_CREATE 函数定义
|
173
|
+
|
174
|
+
:param session: 新创建的会话对象实例
|
175
|
+
"""
|
176
|
+
self._session_create(session)
|
177
|
+
|
178
|
+
def onSessionDestroy(self, session):
|
179
|
+
"""
|
180
|
+
会话关闭时(注意不是断开)对插件执行的操作,由插件文件中的 PLUGIN_SESSION_DESTROY 函数定义
|
181
|
+
|
182
|
+
:param session: 所关闭的会话对象实例
|
183
|
+
"""
|
184
|
+
self._session_destroy(session)
|
185
|
+
|
186
|
+
def __getattr__(self, __name: str) -> Any:
|
187
|
+
if hasattr(self.mod, __name):
|
188
|
+
return self.mod.__getattribute__(__name)
|