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 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
- import sys, os, json, platform, shutil, logging, argparse
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
- parser = argparse.ArgumentParser(prog = "pymud", usage = "python -m pymud [-h] [-d] [-l logfile] [-a] {init} ...", description = "PyMUD命令行参数帮助")
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\\[[\d;]+[abcdmz]", flags = re.IGNORECASE)
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
- children=[]
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, info in site["chars"].items():
358
- after_connect = autologin.format(info[0], info[1])
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\\[[\d;]+[abcdmz]", "", line, flags = re.IGNORECASE).replace("\r", "").replace("\x00", "")
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\\[[\d;]+[abcdmz]", "", line, flags = re.IGNORECASE).replace("\r", "").replace("\x00", "")
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
- if len(args) >= 3:
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("错误的#session命令")
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 main(cfg_data = None):
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\\[[\d;]+[abcdmz]", flags = re.IGNORECASE)
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
- if cls == Alias:
1112
- self._aliases.update(objs)
1113
- elif cls == Command:
1114
- self._commands.update(objs)
1115
- elif cls == Trigger:
1116
- self._triggers.update(objs)
1117
- elif cls == Timer:
1118
- self._timers.update(objs)
1119
- elif cls == GMCPTrigger:
1120
- self._gmcp.update(objs)
1121
-
1122
- def _addObject(self, obj, cls: type):
1123
- #if type(obj) == cls:
1124
- if isinstance(obj, cls):
1125
- if cls == Alias:
1126
- self._aliases[obj.id] = obj
1127
- elif cls == Command:
1128
- self._commands[obj.id] = obj
1129
- elif cls == Trigger:
1130
- self._triggers[obj.id] = obj
1131
- elif cls == Timer:
1132
- self._timers[obj.id] = obj
1133
- elif cls == GMCPTrigger:
1134
- self._gmcp[obj.id] = obj
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 addAliases(self, alis: dict):
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, Alias)
1366
+ self._addObjects(alis)
1175
1367
 
1176
- def addCommands(self, cmds: dict):
1368
+ def addCommands(self, cmds):
1177
1369
  """
1178
1370
  向会话中增加多个命令。使用方法与 addAliases 类似。
1179
1371
 
1180
1372
  :param cmds: 多个命令的字典。字典 key 应为每个命令的 id。
1181
1373
  """
1182
- self._addObjects(cmds, Command)
1374
+ self._addObjects(cmds)
1183
1375
 
1184
- def addTriggers(self, tris: dict):
1376
+ def addTriggers(self, tris):
1185
1377
  """
1186
1378
  向会话中增加多个触发器。使用方法与 addAliases 类似。
1187
1379
 
1188
1380
  :param tris: 多个触发器的字典。字典 key 应为每个触发器的 id。
1189
1381
  """
1190
- self._addObjects(tris, Trigger)
1382
+ self._addObjects(tris)
1191
1383
 
1192
- def addGMCPs(self, gmcps: dict):
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, GMCPTrigger)
1390
+ self._addObjects(gmcps)
1199
1391
 
1200
- def addTimers(self, tis: dict):
1392
+ def addTimers(self, tis):
1201
1393
  """
1202
1394
  向会话中增加多个定时器。使用方法与 addAliases 类似。
1203
1395
 
1204
1396
  :param tis: 多个定时器的字典。字典 key 应为每个定时器的 id。
1205
1397
  """
1206
- self._addObjects(tis, Timer)
1398
+ self._addObjects(tis)
1207
1399
 
1208
- def addAlias(self, ali: Alias):
1400
+ def addAlias(self, ali):
1209
1401
  """
1210
1402
  向会话中增加一个别名。
1211
1403
 
1212
1404
  :param ali: 要增加的别名对象,应为 Alias 类型或其子类
1213
1405
  """
1214
- self._addObject(ali, Alias)
1406
+ self._addObject(ali)
1215
1407
 
1216
- def addCommand(self, cmd: Command):
1408
+ def addCommand(self, cmd):
1217
1409
  """
1218
1410
  向会话中增加一个命令。
1219
1411
 
1220
1412
  :param cmd: 要增加的命令对象,应为 Command 类型或其子类
1221
1413
  """
1222
- self._addObject(cmd, Command)
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, Trigger)
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, Timer)
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, GMCPTrigger)
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: GMCPTrigger):
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(config)
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(config)
2614
+ unload()
2409
2615
 
2410
2616
  del config
2411
2617
  del mod
pymud/settings.py CHANGED
@@ -13,7 +13,7 @@ class Settings:
13
13
  "APP 简要描述"
14
14
  __version__ = "0.20.0"
15
15
  "APP 当前版本"
16
- __release__ = "2024-08-14"
16
+ __release__ = "2024-08-19"
17
17
  "APP 当前版本发布日期"
18
18
  __author__ = "本牛(newstart)@北侠"
19
19
  "APP 作者"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pymud
3
- Version: 0.20.0a1
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 :: 5 - Production/Stable
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.19.5 (2024-08-XX)
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,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (72.2.0)
2
+ Generator: setuptools (73.0.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -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,,