pymud 0.20.0a1__py3-none-any.whl → 0.20.0a3__py3-none-any.whl
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/__init__.py +2 -1
- pymud/__main__.py +2 -138
- pymud/logger.py +1 -1
- pymud/main.py +141 -0
- pymud/pymud.py +72 -49
- pymud/session.py +264 -58
- pymud/settings.py +1 -1
- {pymud-0.20.0a1.dist-info → pymud-0.20.0a3.dist-info}/METADATA +23 -4
- pymud-0.20.0a3.dist-info/RECORD +18 -0
- {pymud-0.20.0a1.dist-info → pymud-0.20.0a3.dist-info}/WHEEL +1 -1
- pymud-0.20.0a1.dist-info/RECORD +0 -17
- {pymud-0.20.0a1.dist-info → pymud-0.20.0a3.dist-info}/LICENSE.txt +0 -0
- {pymud-0.20.0a1.dist-info → pymud-0.20.0a3.dist-info}/entry_points.txt +0 -0
- {pymud-0.20.0a1.dist-info → pymud-0.20.0a3.dist-info}/top_level.txt +0 -0
pymud/__init__.py
CHANGED
@@ -4,7 +4,8 @@ from .objects import CodeBlock, Alias, SimpleAlias, Trigger, SimpleTrigger, Comm
|
|
4
4
|
from .extras import DotDict
|
5
5
|
from .session import Session
|
6
6
|
from .logger import Logger
|
7
|
+
from .main import main
|
7
8
|
|
8
9
|
__all__ = [
|
9
|
-
"PyMudApp", "Settings", "CodeBlock", "Alias", "SimpleAlias", "Trigger", "SimpleTrigger", "Command", "SimpleCommand", "Timer", "SimpleTimer", "GMCPTrigger", "Session", "PyMudApp", "DotDict", "Logger"
|
10
|
+
"PyMudApp", "Settings", "CodeBlock", "Alias", "SimpleAlias", "Trigger", "SimpleTrigger", "Command", "SimpleCommand", "Timer", "SimpleTimer", "GMCPTrigger", "Session", "PyMudApp", "DotDict", "Logger", "main"
|
10
11
|
]
|
pymud/__main__.py
CHANGED
@@ -1,140 +1,4 @@
|
|
1
|
-
|
2
|
-
from .pymud import main
|
3
|
-
from .settings import Settings
|
4
|
-
|
5
|
-
CFG_TEMPLATE = {
|
6
|
-
"client": {
|
7
|
-
"buffer_lines" : 5000, # 保留缓冲行数
|
8
|
-
|
9
|
-
"interval" : 10, # 在自动执行中,两次命令输入中的间隔时间(ms)
|
10
|
-
"auto_connect" : True, # 创建会话后,是否自动连接
|
11
|
-
"auto_reconnect" : False, # 在会话异常断开之后,是否自动重连
|
12
|
-
"var_autosave" : True, # 断开时自动保存会话变量
|
13
|
-
"var_autoload" : True, # 初始化时自动加载会话变量
|
14
|
-
|
15
|
-
"echo_input" : False,
|
16
|
-
"beautify" : True, # 专门为解决控制台下PKUXKX字符画对不齐的问题
|
17
|
-
|
18
|
-
"status_display" : 1, # 状态窗口显示情况设置,0-不显示,1-显示在下方,2-显示在右侧
|
19
|
-
"status_height" : 4, # 下侧状态栏的高度
|
20
|
-
"status_width" : 30, # 右侧状态栏的宽度
|
21
|
-
|
22
|
-
},
|
23
|
-
"sessions" : {
|
24
|
-
"pkuxkx" : {
|
25
|
-
"host" : "mud.pkuxkx.net",
|
26
|
-
"port" : "8081",
|
27
|
-
"encoding" : "utf8",
|
28
|
-
"autologin" : "{0};{1}",
|
29
|
-
"default_script": "examples",
|
30
|
-
"chars" : {
|
31
|
-
"display_title" : ["yourid", "yourpassword", ""],
|
32
|
-
}
|
33
|
-
}
|
34
|
-
},
|
35
|
-
"keys" : {
|
36
|
-
"f3" : "#ig",
|
37
|
-
"f4" : "#clear",
|
38
|
-
"f11" : "#close",
|
39
|
-
"f12" : "#exit",
|
40
|
-
}
|
41
|
-
}
|
42
|
-
|
43
|
-
def init_pymud_env(args):
|
44
|
-
print(f"欢迎使用PyMUD, 版本{Settings.__version__}. 使用PyMUD时, 建议建立一个新目录(任意位置),并将自己的脚本以及配置文件放到该目录下.")
|
45
|
-
print("即将开始为首次运行初始化环境...")
|
46
|
-
system = platform.system().lower()
|
47
|
-
|
48
|
-
dir = args.dir
|
49
|
-
if dir:
|
50
|
-
print(f"你已经指定了创建脚本的目录为 {args.dir}, 将不再检测操作系统")
|
51
|
-
|
52
|
-
else:
|
53
|
-
if system == "windows":
|
54
|
-
dir = input("检测到当前系统为Windows, 请指定游戏脚本的目录(若目录不存在会自动创建),直接回车表示使用默认值[d:\pkuxkx]:")
|
55
|
-
if not dir: dir = "d:\\pkuxkx"
|
56
|
-
|
57
|
-
elif system == "linux":
|
58
|
-
dir = input("检测到当前系统为Linux, 请指定游戏脚本的目录(若目录不存在会自动创建),直接回车表示使用默认值[~/pkuxkx]:")
|
59
|
-
if not dir: dir = "~/pkuxkx"
|
60
|
-
|
61
|
-
elif system == "darwin":
|
62
|
-
dir = input("检测到当前系统为MacOS, 请指定游戏脚本的目录(若目录不存在会自动创建),直接回车表示使用默认值[~/pkuxkx]:")
|
63
|
-
if not dir: dir = "~/pkuxkx"
|
64
|
-
|
65
|
-
else:
|
66
|
-
print(f"当前系统不是Windows、Linux或MacOS, 无法通过init来进行配置, 请手动配置. 默认配置即将推出")
|
67
|
-
|
68
|
-
|
69
|
-
if not os.path.exists(dir):
|
70
|
-
print(f'检测到给定目录 {dir} 不存在,正在创建目录...', end = "")
|
71
|
-
os.mkdir(dir)
|
72
|
-
os.chdir(dir)
|
73
|
-
print(f'完成!')
|
74
|
-
|
75
|
-
if os.path.exists('pymud.cfg'):
|
76
|
-
print(f'检测到脚本目录下已存在pymud.cfg文件,将直接使用此文件进入PyMUD...')
|
77
|
-
else:
|
78
|
-
print(f'检测到脚本目录下不存在pymud.cfg文件,将使用默认内容创建该配置文件...')
|
79
|
-
with open('pymud.cfg', mode = 'x') as fp:
|
80
|
-
fp.writelines(json.dumps(CFG_TEMPLATE, indent = 4))
|
81
|
-
|
82
|
-
if not os.path.exists('examples.py'):
|
83
|
-
from pymud import pkuxkx
|
84
|
-
module_dir = pkuxkx.__file__
|
85
|
-
shutil.copyfile(module_dir, 'examples.py')
|
86
|
-
print(f'已将样例脚本拷贝至脚本目录,并加入默认配置文件')
|
87
|
-
|
88
|
-
print(f"后续可自行修改 {dir} 目录下的 pymud.cfg 文件以进行配置。")
|
89
|
-
if system == "windows":
|
90
|
-
print(f"后续运行PyMUD, 请在 {dir} 目录下键入命令: python -m pymud")
|
91
|
-
else:
|
92
|
-
print(f"后续运行PyMUD, 请在 {dir} 目录下键入命令: python3 -m pymud")
|
93
|
-
|
94
|
-
input('所有内容已初始化完毕, 请按回车进入PyMUD.')
|
95
|
-
|
96
|
-
module_entry(args)
|
97
|
-
|
98
|
-
def module_entry(args):
|
99
|
-
if args.debug:
|
100
|
-
logging.basicConfig(level = logging.NOTSET,
|
101
|
-
format = '%(asctime)s %(name)-12s %(levelname)-8s %(message)s',
|
102
|
-
datefmt = '%m-%d %H:%M',
|
103
|
-
filename = args.logfile,
|
104
|
-
filemode = 'a' if args.filemode else 'w',
|
105
|
-
encoding = "utf-8"
|
106
|
-
)
|
107
|
-
|
108
|
-
else:
|
109
|
-
logging.basicConfig(level = logging.NOTSET,
|
110
|
-
format = '%(asctime)s %(name)-12s: %(message)s',
|
111
|
-
datefmt = '%m-%d %H:%M',
|
112
|
-
handlers = [logging.NullHandler()],
|
113
|
-
)
|
114
|
-
|
115
|
-
cfg = "pymud.cfg"
|
116
|
-
if os.path.exists(cfg):
|
117
|
-
with open(cfg, "r", encoding="utf8", errors="ignore") as fp:
|
118
|
-
cfg_data = json.load(fp)
|
119
|
-
main(cfg_data)
|
120
|
-
else:
|
121
|
-
main()
|
122
|
-
|
1
|
+
from .main import main
|
123
2
|
|
124
3
|
if __name__ == "__main__":
|
125
|
-
|
126
|
-
subparsers = parser.add_subparsers(help = 'init用于初始化运行环境')
|
127
|
-
|
128
|
-
par_init = subparsers.add_parser('init', usage = "python -m pymud init [-h] [-d dir]", description = '初始化pymud运行环境, 包括建立脚本目录, 创建默认配置文件, 创建样例脚本等.')
|
129
|
-
par_init.add_argument('-d', '--dir', dest = 'dir', metavar = 'dir', type = str, default = '', help = '指定构建脚本目录的名称, 不指定时会根据操作系统选择不同默认值')
|
130
|
-
par_init.set_defaults(func = init_pymud_env)
|
131
|
-
|
132
|
-
parser.add_argument('-d', '--debug', dest = 'debug', action = 'store_true', default = False, help = '指定以调试模式进入PyMUD。此时,系统log等级将设置为logging.NOTSET, 所有log数据均会被记录。默认不启用。')
|
133
|
-
parser.add_argument('-l', '--logfile', dest = 'logfile', metavar = 'logfile', default = 'pymud.log', help = '指定调试模式下记录文件名,不指定时,默认为当前目录下的pymud.log')
|
134
|
-
parser.add_argument('-a', '--appendmode', dest = 'filemode', action = 'store_true', default = True, help = '指定log文件的访问模式是否为append尾部添加模式,默认为True。当为False时,使用w模式,即每次运行清空之前记录')
|
135
|
-
|
136
|
-
args=parser.parse_args()
|
137
|
-
if hasattr(args, 'func'):
|
138
|
-
args.func(args)
|
139
|
-
else:
|
140
|
-
module_entry(args)
|
4
|
+
main()
|
pymud/logger.py
CHANGED
@@ -15,7 +15,7 @@ class Logger:
|
|
15
15
|
:param raw: 记录带ANSI标记的原始内容,还是记录纯文本内容,默认为True,即记录带ANSI标记的原始内容。
|
16
16
|
"""
|
17
17
|
|
18
|
-
_esc_regx = re.compile("\x1b
|
18
|
+
_esc_regx = re.compile(r"\x1b\[[\d;]+[abcdmz]", flags = re.IGNORECASE)
|
19
19
|
|
20
20
|
def __init__(self, name, mode = 'a', encoding = "utf-8", errors = "ignore", raw = False):
|
21
21
|
self._name = name
|
pymud/main.py
ADDED
@@ -0,0 +1,141 @@
|
|
1
|
+
import os, sys, json, platform, shutil, logging, argparse
|
2
|
+
from pathlib import Path
|
3
|
+
from .pymud import PyMudApp
|
4
|
+
from .settings import Settings
|
5
|
+
|
6
|
+
CFG_TEMPLATE = {
|
7
|
+
"client": {
|
8
|
+
"buffer_lines" : 5000, # 保留缓冲行数
|
9
|
+
|
10
|
+
"interval" : 10, # 在自动执行中,两次命令输入中的间隔时间(ms)
|
11
|
+
"auto_connect" : True, # 创建会话后,是否自动连接
|
12
|
+
"auto_reconnect" : False, # 在会话异常断开之后,是否自动重连
|
13
|
+
"var_autosave" : True, # 断开时自动保存会话变量
|
14
|
+
"var_autoload" : True, # 初始化时自动加载会话变量
|
15
|
+
|
16
|
+
"echo_input" : False,
|
17
|
+
"beautify" : True, # 专门为解决控制台下PKUXKX字符画对不齐的问题
|
18
|
+
|
19
|
+
"status_display" : 1, # 状态窗口显示情况设置,0-不显示,1-显示在下方,2-显示在右侧
|
20
|
+
"status_height" : 4, # 下侧状态栏的高度
|
21
|
+
"status_width" : 30, # 右侧状态栏的宽度
|
22
|
+
|
23
|
+
},
|
24
|
+
"sessions" : {
|
25
|
+
"pkuxkx" : {
|
26
|
+
"host" : "mud.pkuxkx.net",
|
27
|
+
"port" : "8081",
|
28
|
+
"encoding" : "utf8",
|
29
|
+
"autologin" : "{0};{1}",
|
30
|
+
"default_script": "examples",
|
31
|
+
"chars" : {
|
32
|
+
"display_title" : ["yourid", "yourpassword", ""],
|
33
|
+
}
|
34
|
+
}
|
35
|
+
},
|
36
|
+
"keys" : {
|
37
|
+
"f3" : "#ig",
|
38
|
+
"f4" : "#clear",
|
39
|
+
"f11" : "#close",
|
40
|
+
"f12" : "#exit",
|
41
|
+
}
|
42
|
+
}
|
43
|
+
|
44
|
+
def init_pymud_env(args):
|
45
|
+
print(f"欢迎使用PyMUD, 版本{Settings.__version__}. 使用PyMUD时, 建议建立一个新目录(任意位置),并将自己的脚本以及配置文件放到该目录下.")
|
46
|
+
print("即将开始为首次运行初始化环境...")
|
47
|
+
|
48
|
+
dir = args.dir
|
49
|
+
if dir:
|
50
|
+
print(f"你已经指定了创建脚本的目录为 {args.dir}")
|
51
|
+
dir = Path(dir)
|
52
|
+
else:
|
53
|
+
dir = Path.home().joinpath('pkuxkx')
|
54
|
+
|
55
|
+
system = platform.system().lower()
|
56
|
+
dir_enter = input(f"检测到当前系统为 {system}, 请指定游戏脚本的目录(若目录不存在会自动创建),直接回车表示使用默认值 [{dir}]:")
|
57
|
+
if dir_enter:
|
58
|
+
dir = Path(dir_enter)
|
59
|
+
|
60
|
+
if dir.exists() and dir.is_dir():
|
61
|
+
print(f'检测到给定目录 {dir} 已存在,切换至此目录...')
|
62
|
+
else:
|
63
|
+
print(f'检测到给定目录 {dir} 不存在,正在创建并切换至目录...')
|
64
|
+
dir.mkdir()
|
65
|
+
|
66
|
+
os.chdir(dir)
|
67
|
+
|
68
|
+
if os.path.exists('pymud.cfg'):
|
69
|
+
print(f'检测到脚本目录下已存在pymud.cfg文件,将直接使用此文件进入PyMUD...')
|
70
|
+
else:
|
71
|
+
print(f'检测到脚本目录下不存在pymud.cfg文件,将使用默认内容创建该配置文件...')
|
72
|
+
with open('pymud.cfg', mode = 'x') as fp:
|
73
|
+
fp.writelines(json.dumps(CFG_TEMPLATE, indent = 4))
|
74
|
+
|
75
|
+
if not os.path.exists('examples.py'):
|
76
|
+
from pymud import pkuxkx
|
77
|
+
module_dir = pkuxkx.__file__
|
78
|
+
shutil.copyfile(module_dir, 'examples.py')
|
79
|
+
print(f'已将样例脚本拷贝至脚本目录,并加入默认配置文件')
|
80
|
+
|
81
|
+
print(f"后续可自行修改 {dir} 目录下的 pymud.cfg 文件以进行配置。")
|
82
|
+
if system == "windows":
|
83
|
+
print(f"后续运行PyMUD, 请在 {dir} 目录下键入命令: python -m pymud")
|
84
|
+
else:
|
85
|
+
print(f"后续运行PyMUD, 请在 {dir} 目录下键入命令: python3 -m pymud")
|
86
|
+
|
87
|
+
input('所有内容已初始化完毕, 请按回车进入PyMUD.')
|
88
|
+
|
89
|
+
startApp(args)
|
90
|
+
|
91
|
+
def startApp(args):
|
92
|
+
if args.debug:
|
93
|
+
logging.basicConfig(level = logging.NOTSET,
|
94
|
+
format = '%(asctime)s %(name)-12s %(levelname)-8s %(message)s',
|
95
|
+
datefmt = '%m-%d %H:%M',
|
96
|
+
filename = args.logfile,
|
97
|
+
filemode = 'a' if args.filemode else 'w',
|
98
|
+
encoding = "utf-8"
|
99
|
+
)
|
100
|
+
|
101
|
+
else:
|
102
|
+
logging.basicConfig(level = logging.NOTSET,
|
103
|
+
format = '%(asctime)s %(name)-12s: %(message)s',
|
104
|
+
datefmt = '%m-%d %H:%M',
|
105
|
+
handlers = [logging.NullHandler()],
|
106
|
+
)
|
107
|
+
|
108
|
+
startup_path = Path(args.startup_dir).resolve()
|
109
|
+
sys.path.append(f"{startup_path}")
|
110
|
+
os.chdir(startup_path)
|
111
|
+
cfg = startup_path.joinpath("pymud.cfg")
|
112
|
+
cfg_data = None
|
113
|
+
if os.path.exists(cfg):
|
114
|
+
with open(cfg, "r", encoding="utf8", errors="ignore") as fp:
|
115
|
+
cfg_data = json.load(fp)
|
116
|
+
|
117
|
+
app = PyMudApp(cfg_data)
|
118
|
+
app.run()
|
119
|
+
|
120
|
+
def main():
|
121
|
+
parser = argparse.ArgumentParser(prog = "pymud", description = "PyMUD命令行参数帮助")
|
122
|
+
subparsers = parser.add_subparsers(help = 'init用于初始化运行环境')
|
123
|
+
|
124
|
+
par_init = subparsers.add_parser('init', description = '初始化pymud运行环境, 包括建立脚本目录, 创建默认配置文件, 创建样例脚本等.')
|
125
|
+
par_init.add_argument('-d', '--dir', dest = 'dir', metavar = 'dir', type = str, default = '', help = '指定构建脚本目录的名称, 不指定时会根据操作系统选择不同默认值')
|
126
|
+
par_init.set_defaults(func = init_pymud_env)
|
127
|
+
|
128
|
+
parser.add_argument('-d', '--debug', dest = 'debug', action = 'store_true', default = False, help = '指定以调试模式进入PyMUD。此时,系统log等级将设置为logging.NOTSET, 所有log数据均会被记录。默认不启用。')
|
129
|
+
parser.add_argument('-l', '--logfile', dest = 'logfile', metavar = 'logfile', default = 'pymud.log', help = '指定调试模式下记录文件名,不指定时,默认为当前目录下的pymud.log')
|
130
|
+
parser.add_argument('-a', '--appendmode', dest = 'filemode', action = 'store_true', default = True, help = '指定log文件的访问模式是否为append尾部添加模式,默认为True。当为False时,使用w模式,即每次运行清空之前记录')
|
131
|
+
parser.add_argument('-s', '--startup_dir', dest = 'startup_dir', metavar = 'startup_dir', default = '.', help = '指定启动目录,默认为当前目录。使用该参数可以在任何目录下,通过指定脚本目录来启动')
|
132
|
+
|
133
|
+
args=parser.parse_args()
|
134
|
+
|
135
|
+
if hasattr(args, 'func'):
|
136
|
+
args.func(args)
|
137
|
+
else:
|
138
|
+
startApp(args)
|
139
|
+
|
140
|
+
if __name__ == "__main__":
|
141
|
+
main()
|
pymud/pymud.py
CHANGED
@@ -318,8 +318,8 @@ class PyMudApp:
|
|
318
318
|
),
|
319
319
|
|
320
320
|
MenuItem(
|
321
|
-
"", # 增加一个空名称MenuItem
|
322
|
-
|
321
|
+
"", # 增加一个空名称MenuItem,单机后焦点移动至命令行输入处,阻止右侧空白栏点击响应
|
322
|
+
handler = lambda : self.app.layout.focus(self.commandLine)
|
323
323
|
)
|
324
324
|
],
|
325
325
|
floats=[
|
@@ -340,34 +340,9 @@ class PyMudApp:
|
|
340
340
|
ss = Settings.sessions
|
341
341
|
|
342
342
|
for key, site in ss.items():
|
343
|
-
host = site["host"]
|
344
|
-
port = site["port"]
|
345
|
-
encoding = site["encoding"]
|
346
|
-
autologin = site["autologin"]
|
347
|
-
scripts = list()
|
348
|
-
default_script = site["default_script"]
|
349
|
-
|
350
|
-
def_scripts = list()
|
351
|
-
if isinstance(default_script, str):
|
352
|
-
def_scripts.extend(default_script.split(","))
|
353
|
-
elif isinstance(default_script, (list, tuple)):
|
354
|
-
def_scripts.extend(default_script)
|
355
|
-
|
356
343
|
menu = MenuItem(key)
|
357
|
-
for name
|
358
|
-
|
359
|
-
sess_scripts = list()
|
360
|
-
sess_scripts.extend(def_scripts)
|
361
|
-
|
362
|
-
if len(info) == 3:
|
363
|
-
session_script = info[2]
|
364
|
-
if session_script:
|
365
|
-
if isinstance(session_script, str):
|
366
|
-
sess_scripts.extend(session_script.split(","))
|
367
|
-
elif isinstance(session_script, (list, tuple)):
|
368
|
-
sess_scripts.extend(session_script)
|
369
|
-
|
370
|
-
sub = MenuItem(name, handler = functools.partial(self.create_session, name, host, port, encoding, after_connect, sess_scripts, info[0]))
|
344
|
+
for name in site["chars"].keys():
|
345
|
+
sub = MenuItem(name, handler = functools.partial(self._quickHandleSession, key, name))
|
371
346
|
menu.children.append(sub)
|
372
347
|
menus.append(menu)
|
373
348
|
|
@@ -506,7 +481,7 @@ class PyMudApp:
|
|
506
481
|
line = self.mudFormatProc.line_correction(b.document.current_line)
|
507
482
|
start = max(0, scol)
|
508
483
|
end = min(ecol, len(line))
|
509
|
-
line_plain = re.sub("\x1b
|
484
|
+
line_plain = re.sub(r"\x1b\[[\d;]+[abcdmz]", "", line, flags = re.IGNORECASE).replace("\r", "").replace("\x00", "")
|
510
485
|
#line_plain = re.sub("\x1b\\[[^mz]+[mz]", "", line).replace("\r", "").replace("\x00", "")
|
511
486
|
selection = line_plain[start:end]
|
512
487
|
self.app.clipboard.set_text(selection)
|
@@ -518,7 +493,7 @@ class PyMudApp:
|
|
518
493
|
lines = []
|
519
494
|
for row in range(srow, erow + 1):
|
520
495
|
line = b.document.lines[row]
|
521
|
-
line_plain = re.sub("\x1b
|
496
|
+
line_plain = re.sub(r"\x1b\[[\d;]+[abcdmz]", "", line, flags = re.IGNORECASE).replace("\r", "").replace("\x00", "")
|
522
497
|
lines.append(line_plain)
|
523
498
|
|
524
499
|
self.app.clipboard.set_text("\n".join(lines))
|
@@ -663,6 +638,7 @@ class PyMudApp:
|
|
663
638
|
plugin.onSessionDestroy(self.current_session)
|
664
639
|
|
665
640
|
name = self.current_session.name
|
641
|
+
self.current_session.closeLoggers()
|
666
642
|
self.current_session.clean()
|
667
643
|
self.current_session = None
|
668
644
|
self.consoleView.buffer = SessionBuffer()
|
@@ -918,6 +894,50 @@ class PyMudApp:
|
|
918
894
|
self.status_message = msg
|
919
895
|
self.app.invalidate()
|
920
896
|
|
897
|
+
def _quickHandleSession(self, group, name):
|
898
|
+
'''
|
899
|
+
根据指定的组名和会话角色名,从Settings内容,创建一个会话
|
900
|
+
'''
|
901
|
+
handled = False
|
902
|
+
if name in self.sessions.keys():
|
903
|
+
self.activate_session(name)
|
904
|
+
handled = True
|
905
|
+
|
906
|
+
else:
|
907
|
+
site = Settings.sessions[group]
|
908
|
+
if name in site["chars"].keys():
|
909
|
+
host = site["host"]
|
910
|
+
port = site["port"]
|
911
|
+
encoding = site["encoding"]
|
912
|
+
autologin = site["autologin"]
|
913
|
+
default_script = site["default_script"]
|
914
|
+
|
915
|
+
def_scripts = list()
|
916
|
+
if isinstance(default_script, str):
|
917
|
+
def_scripts.extend(default_script.split(","))
|
918
|
+
elif isinstance(default_script, (list, tuple)):
|
919
|
+
def_scripts.extend(default_script)
|
920
|
+
|
921
|
+
charinfo = site["chars"][name]
|
922
|
+
|
923
|
+
after_connect = autologin.format(charinfo[0], charinfo[1])
|
924
|
+
sess_scripts = list()
|
925
|
+
sess_scripts.extend(def_scripts)
|
926
|
+
|
927
|
+
if len(charinfo) == 3:
|
928
|
+
session_script = charinfo[2]
|
929
|
+
if session_script:
|
930
|
+
if isinstance(session_script, str):
|
931
|
+
sess_scripts.extend(session_script.split(","))
|
932
|
+
elif isinstance(session_script, (list, tuple)):
|
933
|
+
sess_scripts.extend(session_script)
|
934
|
+
|
935
|
+
self.create_session(name, host, port, encoding, after_connect, sess_scripts, charinfo[0])
|
936
|
+
handled = True
|
937
|
+
|
938
|
+
return handled
|
939
|
+
|
940
|
+
|
921
941
|
def handle_session(self, *args):
|
922
942
|
'''
|
923
943
|
嵌入命令 #session 的执行函数,创建一个远程连接会话。
|
@@ -928,12 +948,18 @@ class PyMudApp:
|
|
928
948
|
- 当不指定 Encoding: 时, 默认使用utf-8编码
|
929
949
|
- 可以直接使用 #{名称} 切换会话和操作会话命令
|
930
950
|
|
951
|
+
- #session {group}.{name}
|
952
|
+
- 相当于直接点击菜单{group}下的{name}菜单来创建会话. 当该会话已存在时,切换到该会话
|
953
|
+
|
931
954
|
参数:
|
932
955
|
:name: 会话名称
|
933
956
|
:host: 服务器域名或IP地址
|
934
957
|
:port: 端口号
|
935
958
|
:encoding: 编码格式,不指定时默认为 utf8
|
936
959
|
|
960
|
+
:group: 组名, 即配置文件中, sessions 字段下的某个关键字
|
961
|
+
:name: 会话快捷名称, 上述 group 关键字下的 chars 字段中的某个关键字
|
962
|
+
|
937
963
|
示例:
|
938
964
|
``#session {名称} {宿主机} {端口} {编码}``
|
939
965
|
创建一个远程连接会话,使用指定编码格式连接到远程宿主机的指定端口并保存为 {名称} 。其中,编码可以省略,此时使用Settings.server["default_encoding"]的值,默认为utf8
|
@@ -946,6 +972,9 @@ class PyMudApp:
|
|
946
972
|
``#newstart give miui gold``
|
947
973
|
使名称为newstart的会话执行give miui gold指令,但不切换到该会话
|
948
974
|
|
975
|
+
``#session pkuxkx.newstart``
|
976
|
+
通过指定快捷配置创建会话,相当于点击 世界->pkuxkx->newstart 菜单创建会话。若该会话存在,则切换到该会话
|
977
|
+
|
949
978
|
相关命令:
|
950
979
|
- #close
|
951
980
|
- #exit
|
@@ -953,8 +982,17 @@ class PyMudApp:
|
|
953
982
|
'''
|
954
983
|
|
955
984
|
nothandle = True
|
985
|
+
errmsg = "错误的#session命令"
|
986
|
+
if len(args) == 1:
|
987
|
+
host_session = args[0]
|
988
|
+
if '.' in host_session:
|
989
|
+
group, name = host_session.split('.')
|
990
|
+
nothandle = not self._quickHandleSession(group, name)
|
956
991
|
|
957
|
-
|
992
|
+
else:
|
993
|
+
errmsg = f'通过单一参数快速创建会话时,要使用 group.name 形式,如 #session pkuxkx.newstart'
|
994
|
+
|
995
|
+
elif len(args) >= 3:
|
958
996
|
session_name = args[0]
|
959
997
|
session_host = args[1]
|
960
998
|
session_port = int(args[2])
|
@@ -967,7 +1005,7 @@ class PyMudApp:
|
|
967
1005
|
nothandle = False
|
968
1006
|
|
969
1007
|
if nothandle:
|
970
|
-
self.set_status(
|
1008
|
+
self.set_status(errmsg)
|
971
1009
|
|
972
1010
|
def enter_pressed(self, buffer: Buffer):
|
973
1011
|
"命令行回车按键处理"
|
@@ -1161,21 +1199,6 @@ class PyMudApp:
|
|
1161
1199
|
plugin.onSessionCreate(session)
|
1162
1200
|
|
1163
1201
|
|
1164
|
-
def
|
1202
|
+
def startApp(cfg_data = None):
|
1165
1203
|
app = PyMudApp(cfg_data)
|
1166
1204
|
app.run()
|
1167
|
-
|
1168
|
-
if __name__ == "__main__":
|
1169
|
-
|
1170
|
-
cfg = "pymud.cfg"
|
1171
|
-
import sys
|
1172
|
-
args = sys.argv
|
1173
|
-
if len(args) > 1:
|
1174
|
-
cfg = args[1]
|
1175
|
-
|
1176
|
-
if os.path.exists(cfg):
|
1177
|
-
with open(cfg, "r", encoding="utf8", errors="ignore") as fp:
|
1178
|
-
cfg_data = json.load(fp)
|
1179
|
-
main(cfg_data)
|
1180
|
-
else:
|
1181
|
-
main()
|
pymud/session.py
CHANGED
@@ -8,7 +8,7 @@ from logging.handlers import QueueHandler, QueueListener
|
|
8
8
|
from .logger import Logger
|
9
9
|
from .extras import SessionBuffer, DotDict, Plugin
|
10
10
|
from .protocol import MudClientProtocol
|
11
|
-
from .objects import Trigger, Alias, Command, Timer, SimpleAlias, SimpleTrigger, SimpleTimer, GMCPTrigger, CodeBlock, CodeLine
|
11
|
+
from .objects import BaseObject, Trigger, Alias, Command, Timer, SimpleAlias, SimpleTrigger, SimpleTimer, GMCPTrigger, CodeBlock, CodeLine
|
12
12
|
from .settings import Settings
|
13
13
|
|
14
14
|
|
@@ -29,13 +29,14 @@ class Session:
|
|
29
29
|
|
30
30
|
"""
|
31
31
|
#_esc_regx = re.compile("\x1b\\[[^mz]+[mz]")
|
32
|
-
_esc_regx = re.compile("\x1b
|
32
|
+
_esc_regx = re.compile(r"\x1b\[[\d;]+[abcdmz]", flags = re.IGNORECASE)
|
33
33
|
|
34
34
|
_sys_commands = (
|
35
35
|
"help",
|
36
36
|
"exit",
|
37
37
|
"close",
|
38
38
|
"connect", # 连接到服务器
|
39
|
+
"disconnect", # 从服务器断开连接
|
39
40
|
|
40
41
|
"info", # 输出蓝色info
|
41
42
|
"warning", # 输出黄色warning
|
@@ -89,6 +90,7 @@ class Session:
|
|
89
90
|
"var" : "variable",
|
90
91
|
"rep" : "repeat",
|
91
92
|
"con" : "connect",
|
93
|
+
"dis" : "disconnect",
|
92
94
|
"wa" : "wait",
|
93
95
|
"mess": "message",
|
94
96
|
"action": "trigger",
|
@@ -116,9 +118,6 @@ class Session:
|
|
116
118
|
self._events["connected"] = None
|
117
119
|
self._events["disconnected"] = None
|
118
120
|
|
119
|
-
self._loggers = dict()
|
120
|
-
self.log = self.getLogger(name)
|
121
|
-
|
122
121
|
self._auto_script = kwargs.get("scripts", None)
|
123
122
|
|
124
123
|
self._cmds_handler = dict() # 支持的命令的处理函数字典
|
@@ -144,6 +143,9 @@ class Session:
|
|
144
143
|
|
145
144
|
self.initialize()
|
146
145
|
|
146
|
+
self._loggers = dict()
|
147
|
+
self.log = self.getLogger(name)
|
148
|
+
|
147
149
|
self.host = host
|
148
150
|
self.port = port
|
149
151
|
self.encoding = encoding or self.encoding
|
@@ -176,6 +178,10 @@ class Session:
|
|
176
178
|
if Settings.client["auto_connect"]:
|
177
179
|
self.open()
|
178
180
|
|
181
|
+
def __del__(self):
|
182
|
+
self.clean()
|
183
|
+
self.closeLoggers()
|
184
|
+
|
179
185
|
def initialize(self):
|
180
186
|
"初始化Session有关对象。 **无需脚本调用。**"
|
181
187
|
self._line_buffer = bytearray()
|
@@ -376,6 +382,15 @@ class Session:
|
|
376
382
|
|
377
383
|
return logger
|
378
384
|
|
385
|
+
def closeLoggers(self):
|
386
|
+
"移除本会话所有相关Logger"
|
387
|
+
for name in self._loggers.keys():
|
388
|
+
if isinstance(self._loggers[name], Logger):
|
389
|
+
self._loggers[name].enabled = False
|
390
|
+
|
391
|
+
if name in self.application.loggers.keys():
|
392
|
+
self.application.loggers.pop(name)
|
393
|
+
|
379
394
|
@property
|
380
395
|
def modules(self) -> OrderedDict:
|
381
396
|
"""
|
@@ -758,6 +773,27 @@ class Session:
|
|
758
773
|
cmd = line + self.newline
|
759
774
|
self.write(cmd.encode(self.encoding, Settings.server["encoding_errors"]))
|
760
775
|
|
776
|
+
async def waitfor(self, line: str, awaitable, wait_time = 0.05) -> None:
|
777
|
+
"""
|
778
|
+
调用writline向服务器中写入一行后,等待到可等待对象再返回。
|
779
|
+
|
780
|
+
:param line: 使用writeline写入的行
|
781
|
+
:param awaitable: 等待的可等待对象
|
782
|
+
:param wait_time: 写入行前等待的延时,单位为s。默认0.05
|
783
|
+
|
784
|
+
由于异步的消息循环机制,如果在写入命令之后再创建可等待对象,则有可能服务器响应在可等待对象的创建之前
|
785
|
+
此时使用await就无法等待到可等待对象的响应,会导致任务出错。
|
786
|
+
一种解决方式是先创建可等待对象,然后写入命令,然后再等待可等待对象,但这种情况下需要写入三行代码,书写麻烦
|
787
|
+
因此该函数是用于简化此类使用时的写法。
|
788
|
+
|
789
|
+
示例:
|
790
|
+
await session.waitfor('a_cmd', self.create_task(a_tri.triggered()))
|
791
|
+
done, pending = await session.waitfor('a_cmd', asyncio.wait([self.create_task(a_tri.triggered()), self.create_task(b_tri.triggered())], return_when = 'FIRST_COMPLETED'))
|
792
|
+
"""
|
793
|
+
await asyncio.sleep(wait_time)
|
794
|
+
self.writeline(line)
|
795
|
+
return await awaitable
|
796
|
+
|
761
797
|
def exec(self, cmd: str, name = None, *args, **kwargs):
|
762
798
|
"""
|
763
799
|
在名称为name的会话中使用exec_command执行MUD命令。当不指定name时,在当前会话中执行。
|
@@ -774,7 +810,7 @@ class Session:
|
|
774
810
|
示例:
|
775
811
|
.. code:: Python
|
776
812
|
|
777
|
-
session.addAlias(SimpleAlias(self.session, "^cb\s(\S+)\s(\S+)", "#3 get %1 from jinnang;#wa 250;combine gem;#wa 250;pack gem", id = "ali_combine"))
|
813
|
+
session.addAlias(SimpleAlias(self.session, r"^cb\s(\S+)\s(\S+)", "#3 get %1 from jinnang;#wa 250;combine gem;#wa 250;pack gem", id = "ali_combine"))
|
778
814
|
session.exec("cb j1a")
|
779
815
|
"""
|
780
816
|
name = name or self.name
|
@@ -1107,31 +1143,100 @@ class Session:
|
|
1107
1143
|
|
1108
1144
|
return counts
|
1109
1145
|
|
1110
|
-
def _addObjects(self, objs: dict, cls: type):
|
1111
|
-
|
1112
|
-
|
1113
|
-
|
1114
|
-
|
1115
|
-
|
1116
|
-
|
1117
|
-
|
1118
|
-
|
1119
|
-
|
1120
|
-
|
1121
|
-
|
1122
|
-
def
|
1123
|
-
|
1124
|
-
|
1125
|
-
|
1126
|
-
|
1127
|
-
|
1128
|
-
|
1129
|
-
|
1130
|
-
|
1131
|
-
|
1132
|
-
|
1133
|
-
|
1134
|
-
|
1146
|
+
# def _addObjects(self, objs: dict, cls: type):
|
1147
|
+
# if cls == Alias:
|
1148
|
+
# self._aliases.update(objs)
|
1149
|
+
# elif cls == Command:
|
1150
|
+
# self._commands.update(objs)
|
1151
|
+
# elif cls == Trigger:
|
1152
|
+
# self._triggers.update(objs)
|
1153
|
+
# elif cls == Timer:
|
1154
|
+
# self._timers.update(objs)
|
1155
|
+
# elif cls == GMCPTrigger:
|
1156
|
+
# self._gmcp.update(objs)
|
1157
|
+
|
1158
|
+
def _addObjects(self, objs):
|
1159
|
+
if isinstance(objs, list) or isinstance(objs, tuple):
|
1160
|
+
for item in objs:
|
1161
|
+
self._addObject(item)
|
1162
|
+
|
1163
|
+
elif isinstance(objs, dict):
|
1164
|
+
for key, item in objs.items():
|
1165
|
+
if isinstance(item, BaseObject):
|
1166
|
+
if key != item.id:
|
1167
|
+
self.warning(f'对象 {item} 字典键值 {key} 与其id {item.id} 不一致,将丢弃键值,以其id添加到会话中...')
|
1168
|
+
|
1169
|
+
self._addObject(item)
|
1170
|
+
|
1171
|
+
# def _addObject(self, obj, cls: type):
|
1172
|
+
# #if type(obj) == cls:
|
1173
|
+
# if isinstance(obj, cls):
|
1174
|
+
# if cls == Alias:
|
1175
|
+
# self._aliases[obj.id] = obj
|
1176
|
+
# elif cls == Command:
|
1177
|
+
# self._commands[obj.id] = obj
|
1178
|
+
# elif cls == Trigger:
|
1179
|
+
# self._triggers[obj.id] = obj
|
1180
|
+
# elif cls == Timer:
|
1181
|
+
# self._timers[obj.id] = obj
|
1182
|
+
# elif cls == GMCPTrigger:
|
1183
|
+
# self._gmcp[obj.id] = obj
|
1184
|
+
|
1185
|
+
def _addObject(self, obj):
|
1186
|
+
if isinstance(obj, Alias):
|
1187
|
+
self._aliases[obj.id] = obj
|
1188
|
+
elif isinstance(obj, Command):
|
1189
|
+
self._commands[obj.id] = obj
|
1190
|
+
elif isinstance(obj, Trigger):
|
1191
|
+
self._triggers[obj.id] = obj
|
1192
|
+
elif isinstance(obj, Timer):
|
1193
|
+
self._timers[obj.id] = obj
|
1194
|
+
elif isinstance(obj, GMCPTrigger):
|
1195
|
+
self._gmcp[obj.id] = obj
|
1196
|
+
|
1197
|
+
def addObject(self, obj: BaseObject):
|
1198
|
+
"""
|
1199
|
+
向会话中增加单个对象,可直接添加 Alias, Trigger, GMCPTrigger, Command, Timer 或它们的子类
|
1200
|
+
|
1201
|
+
:param obj: 特定对象本身,可以为 Alias, Trigger, GMCPTrigger, Command, Timer 或其子类
|
1202
|
+
|
1203
|
+
示例:
|
1204
|
+
.. code:: Python
|
1205
|
+
|
1206
|
+
class Configuration:
|
1207
|
+
def __init__(self, session):
|
1208
|
+
self.session = session
|
1209
|
+
|
1210
|
+
self.session.addObject(SimpleAlias(session, r'^gta$', 'get all'),)
|
1211
|
+
self.session.addObject(SimpleTrigger(session, r'^[> ]*你嘻嘻地笑了起来.+', 'haha'))
|
1212
|
+
self.session.addObject(SimpleTimer(session, 'xixi', timeout = 10))
|
1213
|
+
|
1214
|
+
"""
|
1215
|
+
self._addObject(obj)
|
1216
|
+
|
1217
|
+
def addObjects(self, objs):
|
1218
|
+
"""
|
1219
|
+
向会话中增加多个对象,可直接添加 Alias, Trigger, GMCPTrigger, Command, Timer 或它们的子类的元组、列表或者字典(保持兼容性)
|
1220
|
+
|
1221
|
+
:param objs: 多个特定对象组成的元组、列表或者字典,可以为 Alias, Trigger, GMCPTrigger, Command, Timer 或其子类
|
1222
|
+
|
1223
|
+
示例:
|
1224
|
+
.. code:: Python
|
1225
|
+
|
1226
|
+
class Configuration:
|
1227
|
+
def __init__(self, session):
|
1228
|
+
self.session = session
|
1229
|
+
|
1230
|
+
self.objs = [
|
1231
|
+
SimpleAlias(session, r'^gta$', 'get all;xixi'),
|
1232
|
+
SimpleTrigger(session, r'^[> ]*你嘻嘻地笑了起来.+', 'haha'),
|
1233
|
+
SimpleTimer(session, 'xixi', timeout = 10)
|
1234
|
+
]
|
1235
|
+
|
1236
|
+
self.session.addObjects(self.objs)
|
1237
|
+
|
1238
|
+
"""
|
1239
|
+
self._addObjects(objs)
|
1135
1240
|
|
1136
1241
|
def _delObject(self, id, cls: type):
|
1137
1242
|
if cls == Alias:
|
@@ -1141,16 +1246,103 @@ class Session:
|
|
1141
1246
|
elif cls == Trigger:
|
1142
1247
|
self._triggers.pop(id, None)
|
1143
1248
|
elif cls == Timer:
|
1144
|
-
self._timers.pop(id, None)
|
1249
|
+
timer = self._timers.pop(id, None)
|
1250
|
+
if isinstance(timer, Timer):
|
1251
|
+
timer.enabled = False
|
1145
1252
|
elif cls == GMCPTrigger:
|
1146
1253
|
self._gmcp.pop(id, None)
|
1147
1254
|
|
1255
|
+
# def _delObject(self, obj):
|
1256
|
+
# if isinstance(obj, Alias):
|
1257
|
+
# self._aliases.pop(obj.id, None)
|
1258
|
+
# elif isinstance(obj, Command):
|
1259
|
+
# self._commands.pop(obj.id, None)
|
1260
|
+
# elif isinstance(obj, Trigger):
|
1261
|
+
# self._triggers.pop(obj.id, None)
|
1262
|
+
# elif isinstance(obj, Timer):
|
1263
|
+
# self._timers.pop(obj.id, None)
|
1264
|
+
# elif isinstance(obj, GMCPTrigger):
|
1265
|
+
# self._gmcp.pop(obj.id, None)
|
1266
|
+
|
1148
1267
|
def _delObjects(self, ids: Iterable, cls: type):
|
1149
1268
|
"删除多个指定元素"
|
1150
1269
|
for id in ids:
|
1151
1270
|
self._delObject(id, cls)
|
1152
1271
|
|
1153
|
-
def
|
1272
|
+
def delObject(self, obj):
|
1273
|
+
"""
|
1274
|
+
从会话中移除一个对象,可直接删除 Alias, Trigger, GMCPTrigger, Command, Timer 或它们的子类本身
|
1275
|
+
|
1276
|
+
:param obj: 要删除的多个特定对象组成的元组、列表或者字典,可以为 Alias, Trigger, GMCPTrigger, Command, Timer 或其子类
|
1277
|
+
|
1278
|
+
示例:
|
1279
|
+
.. code:: Python
|
1280
|
+
|
1281
|
+
class Configuration:
|
1282
|
+
def __init__(self, session):
|
1283
|
+
self.session = session
|
1284
|
+
|
1285
|
+
ali = Alias(session, "s", "south", id = "my_ali1")
|
1286
|
+
|
1287
|
+
# 以下几种方式均可将该别名添加到会话
|
1288
|
+
session.addObject(ali)
|
1289
|
+
session.addAlias(ali)
|
1290
|
+
|
1291
|
+
# 以下三种方式均可以删除该别名
|
1292
|
+
session.delObjec(ali)
|
1293
|
+
session.delAlias(ali)
|
1294
|
+
session.delAlias("my_ali1")
|
1295
|
+
|
1296
|
+
"""
|
1297
|
+
if isinstance(obj, Alias):
|
1298
|
+
self._aliases.pop(obj.id, None)
|
1299
|
+
elif isinstance(obj, Command):
|
1300
|
+
self._commands.pop(obj.id, None)
|
1301
|
+
elif isinstance(obj, Trigger):
|
1302
|
+
self._triggers.pop(obj.id, None)
|
1303
|
+
elif isinstance(obj, Timer):
|
1304
|
+
timer = self._timers.pop(obj.id, None)
|
1305
|
+
if isinstance(timer, Timer):
|
1306
|
+
timer.enabled = False
|
1307
|
+
elif isinstance(obj, GMCPTrigger):
|
1308
|
+
self._gmcp.pop(obj.id, None)
|
1309
|
+
|
1310
|
+
def delObjects(self, objs):
|
1311
|
+
"""
|
1312
|
+
从会话中移除一组对象,可直接删除 Alias, Trigger, GMCPTrigger, Command, Timer 或它们的子类的元组、列表或者字典(保持兼容性)
|
1313
|
+
|
1314
|
+
:param objs: 要删除的对象本身,可以为 Alias, Trigger, GMCPTrigger, Command, Timer 或它们的子类
|
1315
|
+
|
1316
|
+
示例:
|
1317
|
+
|
1318
|
+
.. code:: Python
|
1319
|
+
|
1320
|
+
class Configuration:
|
1321
|
+
def __init__(self, session):
|
1322
|
+
self.session = session
|
1323
|
+
|
1324
|
+
self.objs = [
|
1325
|
+
SimpleAlias(session, r'^gta$', 'get all;xixi'),
|
1326
|
+
SimpleTrigger(session, r'^[> ]*你嘻嘻地笑了起来.+', 'haha'),
|
1327
|
+
SimpleTimer(session, 'xixi', timeout = 10)
|
1328
|
+
]
|
1329
|
+
|
1330
|
+
self.session.addObjects(self.objs)
|
1331
|
+
|
1332
|
+
def __unload__(self):
|
1333
|
+
"卸载本模块时,删除所有本模块添加的对象"
|
1334
|
+
self.session.delObjects(self.objs)
|
1335
|
+
|
1336
|
+
"""
|
1337
|
+
if isinstance(objs, list) or isinstance(objs, tuple):
|
1338
|
+
for item in objs:
|
1339
|
+
self.delObject(item)
|
1340
|
+
|
1341
|
+
elif isinstance(objs, dict):
|
1342
|
+
for key, item in objs.items():
|
1343
|
+
self.delObject(item)
|
1344
|
+
|
1345
|
+
def addAliases(self, alis):
|
1154
1346
|
"""
|
1155
1347
|
向会话中增加多个别名
|
1156
1348
|
|
@@ -1171,55 +1363,55 @@ class Session:
|
|
1171
1363
|
self._aliases['my_ali2'] = SimpleAlias(self.session, "s", "south", id = "my_ali2")
|
1172
1364
|
self.session.addAliases(self._aliases)
|
1173
1365
|
"""
|
1174
|
-
self._addObjects(alis
|
1366
|
+
self._addObjects(alis)
|
1175
1367
|
|
1176
|
-
def addCommands(self, cmds
|
1368
|
+
def addCommands(self, cmds):
|
1177
1369
|
"""
|
1178
1370
|
向会话中增加多个命令。使用方法与 addAliases 类似。
|
1179
1371
|
|
1180
1372
|
:param cmds: 多个命令的字典。字典 key 应为每个命令的 id。
|
1181
1373
|
"""
|
1182
|
-
self._addObjects(cmds
|
1374
|
+
self._addObjects(cmds)
|
1183
1375
|
|
1184
|
-
def addTriggers(self, tris
|
1376
|
+
def addTriggers(self, tris):
|
1185
1377
|
"""
|
1186
1378
|
向会话中增加多个触发器。使用方法与 addAliases 类似。
|
1187
1379
|
|
1188
1380
|
:param tris: 多个触发器的字典。字典 key 应为每个触发器的 id。
|
1189
1381
|
"""
|
1190
|
-
self._addObjects(tris
|
1382
|
+
self._addObjects(tris)
|
1191
1383
|
|
1192
|
-
def addGMCPs(self, gmcps
|
1384
|
+
def addGMCPs(self, gmcps):
|
1193
1385
|
"""
|
1194
1386
|
向会话中增加多个GMCPTrigger。使用方法与 addAliases 类似。
|
1195
1387
|
|
1196
1388
|
:param gmcps: 多个GMCPTrigger的字典。字典 key 应为每个GMCPTrigger的 id。
|
1197
1389
|
"""
|
1198
|
-
self._addObjects(gmcps
|
1390
|
+
self._addObjects(gmcps)
|
1199
1391
|
|
1200
|
-
def addTimers(self, tis
|
1392
|
+
def addTimers(self, tis):
|
1201
1393
|
"""
|
1202
1394
|
向会话中增加多个定时器。使用方法与 addAliases 类似。
|
1203
1395
|
|
1204
1396
|
:param tis: 多个定时器的字典。字典 key 应为每个定时器的 id。
|
1205
1397
|
"""
|
1206
|
-
self._addObjects(tis
|
1398
|
+
self._addObjects(tis)
|
1207
1399
|
|
1208
|
-
def addAlias(self, ali
|
1400
|
+
def addAlias(self, ali):
|
1209
1401
|
"""
|
1210
1402
|
向会话中增加一个别名。
|
1211
1403
|
|
1212
1404
|
:param ali: 要增加的别名对象,应为 Alias 类型或其子类
|
1213
1405
|
"""
|
1214
|
-
self._addObject(ali
|
1406
|
+
self._addObject(ali)
|
1215
1407
|
|
1216
|
-
def addCommand(self, cmd
|
1408
|
+
def addCommand(self, cmd):
|
1217
1409
|
"""
|
1218
1410
|
向会话中增加一个命令。
|
1219
1411
|
|
1220
1412
|
:param cmd: 要增加的命令对象,应为 Command 类型或其子类
|
1221
1413
|
"""
|
1222
|
-
self._addObject(cmd
|
1414
|
+
self._addObject(cmd)
|
1223
1415
|
|
1224
1416
|
def addTrigger(self, tri: Trigger):
|
1225
1417
|
"""
|
@@ -1227,7 +1419,7 @@ class Session:
|
|
1227
1419
|
|
1228
1420
|
:param tri: 要增加的触发器对象,应为 Trigger 类型或其子类
|
1229
1421
|
"""
|
1230
|
-
self._addObject(tri
|
1422
|
+
self._addObject(tri)
|
1231
1423
|
|
1232
1424
|
def addTimer(self, ti: Timer):
|
1233
1425
|
"""
|
@@ -1235,7 +1427,7 @@ class Session:
|
|
1235
1427
|
|
1236
1428
|
:param ti: 要增加的定时器对象,应为 Timer 类型或其子类
|
1237
1429
|
"""
|
1238
|
-
self._addObject(ti
|
1430
|
+
self._addObject(ti)
|
1239
1431
|
|
1240
1432
|
def addGMCP(self, gmcp: GMCPTrigger):
|
1241
1433
|
"""
|
@@ -1244,7 +1436,7 @@ class Session:
|
|
1244
1436
|
:param gmcp: 要增加的GMCP触发器对象,应为 GMCPTrigger 类型或其子类
|
1245
1437
|
"""
|
1246
1438
|
|
1247
|
-
self._addObject(gmcp
|
1439
|
+
self._addObject(gmcp)
|
1248
1440
|
|
1249
1441
|
def delAlias(self, ali):
|
1250
1442
|
"""
|
@@ -1363,7 +1555,7 @@ class Session:
|
|
1363
1555
|
for ti in ti_s:
|
1364
1556
|
self.delTimer(ti)
|
1365
1557
|
|
1366
|
-
def delGMCP(self, gmcp
|
1558
|
+
def delGMCP(self, gmcp):
|
1367
1559
|
"""
|
1368
1560
|
从会话中移除一个GMCP触发器,可接受 GMCPTrigger 对象或其的id。使用方法与 delAlias 类似
|
1369
1561
|
|
@@ -1679,6 +1871,7 @@ class Session:
|
|
1679
1871
|
该函数不应该在代码中直接调用。
|
1680
1872
|
|
1681
1873
|
相关命令:
|
1874
|
+
- #disconnect
|
1682
1875
|
- #close
|
1683
1876
|
- #exit
|
1684
1877
|
'''
|
@@ -1700,6 +1893,18 @@ class Session:
|
|
1700
1893
|
|
1701
1894
|
self.info("已经与服务器连接了 {}".format(time_msg))
|
1702
1895
|
|
1896
|
+
def handle_disconnect(self, code: CodeLine = None, *args, **kwargs):
|
1897
|
+
'''
|
1898
|
+
嵌入命令 #disconnect / #dis 的执行函数,断开到远程服务器的连接(仅当远程服务器已连接时有效)。
|
1899
|
+
该函数不应该在代码中直接调用。
|
1900
|
+
|
1901
|
+
相关命令:
|
1902
|
+
- #connect
|
1903
|
+
- #close
|
1904
|
+
'''
|
1905
|
+
|
1906
|
+
self.disconnect()
|
1907
|
+
|
1703
1908
|
def handle_variable(self, code: CodeLine = None, *args, **kwargs):
|
1704
1909
|
'''
|
1705
1910
|
嵌入命令 #variable / #var 的执行函数,操作会话变量。
|
@@ -1933,7 +2138,7 @@ class Session:
|
|
1933
2138
|
self.info("创建Timer {} 成功: {}".format(ti.id, ti.__repr__()))
|
1934
2139
|
|
1935
2140
|
def handle_alias(self, code: CodeLine = None, *args, **kwargs):
|
1936
|
-
|
2141
|
+
"""
|
1937
2142
|
嵌入命令 #alias / #ali 的执行函数,操作别名。该命令可以不带参数、带一个参数或者两个参数。
|
1938
2143
|
该函数不应该在代码中直接调用。
|
1939
2144
|
|
@@ -1964,7 +2169,7 @@ class Session:
|
|
1964
2169
|
- #trigger
|
1965
2170
|
- #timer
|
1966
2171
|
- #command
|
1967
|
-
|
2172
|
+
"""
|
1968
2173
|
|
1969
2174
|
self._handle_objs("Alias", self._aliases, *code.code[2:])
|
1970
2175
|
|
@@ -2359,10 +2564,11 @@ class Session:
|
|
2359
2564
|
mod = self._modules[module_name]["module"]
|
2360
2565
|
config = self._modules[module_name]["config"]
|
2361
2566
|
if config:
|
2362
|
-
if hasattr(config, "unload"):
|
2363
|
-
unload = getattr(config, "unload", None)
|
2567
|
+
if hasattr(config, "__unload__") or hasattr(config, "unload"):
|
2568
|
+
unload = getattr(config, "__unload__", None) or getattr(config, "unload", None)
|
2364
2569
|
if callable(unload):
|
2365
|
-
unload(
|
2570
|
+
unload()
|
2571
|
+
|
2366
2572
|
del config
|
2367
2573
|
mod = importlib.reload(mod)
|
2368
2574
|
if hasattr(mod, 'Configuration'):
|
@@ -2402,10 +2608,10 @@ class Session:
|
|
2402
2608
|
mod = self._modules[module_name]["module"]
|
2403
2609
|
config = self._modules[module_name]["config"]
|
2404
2610
|
if config:
|
2405
|
-
if hasattr(config, "unload"):
|
2406
|
-
unload = getattr(config, "unload", None)
|
2611
|
+
if hasattr(config, "__unload__") or hasattr(config, "unload"):
|
2612
|
+
unload = getattr(config, "__unload__", None) or getattr(config, "unload", None)
|
2407
2613
|
if callable(unload):
|
2408
|
-
unload(
|
2614
|
+
unload()
|
2409
2615
|
|
2410
2616
|
del config
|
2411
2617
|
del mod
|
pymud/settings.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: pymud
|
3
|
-
Version: 0.20.
|
3
|
+
Version: 0.20.0a3
|
4
4
|
Summary: a MUD Client written in Python
|
5
5
|
Author-email: "newstart@pkuxkx" <crapex@crapex.cc>
|
6
6
|
Maintainer-email: "newstart@pkuxkx" <crapex@crapex.cc>
|
@@ -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 :: 3 - Alpha
|
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)
|
@@ -694,6 +694,7 @@ Classifier: Programming Language :: Python :: 3.8
|
|
694
694
|
Classifier: Programming Language :: Python :: 3.9
|
695
695
|
Classifier: Programming Language :: Python :: 3.10
|
696
696
|
Classifier: Programming Language :: Python :: 3.11
|
697
|
+
Classifier: Programming Language :: Python :: 3.12
|
697
698
|
Classifier: Programming Language :: Python :: 3 :: Only
|
698
699
|
Requires-Python: >=3.7
|
699
700
|
Description-Content-Type: text/markdown
|
@@ -971,8 +972,26 @@ Requires-Dist: prompt-toolkit
|
|
971
972
|
+ 功能调整: 变量替代时,会自动实现类型转化,当被替代变量值为非 str 类型时不会再报错
|
972
973
|
+ 问题修复: 修复之前从后向前选择时,无法复制的问题
|
973
974
|
|
974
|
-
### 0.
|
975
|
+
### 0.20.0a1 (2024-08-14)
|
976
|
+
+ 功能调整: 使用argsparser标准模块来配置命令行,可以使用 python -m pymud -h 查看命令行具体参数及说明
|
975
977
|
+ 功能调整: 恢复在__init__.py中增加PyMudApp的导出,可以恢复使用from pymud import PyMudApp了
|
976
978
|
+ 功能调整: 在没有session的时候,也可以执行#exit命令
|
977
979
|
+ 功能调整: 模块加载和重新加载前,会自动调用模块的unload方法(若有)
|
978
|
-
+ 功能新增: 增加log
|
980
|
+
+ 功能新增: 增加log功能,详见 #log 命令介绍、类参考中的 Logger 类,以及 Session 类的 handle_log 方法
|
981
|
+
|
982
|
+
### 0.20.0a2 (2024-08-19)
|
983
|
+
+ 问题修复: 关闭会话时未关闭相关记录器,导致后续会话无法启动
|
984
|
+
+ 问题修复: MacOS下 python -m pymud init 创建目录报错的问题。同时,将所有系统上的默认目录均使用 ~/pkuxkx (影响windows)
|
985
|
+
+ 功能新增: Session类新增waitfor函数,用于执行一段代码后立即等待某个触发器的情况,简化原三行代码写法
|
986
|
+
|
987
|
+
### 0.20.0 (2024-08-XX)
|
988
|
+
+ 功能调整: 将模块主入口函数从__main__.py中移动到main.py中,以使可以在当前目录下,可直接使用pymud,也可使用python -m pymud启动
|
989
|
+
+ 问题修复: 修复部分正则表达式书写错误问题
|
990
|
+
+ 功能调整: Session类的addTriggers等方法接受的dict中,会将对象本身id作为会话处理id。当该id与key不一致时,会同时显示警告。
|
991
|
+
+ 功能新增: Session类新增addObject, addObjects, delObject, delObjects用于操作别名、定时器、触发器、GMCP触发器、命令等对象。
|
992
|
+
+ 功能新增: 主模块卸载现在既可以定义在__unload__方法中,也可以定义在unload方法中
|
993
|
+
+ 问题修复: 修复原unload方法不能正确卸载的问题
|
994
|
+
+ 功能新增: 命令行参数增加指定启动目录的功能,参数为 -s, --startup_dir。即可以从任意目录通过指定脚本目录方式启动PyMUD了。例如, PS C:\> pymud -s d:\prog\pkuxkx 相当于 PS D:\prog\pkuxk> pymud
|
995
|
+
+ 功能新增: 增加 #disconnect, #dis 命令,可以使当前会话从服务器断开。相当于操作菜单 会话->断开连接
|
996
|
+
+ 功能新增: #session 命令增加快捷创建会话功能,假如已有快捷菜单 世界->pkuxkx->newstart , 则可以通过 #session pkuxkx.newstart 直接创建该会话,效果等同于点击该菜单
|
997
|
+
+ 功能调整: 点击菜单创建会话时,若会话已存在,则将该会话切换为当前会话
|
@@ -0,0 +1,18 @@
|
|
1
|
+
pymud/__init__.py,sha256=hvX7Ga2xPG9J3ZpxBLrC6BvrzUo-Dj26HsRKf41Brhw,533
|
2
|
+
pymud/__main__.py,sha256=hFzZjadLlcOuoLM7D8wFiFVO8mqF7vMuo9y-9xfIhRc,64
|
3
|
+
pymud/dialogs.py,sha256=D0ZtCeoBchF5eYzXumkOi3p-maCQZu4v9-wJgxQ790o,6500
|
4
|
+
pymud/extras.py,sha256=QwWwLavVtuXfg0Qb0f_040va1_kej27P-ZB_19HB6Qk,42422
|
5
|
+
pymud/logger.py,sha256=elYfbpvmKYJfB-rnPYZWY5r8ROu9yja9t-dBi1faRGc,5358
|
6
|
+
pymud/main.py,sha256=wpL1t88qcnYDq1vyguj2NOMpD9yVgZeuAJSMWEbDeiI,6532
|
7
|
+
pymud/objects.py,sha256=0aUc-PZfVSdYPEAamY0eO6k3P9wJcw6PNxfSE8eZZeE,38072
|
8
|
+
pymud/pkuxkx.py,sha256=vWXHU6GF0HQ0eWb3LmxFVRP0cKnigffCX7Z-LJvwVtw,11496
|
9
|
+
pymud/protocol.py,sha256=QfDXjlg2OcJXmVoXf_3mAemnYotRXDUlEZNQjhkfXdA,49106
|
10
|
+
pymud/pymud.py,sha256=N9WxaHDqqsTWIBG8UJ38gdMC_pQ30wphcN2LtT66eA4,49584
|
11
|
+
pymud/session.py,sha256=Ea5pqFFMQ1NWct51g3LCWpyVzM83puFzLhdE7I4WEy4,129768
|
12
|
+
pymud/settings.py,sha256=CRNpHl4RjOuQYzAIWyUEvOo7q-f4lo01X1v_CriHWO4,7145
|
13
|
+
pymud-0.20.0a3.dist-info/LICENSE.txt,sha256=IwGE9guuL-ryRPEKi6wFPI_zOhg7zDZbTYuHbSt_SAk,35823
|
14
|
+
pymud-0.20.0a3.dist-info/METADATA,sha256=rXogwgamQhQ1geFAq3eH-Vyi2m521g2vevHm9XEf3Rg,70341
|
15
|
+
pymud-0.20.0a3.dist-info/WHEEL,sha256=nCVcAvsfA9TDtwGwhYaRrlPhTLV9m-Ga6mdyDtuwK18,91
|
16
|
+
pymud-0.20.0a3.dist-info/entry_points.txt,sha256=diPUOtTkhgC1hVny7Cdg4aRhaHSynMQoraE7ZhJxUcw,37
|
17
|
+
pymud-0.20.0a3.dist-info/top_level.txt,sha256=8Gp1eXjxixXjqhhti6tLCspV_8s9sNV3z5Em2_KRhD4,6
|
18
|
+
pymud-0.20.0a3.dist-info/RECORD,,
|
pymud-0.20.0a1.dist-info/RECORD
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
pymud/__init__.py,sha256=Ep6JrWv2wwlLV5nI7F6BOA7Ei6yVgnxvhIRwFGPr2Oc,501
|
2
|
-
pymud/__main__.py,sha256=ybi8pA0BXCZJ_GWgndllT7f1FBBE2tGNCByN0zkaSu0,6748
|
3
|
-
pymud/dialogs.py,sha256=D0ZtCeoBchF5eYzXumkOi3p-maCQZu4v9-wJgxQ790o,6500
|
4
|
-
pymud/extras.py,sha256=QwWwLavVtuXfg0Qb0f_040va1_kej27P-ZB_19HB6Qk,42422
|
5
|
-
pymud/logger.py,sha256=39I_rFBU5MEiOTJyU7GAy9cY_MpYHKiXNgBhpokQrTA,5358
|
6
|
-
pymud/objects.py,sha256=0aUc-PZfVSdYPEAamY0eO6k3P9wJcw6PNxfSE8eZZeE,38072
|
7
|
-
pymud/pkuxkx.py,sha256=vWXHU6GF0HQ0eWb3LmxFVRP0cKnigffCX7Z-LJvwVtw,11496
|
8
|
-
pymud/protocol.py,sha256=QfDXjlg2OcJXmVoXf_3mAemnYotRXDUlEZNQjhkfXdA,49106
|
9
|
-
pymud/pymud.py,sha256=2dbgnzlG9476esOfyp9sv-lRSTpMuRaCXnqrOofOu24,48182
|
10
|
-
pymud/session.py,sha256=uAWt0-AZWpDvVfCjzoxWDpuOwCQ8s1DN_DZ-rVAC5xs,121232
|
11
|
-
pymud/settings.py,sha256=XkyFWTskPGZljuWCaC8cU18ie8-FkecEtb0h3OfcfjU,7145
|
12
|
-
pymud-0.20.0a1.dist-info/LICENSE.txt,sha256=IwGE9guuL-ryRPEKi6wFPI_zOhg7zDZbTYuHbSt_SAk,35823
|
13
|
-
pymud-0.20.0a1.dist-info/METADATA,sha256=N57G7pGHXWfYKJ2YwJjWFMSYxvfkvwIpmsTKTR1hjro,68201
|
14
|
-
pymud-0.20.0a1.dist-info/WHEEL,sha256=HiCZjzuy6Dw0hdX5R3LCFPDmFS4BWl8H-8W39XfmgX4,91
|
15
|
-
pymud-0.20.0a1.dist-info/entry_points.txt,sha256=diPUOtTkhgC1hVny7Cdg4aRhaHSynMQoraE7ZhJxUcw,37
|
16
|
-
pymud-0.20.0a1.dist-info/top_level.txt,sha256=8Gp1eXjxixXjqhhti6tLCspV_8s9sNV3z5Em2_KRhD4,6
|
17
|
-
pymud-0.20.0a1.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|