pymud 0.20.1.post1__py3-none-any.whl → 0.20.2__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/extras.py +35 -28
- pymud/logger.py +4 -2
- pymud/objects.py +1 -1
- pymud/protocol.py +3 -3
- pymud/pymud.py +29 -9
- pymud/session.py +212 -127
- pymud/settings.py +7 -5
- {pymud-0.20.1.post1.dist-info → pymud-0.20.2.dist-info}/METADATA +33 -22
- pymud-0.20.2.dist-info/RECORD +19 -0
- {pymud-0.20.1.post1.dist-info → pymud-0.20.2.dist-info}/WHEEL +1 -1
- pymud-0.20.1.post1.dist-info/RECORD +0 -19
- {pymud-0.20.1.post1.dist-info → pymud-0.20.2.dist-info}/LICENSE.txt +0 -0
- {pymud-0.20.1.post1.dist-info → pymud-0.20.2.dist-info}/entry_points.txt +0 -0
- {pymud-0.20.1.post1.dist-info → pymud-0.20.2.dist-info}/top_level.txt +0 -0
pymud/extras.py
CHANGED
@@ -40,6 +40,8 @@ from prompt_toolkit.layout.controls import (
|
|
40
40
|
)
|
41
41
|
from prompt_toolkit.layout.processors import (
|
42
42
|
Processor,
|
43
|
+
TransformationInput,
|
44
|
+
Transformation
|
43
45
|
)
|
44
46
|
from prompt_toolkit.lexers import Lexer
|
45
47
|
from prompt_toolkit.mouse_events import MouseEvent, MouseEventType
|
@@ -64,10 +66,11 @@ class MudFormatProcessor(Processor):
|
|
64
66
|
self.FULL_BLOCKS = set("▂▃▅▆▇▄█")
|
65
67
|
self.SINGLE_LINES = set("┌└├┬┼┴╭╰─")
|
66
68
|
self.DOUBLE_LINES = set("╔╚╠╦╪╩═")
|
67
|
-
self.
|
68
|
-
self.
|
69
|
+
self.ALL_COLOR_REGX = re.compile(r"(?:\[[\d;]+m)+")
|
70
|
+
self.AVAI_COLOR_REGX = re.compile(r"(?:\[[\d;]+m)+(?!$)")
|
69
71
|
self._color_start = ""
|
70
72
|
self._color_correction = False
|
73
|
+
self._color_line_index = 0
|
71
74
|
|
72
75
|
def width_correction(self, line: str) -> str:
|
73
76
|
new_str = []
|
@@ -91,48 +94,52 @@ class MudFormatProcessor(Processor):
|
|
91
94
|
def tab_correction(self, line: str):
|
92
95
|
return line.replace("\t", " " * Settings.client["tabstop"])
|
93
96
|
|
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
|
-
|
117
97
|
def line_correction(self, line: str):
|
118
98
|
# 处理\r符号(^M)
|
119
99
|
line = self.return_correction(line)
|
120
100
|
# 处理Tab(\r)符号(^I)
|
121
101
|
line = self.tab_correction(line)
|
122
|
-
|
123
|
-
# line = self.color_correction(line)
|
102
|
+
|
124
103
|
# 美化(解决中文英文在Console中不对齐的问题)
|
125
104
|
if Settings.client["beautify"]:
|
126
105
|
line = self.width_correction(line)
|
127
106
|
|
128
107
|
return line
|
129
108
|
|
130
|
-
def apply_transformation(self, transformation_input):
|
109
|
+
def apply_transformation(self, transformation_input: TransformationInput):
|
131
110
|
# 准备(先还原为str)
|
132
111
|
line = fragment_list_to_text(transformation_input.fragments)
|
112
|
+
|
113
|
+
# 颜色校正
|
114
|
+
thislinecolors = len(self.AVAI_COLOR_REGX.findall(line))
|
115
|
+
if thislinecolors == 0:
|
116
|
+
lineno = transformation_input.lineno - 1
|
117
|
+
while lineno > 0:
|
118
|
+
lastline = transformation_input.document.lines[lineno]
|
119
|
+
allcolors = self.ALL_COLOR_REGX.findall(lastline)
|
120
|
+
|
121
|
+
if len(allcolors) == 0:
|
122
|
+
lineno = lineno - 1
|
123
|
+
|
124
|
+
elif len(allcolors) == 1:
|
125
|
+
colors = self.AVAI_COLOR_REGX.findall(lastline)
|
126
|
+
|
127
|
+
if len(colors) == 1:
|
128
|
+
line = f"{colors[0]}{line}"
|
129
|
+
break
|
130
|
+
|
131
|
+
else:
|
132
|
+
break
|
133
|
+
|
134
|
+
else:
|
135
|
+
break
|
136
|
+
|
137
|
+
# 其他校正
|
133
138
|
line = self.line_correction(line)
|
139
|
+
|
134
140
|
# 处理ANSI标记(生成FormmatedText)
|
135
141
|
fragments = to_formatted_text(ANSI(line))
|
142
|
+
|
136
143
|
return Transformation(fragments)
|
137
144
|
|
138
145
|
class SessionBuffer(Buffer):
|
pymud/logger.py
CHANGED
@@ -16,7 +16,7 @@ class Logger:
|
|
16
16
|
:param raw: 记录带ANSI标记的原始内容,还是记录纯文本内容,默认为True,即记录带ANSI标记的原始内容。
|
17
17
|
"""
|
18
18
|
|
19
|
-
_esc_regx = re.compile(r"\x1b\[[\d;]+[abcdmz]", flags = re.IGNORECASE)
|
19
|
+
# _esc_regx = re.compile(r"\x1b\[[\d;]+[abcdmz]", flags = re.IGNORECASE)
|
20
20
|
|
21
21
|
def __init__(self, name, mode = 'a', encoding = "utf-8", errors = "ignore", raw = False):
|
22
22
|
self._name = name
|
@@ -147,7 +147,9 @@ class Logger:
|
|
147
147
|
newline = True
|
148
148
|
|
149
149
|
if not self._raw:
|
150
|
-
|
150
|
+
from .session import Session
|
151
|
+
data = Session.PLAIN_TEXT_REGX.sub("", data)
|
152
|
+
#data = self._esc_regx.sub("", data)
|
151
153
|
|
152
154
|
self._stream.write(data)
|
153
155
|
|
pymud/objects.py
CHANGED
pymud/protocol.py
CHANGED
@@ -475,7 +475,7 @@ class MudClientProtocol(Protocol):
|
|
475
475
|
self._mtts_index += 1
|
476
476
|
self.log.debug('回复第二次MTTS子协商: IAC SB TTYPE IS "XTERM" IAC SE')
|
477
477
|
elif self._mtts_index == 2:
|
478
|
-
# 第三次收到,回复客户端终端支持的标准功能,此处默认设置
|
478
|
+
# 第三次收到,回复客户端终端支持的标准功能,此处默认设置783(支持ANSI, VT100, UTF-8, 256 COLORS, TRUECOLOR, MNES),后续功能完善后再更改
|
479
479
|
# 根据完善的终端模拟功能,修改终端标准
|
480
480
|
# 1 "ANSI" Client supports all common ANSI color codes.
|
481
481
|
# 2 "VT100" Client supports all common VT100 codes.
|
@@ -489,9 +489,9 @@ class MudClientProtocol(Protocol):
|
|
489
489
|
# 512 "MNES" Client supports the Mud New Environment Standard for information exchange.
|
490
490
|
# 1024 "MSLP" Client supports the Mud Server Link Protocol for clickable link handling.
|
491
491
|
# 2048 "SSL" Client supports SSL for data encryption, preferably TLS 1.3 or higher.
|
492
|
-
self.session.write(IAC + SB + TTYPE + IS + b"MTTS
|
492
|
+
self.session.write(IAC + SB + TTYPE + IS + b"MTTS 783" + IAC + SE)
|
493
493
|
self._mtts_index += 1
|
494
|
-
self.log.debug('回复第三次MTTS子协商: IAC SB TTYPE IS "MTTS
|
494
|
+
self.log.debug('回复第三次MTTS子协商: IAC SB TTYPE IS "MTTS 783" IAC SE')
|
495
495
|
else:
|
496
496
|
self.log.warning(f'收到第{self._mtts_index + 1}次(正常为3次)的MTTS子协商, 将不予应答')
|
497
497
|
else:
|
pymud/pymud.py
CHANGED
@@ -229,7 +229,8 @@ class PyMudApp:
|
|
229
229
|
content = HSplit(
|
230
230
|
[
|
231
231
|
self.console,
|
232
|
-
Window(char = "—", height = 1),
|
232
|
+
ConditionalContainer(content = Window(char = "—", height = 1), filter = Settings.client["status_divider"]),
|
233
|
+
#Window(char = "—", height = 1),
|
233
234
|
Window(content = self.statusView, height = Settings.client["status_height"]),
|
234
235
|
]
|
235
236
|
),
|
@@ -241,7 +242,7 @@ class PyMudApp:
|
|
241
242
|
content = VSplit(
|
242
243
|
[
|
243
244
|
self.console,
|
244
|
-
Window(char = "|", width = 1),
|
245
|
+
ConditionalContainer(content = Window(char = "|", width = 1), filter = Settings.client["status_divider"]),
|
245
246
|
Window(content = self.statusView, width = Settings.client["status_width"]),
|
246
247
|
]
|
247
248
|
),
|
@@ -311,12 +312,14 @@ class PyMudApp:
|
|
311
312
|
MenuItem(Settings.text["closesession"], handler = self.act_close_session),
|
312
313
|
MenuItem(Settings.text["autoreconnect"], handler = self.act_autoreconnect),
|
313
314
|
MenuItem("-", disabled=True),
|
314
|
-
MenuItem(Settings.text["echoinput"], handler = self.act_echoinput),
|
315
315
|
MenuItem(Settings.text["nosplit"], handler = self.act_nosplit),
|
316
|
+
MenuItem(Settings.text["echoinput"], handler = self.act_echoinput),
|
317
|
+
MenuItem(Settings.text["beautify"], handler = self.act_beautify),
|
316
318
|
MenuItem(Settings.text["copy"], handler = self.act_copy),
|
317
319
|
MenuItem(Settings.text["copyraw"], handler = self.act_copyraw),
|
318
320
|
MenuItem(Settings.text["clearsession"], handler = self.act_clearsession),
|
319
321
|
MenuItem("-", disabled=True),
|
322
|
+
|
320
323
|
MenuItem(Settings.text["reloadconfig"], handler = self.act_reload),
|
321
324
|
]
|
322
325
|
),
|
@@ -484,6 +487,7 @@ class PyMudApp:
|
|
484
487
|
|
485
488
|
``注意: 复制的内容仅存在于运行环境的剪贴板中。若使用ssh远程,该复制命令不能访问本地剪贴板。``
|
486
489
|
"""
|
490
|
+
|
487
491
|
b = self.consoleView.buffer
|
488
492
|
if b.selection_state:
|
489
493
|
cur1, cur2 = b.selection_state.original_cursor_position, b.document.cursor_position
|
@@ -501,7 +505,8 @@ class PyMudApp:
|
|
501
505
|
line = self.mudFormatProc.line_correction(b.document.current_line)
|
502
506
|
start = max(0, scol)
|
503
507
|
end = min(ecol, len(line))
|
504
|
-
line_plain = re.sub(r"\x1b\[[
|
508
|
+
#line_plain = re.sub(r"\x1b\[[0-9;]*[a-zA-Z]", "", line, flags = re.IGNORECASE).replace("\r", "").replace("\x00", "")
|
509
|
+
line_plain = Session.PLAIN_TEXT_REGX.sub("", line).replace("\r", "").replace("\x00", "")
|
505
510
|
#line_plain = re.sub("\x1b\\[[^mz]+[mz]", "", line).replace("\r", "").replace("\x00", "")
|
506
511
|
selection = line_plain[start:end]
|
507
512
|
self.app.clipboard.set_text(selection)
|
@@ -513,7 +518,8 @@ class PyMudApp:
|
|
513
518
|
lines = []
|
514
519
|
for row in range(srow, erow + 1):
|
515
520
|
line = b.document.lines[row]
|
516
|
-
line_plain = re.sub(r"\x1b\[[
|
521
|
+
#line_plain = re.sub(r"\x1b\[[0-9;]*[a-zA-Z]", "", line, flags = re.IGNORECASE).replace("\r", "").replace("\x00", "")
|
522
|
+
line_plain = Session.PLAIN_TEXT_REGX.sub("", line).replace("\r", "").replace("\x00", "")
|
517
523
|
lines.append(line_plain)
|
518
524
|
|
519
525
|
self.app.clipboard.set_text("\n".join(lines))
|
@@ -733,6 +739,13 @@ class PyMudApp:
|
|
733
739
|
new_sess = list(self.sessions.keys())[0]
|
734
740
|
self.activate_session(new_sess)
|
735
741
|
|
742
|
+
def act_beautify(self):
|
743
|
+
"菜单: 打开/关闭美化显示"
|
744
|
+
val = not Settings.client["beautify"]
|
745
|
+
Settings.client["beautify"] = val
|
746
|
+
if self.current_session:
|
747
|
+
self.current_session.info(f"显示美化已{'打开' if val else '关闭'}!")
|
748
|
+
|
736
749
|
def act_echoinput(self):
|
737
750
|
"菜单: 显示/隐藏输入指令"
|
738
751
|
val = not Settings.client["echo_input"]
|
@@ -879,7 +892,10 @@ class PyMudApp:
|
|
879
892
|
|
880
893
|
def get_statusbar_right_text(self):
|
881
894
|
"状态栏右侧内容"
|
882
|
-
con_str, mouse_support, tri_status = "", "", ""
|
895
|
+
con_str, mouse_support, tri_status, beautify = "", "", "", ""
|
896
|
+
if not Settings.client["beautify"]:
|
897
|
+
beautify = "美化已关闭 "
|
898
|
+
|
883
899
|
if not self._mouse_support:
|
884
900
|
mouse_support = "鼠标已禁用 "
|
885
901
|
|
@@ -909,13 +925,17 @@ class PyMudApp:
|
|
909
925
|
else:
|
910
926
|
con_str = "已连接:{:.0f}秒".format(sec)
|
911
927
|
|
912
|
-
return "{}{}{} {} {} ".format(mouse_support, tri_status, con_str, Settings.__appname__, Settings.__version__)
|
928
|
+
return "{}{}{}{} {} {} ".format(beautify, mouse_support, tri_status, con_str, Settings.__appname__, Settings.__version__)
|
913
929
|
|
914
930
|
def get_statuswindow_text(self):
|
915
931
|
"状态窗口: status_maker 的内容"
|
916
932
|
text = ""
|
917
|
-
|
918
|
-
|
933
|
+
|
934
|
+
try:
|
935
|
+
if self.current_session:
|
936
|
+
text = self.current_session.get_status()
|
937
|
+
except Exception as e:
|
938
|
+
text = f"{e}"
|
919
939
|
|
920
940
|
return text
|
921
941
|
|
pymud/session.py
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
import asyncio, logging, re, math, os, pickle, datetime, importlib, importlib.util, sysconfig, time
|
1
|
+
import asyncio, logging, re, math, os, pickle, datetime, importlib, importlib.util, sysconfig, time, dataclasses
|
2
2
|
from collections.abc import Iterable
|
3
3
|
from collections import OrderedDict
|
4
4
|
import logging, queue
|
@@ -30,7 +30,8 @@ class Session:
|
|
30
30
|
|
31
31
|
"""
|
32
32
|
#_esc_regx = re.compile("\x1b\\[[^mz]+[mz]")
|
33
|
-
_esc_regx = re.compile(r"\x1b\[[\d;]+[abcdmz]", flags = re.IGNORECASE)
|
33
|
+
#_esc_regx = re.compile(r"\x1b\[[\d;]+[abcdmz]", flags = re.IGNORECASE)
|
34
|
+
PLAIN_TEXT_REGX = re.compile("\x1b\\[[0-9;]*[a-zA-Z]", flags = re.IGNORECASE | re.ASCII)
|
34
35
|
|
35
36
|
_sys_commands = (
|
36
37
|
"help",
|
@@ -108,7 +109,10 @@ class Session:
|
|
108
109
|
self.loop = loop or asyncio.get_running_loop()
|
109
110
|
self.syslog = logging.getLogger("pymud.Session")
|
110
111
|
|
111
|
-
|
112
|
+
from .pymud import PyMudApp
|
113
|
+
if isinstance(app, PyMudApp):
|
114
|
+
self.application = app
|
115
|
+
|
112
116
|
self.name = name
|
113
117
|
self._transport = None
|
114
118
|
self._protocol = None
|
@@ -529,7 +533,7 @@ class Session:
|
|
529
533
|
:return: 经处理后的纯文本字符串
|
530
534
|
|
531
535
|
"""
|
532
|
-
plainText =
|
536
|
+
plainText = Session.PLAIN_TEXT_REGX.sub("", rawText)
|
533
537
|
if trim_newline:
|
534
538
|
plainText = plainText.rstrip("\n").rstrip("\r")
|
535
539
|
|
@@ -1926,6 +1930,154 @@ class Session:
|
|
1926
1930
|
|
1927
1931
|
self.disconnect()
|
1928
1932
|
|
1933
|
+
def getMaxLength(self, iter: Iterable):
|
1934
|
+
return wcswidth(sorted(iter, key = lambda s: wcswidth(s), reverse = True)[0])
|
1935
|
+
|
1936
|
+
def splitByPrintableWidth(self, str, printable_length):
|
1937
|
+
strlist = []
|
1938
|
+
startindex = 0
|
1939
|
+
remain = False
|
1940
|
+
split_str = ""
|
1941
|
+
for idx in range(1, len(str)):
|
1942
|
+
remain = True
|
1943
|
+
split_str = str[startindex:idx]
|
1944
|
+
if wcswidth(split_str) >= printable_length:
|
1945
|
+
strlist.append(split_str)
|
1946
|
+
startindex = idx
|
1947
|
+
remain = False
|
1948
|
+
|
1949
|
+
if remain:
|
1950
|
+
strlist.append(str[startindex:])
|
1951
|
+
|
1952
|
+
return strlist
|
1953
|
+
|
1954
|
+
def buildDisplayLines(self, vars: DotDict, title: str):
|
1955
|
+
MIN_MARGIN = 4
|
1956
|
+
KEY_WIDTH = (self.getMaxLength(vars.keys()) // 4) * 4 + 4
|
1957
|
+
VALUE_WIDTH = 20
|
1958
|
+
VAR_WIDTH = KEY_WIDTH + 3 + VALUE_WIDTH
|
1959
|
+
display_lines = []
|
1960
|
+
vars_simple = {}
|
1961
|
+
vars_complex = {}
|
1962
|
+
|
1963
|
+
for k, v in vars.items():
|
1964
|
+
if k in ("%line", "%raw", "%copy"):
|
1965
|
+
continue
|
1966
|
+
|
1967
|
+
if dataclasses.is_dataclass(v) or (isinstance(v, Iterable) and not isinstance(v, str)):
|
1968
|
+
vars_complex[k] = v
|
1969
|
+
else:
|
1970
|
+
vars_simple[k] = v
|
1971
|
+
|
1972
|
+
totalWidth = self.application.get_width() - 2
|
1973
|
+
|
1974
|
+
# draw title
|
1975
|
+
left_margin = (totalWidth - len(title)) // 2
|
1976
|
+
right_margin = totalWidth - len(title) - left_margin
|
1977
|
+
title_line = "{}{}{}".format("=" * left_margin, title, "=" * right_margin)
|
1978
|
+
display_lines.append(title_line)
|
1979
|
+
|
1980
|
+
# draw simple vars
|
1981
|
+
vars_per_line = totalWidth // VAR_WIDTH
|
1982
|
+
left_margin = (totalWidth - vars_per_line * VAR_WIDTH) // 2
|
1983
|
+
left_margin = min(MIN_MARGIN, left_margin)
|
1984
|
+
right_margin = totalWidth - vars_per_line * VAR_WIDTH - left_margin
|
1985
|
+
right_margin = min(left_margin, right_margin)
|
1986
|
+
|
1987
|
+
line = " " * left_margin
|
1988
|
+
cursor = left_margin
|
1989
|
+
var_count = 0
|
1990
|
+
|
1991
|
+
var_keys = sorted(vars_simple.keys())
|
1992
|
+
for key in var_keys:
|
1993
|
+
if len(key) < KEY_WIDTH:
|
1994
|
+
name = key.rjust(KEY_WIDTH)
|
1995
|
+
else:
|
1996
|
+
name = key.rjust(KEY_WIDTH + VAR_WIDTH)
|
1997
|
+
|
1998
|
+
value_dis = vars_simple[key].__repr__()
|
1999
|
+
var_display = "{0} = {1}".format(name, value_dis)
|
2000
|
+
|
2001
|
+
if (cursor + wcswidth(var_display) > totalWidth) or (var_count >= vars_per_line):
|
2002
|
+
display_lines.append(line)
|
2003
|
+
|
2004
|
+
line = " " * left_margin
|
2005
|
+
cursor = left_margin
|
2006
|
+
var_count = 0
|
2007
|
+
|
2008
|
+
line += var_display
|
2009
|
+
cursor += wcswidth(var_display)
|
2010
|
+
var_count += 1
|
2011
|
+
|
2012
|
+
# 下一处判定
|
2013
|
+
for x in range(vars_per_line, 0, -1):
|
2014
|
+
next_start = left_margin + (vars_per_line - x) * VAR_WIDTH
|
2015
|
+
if cursor < next_start:
|
2016
|
+
line += " " * (next_start - cursor)
|
2017
|
+
cursor = next_start
|
2018
|
+
|
2019
|
+
if (vars_per_line - x) > var_count:
|
2020
|
+
var_count = (vars_per_line - x)
|
2021
|
+
break
|
2022
|
+
|
2023
|
+
if cursor > left_margin:
|
2024
|
+
display_lines.append(line)
|
2025
|
+
|
2026
|
+
var_keys = sorted(vars_complex.keys())
|
2027
|
+
for key in var_keys:
|
2028
|
+
name = key.rjust(KEY_WIDTH)
|
2029
|
+
value_dis = vars_complex[key].__repr__()
|
2030
|
+
allow_len = totalWidth - left_margin - KEY_WIDTH - 3 - right_margin
|
2031
|
+
line = "{0}{1} = ".format(" " * left_margin, name.rjust(KEY_WIDTH))
|
2032
|
+
if wcswidth(value_dis) > allow_len:
|
2033
|
+
value = vars_complex[key]
|
2034
|
+
if isinstance(value, dict):
|
2035
|
+
max_len = self.getMaxLength(value.keys())
|
2036
|
+
line += '{'
|
2037
|
+
display_lines.append(line)
|
2038
|
+
line = " " * (left_margin + KEY_WIDTH + 4)
|
2039
|
+
for k, v in value.items():
|
2040
|
+
subvalue_dis = "{},".format(v.__repr__())
|
2041
|
+
allow_len_subvalue = allow_len - max_len - 4
|
2042
|
+
if wcswidth(subvalue_dis) > allow_len_subvalue:
|
2043
|
+
subvalue_lines = self.splitByPrintableWidth(subvalue_dis, allow_len_subvalue)
|
2044
|
+
line += "{0}: ".format(k.ljust(max_len))
|
2045
|
+
for subline in subvalue_lines:
|
2046
|
+
line += subline
|
2047
|
+
display_lines.append(line)
|
2048
|
+
line = " " * (left_margin + KEY_WIDTH + 4 + max_len + 2)
|
2049
|
+
|
2050
|
+
line = " " * (left_margin + KEY_WIDTH + 4)
|
2051
|
+
else:
|
2052
|
+
val_line = "{0}: {1}".format(k.ljust(max_len), subvalue_dis)
|
2053
|
+
line += val_line
|
2054
|
+
display_lines.append(line)
|
2055
|
+
line = " " * (left_margin + KEY_WIDTH + 4)
|
2056
|
+
line = line[:-1] + '}'
|
2057
|
+
display_lines.append(line)
|
2058
|
+
elif isinstance(value, list):
|
2059
|
+
line += '['
|
2060
|
+
for v in value:
|
2061
|
+
val_line = "{0},".format(v.__repr__())
|
2062
|
+
line += val_line
|
2063
|
+
display_lines.append(line)
|
2064
|
+
line = " " * (left_margin + KEY_WIDTH + 4)
|
2065
|
+
line = line[:-1] + ']'
|
2066
|
+
display_lines.append(line)
|
2067
|
+
else:
|
2068
|
+
value_lines = self.splitByPrintableWidth(value_dis, allow_len)
|
2069
|
+
for val_line in value_lines:
|
2070
|
+
line += val_line
|
2071
|
+
display_lines.append(line)
|
2072
|
+
line = " " * (left_margin + KEY_WIDTH + 3)
|
2073
|
+
else:
|
2074
|
+
line = "{0}{1} = {2}".format(" " * left_margin, key.rjust(KEY_WIDTH), vars_complex[key].__repr__())
|
2075
|
+
display_lines.append(line)
|
2076
|
+
|
2077
|
+
display_lines.append("=" * totalWidth)
|
2078
|
+
|
2079
|
+
return display_lines
|
2080
|
+
|
1929
2081
|
def handle_variable(self, code: CodeLine = None, *args, **kwargs):
|
1930
2082
|
'''
|
1931
2083
|
嵌入命令 #variable / #var 的执行函数,操作会话变量。
|
@@ -1951,62 +2103,20 @@ class Session:
|
|
1951
2103
|
#args = code.code[2:]
|
1952
2104
|
|
1953
2105
|
if len(args) == 0:
|
1954
|
-
|
1955
|
-
vars_simple = {}
|
1956
|
-
vars_complex = {}
|
1957
|
-
for k, v in vars.items():
|
1958
|
-
# 不显示line, raw两个系统变量
|
1959
|
-
if k in ("%line", "%raw"):
|
1960
|
-
continue
|
1961
|
-
|
1962
|
-
if isinstance(v, Iterable) and not isinstance(v, str):
|
1963
|
-
vars_complex[k] = v
|
1964
|
-
else:
|
1965
|
-
vars_simple[k] = v
|
1966
|
-
|
1967
|
-
width = self.application.get_width() - 2 # 保留2个字符,防止 > 导致换行
|
2106
|
+
lines = self.buildDisplayLines(self._variables, f" VARIABLE LIST IN SESSION {self.name} ")
|
1968
2107
|
|
1969
|
-
|
1970
|
-
|
1971
|
-
right = width - len(title) - left
|
1972
|
-
self.writetobuffer("="*left + title + "="*right, newline = True)
|
1973
|
-
|
1974
|
-
# print vars in simple, 每个变量占40格,一行可以多个变量
|
1975
|
-
# 这里可以考虑调整一下,默认40, 但如果一个变量值太长,则选择占两个位置
|
1976
|
-
var_count = len(vars_simple)
|
1977
|
-
var_per_line = (width - 2) // 40
|
1978
|
-
lines = math.ceil(var_count / var_per_line)
|
1979
|
-
left_space = (width - var_per_line * 40) // 2
|
1980
|
-
if left_space > 4: left_space = 4
|
1981
|
-
|
1982
|
-
var_keys = sorted(vars_simple.keys())
|
1983
|
-
|
1984
|
-
for idx in range(0, lines):
|
1985
|
-
start = idx * var_per_line
|
1986
|
-
end = (idx + 1) * var_per_line
|
1987
|
-
if end > var_count: end = var_count
|
1988
|
-
self.writetobuffer(" " * left_space)
|
1989
|
-
line_vars = var_keys[start:end]
|
1990
|
-
for var in line_vars:
|
1991
|
-
repr = vars_simple[var].__repr__()
|
1992
|
-
vwidth = 22 - (wcswidth(repr) - len(repr))
|
1993
|
-
self.writetobuffer("{0} = {1}".format(var.rjust(20), repr.ljust(vwidth)))
|
1994
|
-
#self.writetobuffer("{0:>18} = {1:<19}".format(var, vars_simple[var].__repr__()))
|
1995
|
-
|
1996
|
-
self.writetobuffer("", newline = True)
|
1997
|
-
|
1998
|
-
# print vars in complex, 每个变量占1行
|
1999
|
-
var_keys = sorted(vars_complex.keys())
|
2000
|
-
for key in var_keys:
|
2001
|
-
self.writetobuffer(" " * left_space)
|
2002
|
-
self.writetobuffer("{0:>20} = {1}".format(key, vars_complex[key].__repr__()), newline = True)
|
2003
|
-
|
2004
|
-
self.writetobuffer("="*width, newline = True)
|
2108
|
+
for line in lines:
|
2109
|
+
self.writetobuffer(line, newline = True)
|
2005
2110
|
|
2006
2111
|
elif len(args) == 1:
|
2007
2112
|
if args[0] in self._variables.keys():
|
2008
2113
|
obj = self.getVariable(args[0])
|
2009
|
-
|
2114
|
+
var_dict = {args[0] : obj}
|
2115
|
+
lines = self.buildDisplayLines(var_dict, f" VARIABLE [{args[0]}] IN SESSION {self.name} ")
|
2116
|
+
|
2117
|
+
for line in lines:
|
2118
|
+
self.writetobuffer(line, newline = True)
|
2119
|
+
|
2010
2120
|
else:
|
2011
2121
|
self.warning(f"当前session中不存在名称为 {args[0]} 的变量")
|
2012
2122
|
|
@@ -2018,6 +2128,7 @@ class Session:
|
|
2018
2128
|
val = args[1]
|
2019
2129
|
|
2020
2130
|
self.setVariable(args[0], val)
|
2131
|
+
self.info(f"成功设置变量 {args[0]} 值为 {val}")
|
2021
2132
|
|
2022
2133
|
def handle_global(self, code: CodeLine = None, *args, **kwargs):
|
2023
2134
|
'''
|
@@ -2043,55 +2154,21 @@ class Session:
|
|
2043
2154
|
#args = code.code[2:]
|
2044
2155
|
|
2045
2156
|
if len(args) == 0:
|
2046
|
-
|
2047
|
-
vars_simple = {}
|
2048
|
-
vars_complex = {}
|
2049
|
-
for k, v in vars.items():
|
2050
|
-
if isinstance(v, Iterable) and not isinstance(v, str):
|
2051
|
-
vars_complex[k] = v
|
2052
|
-
else:
|
2053
|
-
vars_simple[k] = v
|
2054
|
-
|
2055
|
-
width = self.application.get_width() - 2 # 保留2个字符,防止 > 导致换行
|
2056
|
-
|
2057
|
-
title = f" GLOBAL VARIABLES LIST "
|
2058
|
-
left = (width - len(title)) // 2
|
2059
|
-
right = width - len(title) - left
|
2060
|
-
self.writetobuffer("="*left + title + "="*right, newline = True)
|
2157
|
+
lines = self.buildDisplayLines(self.application.globals, f" GLOBAL VARIABLES LIST ")
|
2061
2158
|
|
2062
|
-
|
2063
|
-
|
2064
|
-
var_per_line = (width - 2) // 40
|
2065
|
-
lines = math.ceil(var_count / var_per_line)
|
2066
|
-
left_space = (width - var_per_line * 40) // 2
|
2067
|
-
if left_space > 4: left_space = 4
|
2068
|
-
|
2069
|
-
var_keys = sorted(vars_simple.keys())
|
2070
|
-
|
2071
|
-
for idx in range(0, lines):
|
2072
|
-
start = idx * var_per_line
|
2073
|
-
end = (idx + 1) * var_per_line
|
2074
|
-
if end > var_count: end = var_count
|
2075
|
-
self.writetobuffer(" " * left_space)
|
2076
|
-
line_vars = var_keys[start:end]
|
2077
|
-
for var in line_vars:
|
2078
|
-
repr = vars_simple[var].__repr__()
|
2079
|
-
vwidth = 22 - (wcswidth(repr) - len(repr))
|
2080
|
-
self.writetobuffer("{0} = {1}".format(var.rjust(20), repr.ljust(vwidth)))
|
2081
|
-
|
2082
|
-
self.writetobuffer("", newline = True)
|
2083
|
-
|
2084
|
-
# print vars in complex, 每个变量占1行
|
2085
|
-
for k, v in vars_complex.items():
|
2086
|
-
self.writetobuffer(" " * left_space)
|
2087
|
-
self.writetobuffer("{0:>20} = {1}".format(k, v.__repr__()), newline = True)
|
2088
|
-
|
2089
|
-
self.writetobuffer("="*width, newline = True)
|
2159
|
+
for line in lines:
|
2160
|
+
self.writetobuffer(line, newline = True)
|
2090
2161
|
|
2091
2162
|
elif len(args) == 1:
|
2092
2163
|
var = args[0]
|
2093
2164
|
if var in self.application.globals.keys():
|
2094
|
-
self.info("{0:>20} = {1:<22}".format(var, self.application.get_globals(var).__repr__()), "全局变量")
|
2165
|
+
# self.info("{0:>20} = {1:<22}".format(var, self.application.get_globals(var).__repr__()), "全局变量")
|
2166
|
+
|
2167
|
+
var_dict = {var : self.application.get_globals(var)}
|
2168
|
+
lines = self.buildDisplayLines(var_dict, f" GLOBAL VARIABLE [{var}] ")
|
2169
|
+
|
2170
|
+
for line in lines:
|
2171
|
+
self.writetobuffer(line, newline = True)
|
2095
2172
|
else:
|
2096
2173
|
self.info("全局空间不存在名称为 {} 的变量".format(var), "全局变量")
|
2097
2174
|
|
@@ -2102,6 +2179,7 @@ class Session:
|
|
2102
2179
|
except:
|
2103
2180
|
val = args[1]
|
2104
2181
|
self.application.set_globals(args[0], val)
|
2182
|
+
self.info(f"成功设置全局变量 {args[0]} 值为 {val}")
|
2105
2183
|
|
2106
2184
|
def _handle_objs(self, name: str, objs: dict, *args):
|
2107
2185
|
if len(args) == 0:
|
@@ -2932,14 +3010,18 @@ class Session:
|
|
2932
3010
|
triggered_enabled += 1
|
2933
3011
|
if not block:
|
2934
3012
|
triggered += 1
|
2935
|
-
info_enabled.append(f" {Settings.INFO_STYLE}{tri.__detailed__()} 正常触发。{Settings.CLR_STYLE}")
|
2936
|
-
info_enabled.append(f" {Settings.INFO_STYLE}捕获:{state.wildcards}{Settings.CLR_STYLE}")
|
3013
|
+
# info_enabled.append(f" {Settings.INFO_STYLE}{tri.__detailed__()} 正常触发。{Settings.CLR_STYLE}")
|
3014
|
+
# info_enabled.append(f" {Settings.INFO_STYLE}捕获:{state.wildcards}{Settings.CLR_STYLE}")
|
3015
|
+
info_enabled.append(f" {tri.__detailed__()} 正常触发。")
|
3016
|
+
info_enabled.append(f" 捕获:{state.wildcards}")
|
2937
3017
|
|
2938
3018
|
if not tri.keepEval: # 非持续匹配的trigger,匹配成功后停止检测后续Trigger
|
2939
|
-
info_enabled.append(f"
|
3019
|
+
info_enabled.append(f" {Settings.WARN_STYLE}该触发器未开启keepEval, 会阻止后续触发器。{Settings.CLR_STYLE}")
|
3020
|
+
#info_enabled.append(f" 该触发器未开启keepEval, 会阻止后续触发器。")
|
2940
3021
|
block = True
|
2941
3022
|
else:
|
2942
|
-
info_enabled.append(f"
|
3023
|
+
info_enabled.append(f" {Settings.WARN_STYLE}{tri.__detailed__()} 可以触发,但由于优先级与keepEval设定,触发器不会触发。{Settings.CLR_STYLE}")
|
3024
|
+
#info_enabled.append(f" {tri.__detailed__()} 可以触发,但由于优先级与keepEval设定,触发器不会触发。")
|
2943
3025
|
|
2944
3026
|
|
2945
3027
|
for tri in tris_disabled:
|
@@ -2950,35 +3032,38 @@ class Session:
|
|
2950
3032
|
|
2951
3033
|
if state.result == Trigger.SUCCESS:
|
2952
3034
|
triggered_disabled += 1
|
2953
|
-
info_disabled.append(f" {Settings.INFO_STYLE}{tri.__detailed__()} 可以匹配触发。{Settings.CLR_STYLE}")
|
3035
|
+
# info_disabled.append(f" {Settings.INFO_STYLE}{tri.__detailed__()} 可以匹配触发。{Settings.CLR_STYLE}")
|
3036
|
+
info_disabled.append(f" {tri.__detailed__()} 可以匹配触发。")
|
2954
3037
|
|
2955
3038
|
if triggered_enabled + triggered_disabled == 0:
|
2956
3039
|
info_all.append("")
|
2957
3040
|
|
2958
3041
|
if triggered_enabled == 0:
|
2959
|
-
info_enabled.insert(0, f"使能的触发器中,没有可以触发的。")
|
3042
|
+
info_enabled.insert(0, f"{Settings.INFO_STYLE} 使能的触发器中,没有可以触发的。")
|
2960
3043
|
elif triggered < triggered_enabled:
|
2961
|
-
info_enabled.insert(0, f"使能的触发器中,共有 {triggered_enabled} 个可以触发,实际触发 {triggered} 个,另有 {triggered_enabled - triggered} 个由于 keepEval 原因实际不会触发。")
|
3044
|
+
info_enabled.insert(0, f"{Settings.INFO_STYLE} 使能的触发器中,共有 {triggered_enabled} 个可以触发,实际触发 {triggered} 个,另有 {triggered_enabled - triggered} 个由于 keepEval 原因实际不会触发。")
|
2962
3045
|
else:
|
2963
|
-
info_enabled.insert(0, f"使能的触发器中,共有 {triggered_enabled} 个全部可以被正常触发。")
|
3046
|
+
info_enabled.insert(0, f"{Settings.INFO_STYLE} 使能的触发器中,共有 {triggered_enabled} 个全部可以被正常触发。")
|
2964
3047
|
|
2965
3048
|
if triggered_disabled > 0:
|
2966
|
-
info_disabled.insert(0, f"未使能的触发器中,共有 {triggered_disabled} 个可以匹配。")
|
3049
|
+
info_disabled.insert(0, f"{Settings.INFO_STYLE} 未使能的触发器中,共有 {triggered_disabled} 个可以匹配。")
|
2967
3050
|
else:
|
2968
|
-
info_disabled.insert(0, f"未使能触发器,没有可以匹配的。")
|
2969
|
-
|
3051
|
+
info_disabled.insert(0, f"{Settings.INFO_STYLE} 未使能触发器,没有可以匹配的。")
|
3052
|
+
|
3053
|
+
info_all.append("")
|
2970
3054
|
if triggered_enabled + triggered_disabled == 0:
|
2971
|
-
info_all.append(f"PYMUD 触发器测试: {'响应模式' if docallback else '测试模式'}")
|
3055
|
+
#info_all.append(f"PYMUD 触发器测试: {'响应模式' if docallback else '测试模式'}")
|
2972
3056
|
info_all.append(f" 测试内容: {line}")
|
2973
3057
|
info_all.append(f" 测试结果: 没有可以匹配的触发器。")
|
2974
3058
|
else:
|
2975
|
-
info_all.append(f"PYMUD 触发器测试: {'响应模式' if docallback else '测试模式'}")
|
3059
|
+
#info_all.append(f"PYMUD 触发器测试: {'响应模式' if docallback else '测试模式'}")
|
2976
3060
|
info_all.append(f" 测试内容: {line}")
|
2977
3061
|
info_all.append(f" 测试结果: 有{triggered}个触发器可以被正常触发,一共有{triggered_enabled + triggered_disabled}个满足匹配触发要求。")
|
2978
3062
|
info_all.extend(info_enabled)
|
2979
3063
|
info_all.extend(info_disabled)
|
2980
|
-
|
2981
|
-
|
3064
|
+
|
3065
|
+
title = f"触发器测试 - {'响应模式' if docallback else '测试模式'}"
|
3066
|
+
self.info("\n".join(info_all), title)
|
2982
3067
|
#self.info("PYMUD 触发器测试 完毕")
|
2983
3068
|
|
2984
3069
|
def handle_plugins(self, code: CodeLine = None, *args, **kwargs):
|
@@ -3102,7 +3187,7 @@ class Session:
|
|
3102
3187
|
'''
|
3103
3188
|
|
3104
3189
|
new_text, new_code = code.expand(self, *args, **kwargs)
|
3105
|
-
self.warning(new_text[
|
3190
|
+
self.warning(new_text[9:])
|
3106
3191
|
|
3107
3192
|
def handle_error(self, code: CodeLine = None, *args, **kwargs):
|
3108
3193
|
'''
|
@@ -3118,23 +3203,23 @@ class Session:
|
|
3118
3203
|
'''
|
3119
3204
|
|
3120
3205
|
new_text, new_code = code.expand(self, *args, **kwargs)
|
3121
|
-
self.error(new_text[
|
3206
|
+
self.error(new_text[7:])
|
3122
3207
|
|
3123
|
-
def info2(self, msg, title = "
|
3208
|
+
def info2(self, msg, title = "消息", style = Settings.INFO_STYLE):
|
3124
3209
|
msg = f"{msg}"
|
3125
3210
|
|
3126
|
-
if Settings.client["newline"] in msg:
|
3127
|
-
|
3128
|
-
|
3129
|
-
|
3130
|
-
|
3211
|
+
# if Settings.client["newline"] in msg:
|
3212
|
+
# new_lines = list()
|
3213
|
+
# msg_lines = msg.split(Settings.client["newline"])
|
3214
|
+
# for line in msg_lines:
|
3215
|
+
# new_lines.append("{}{}".format(style, line))
|
3131
3216
|
|
3132
|
-
|
3217
|
+
# msg = Settings.client["newline"].join(new_lines)
|
3133
3218
|
|
3134
3219
|
# 将颜色跨行显示移动到了MudFormatProcessor中,此处无需再处理(不行,还得恢复)
|
3135
|
-
self.writetobuffer("{}
|
3220
|
+
self.writetobuffer("{}〔{}〕{}{}".format(style, title, msg, Settings.CLR_STYLE), newline = True)
|
3136
3221
|
|
3137
|
-
def info(self, msg, title = "
|
3222
|
+
def info(self, msg, title = "提示", style = Settings.INFO_STYLE):
|
3138
3223
|
"""
|
3139
3224
|
使用默认的INFO_STYLE(绿色)输出信息,并自动换行。信息格式类似 [title] msg
|
3140
3225
|
|
@@ -3144,7 +3229,7 @@ class Session:
|
|
3144
3229
|
"""
|
3145
3230
|
self.info2(msg, title, style)
|
3146
3231
|
|
3147
|
-
def warning(self, msg, title = "
|
3232
|
+
def warning(self, msg, title = "警告", style = Settings.WARN_STYLE):
|
3148
3233
|
"""
|
3149
3234
|
使用默认的WARN_STYLE(黄色)输出信息,并自动换行。信息格式类似 [title] msg
|
3150
3235
|
|
@@ -3154,7 +3239,7 @@ class Session:
|
|
3154
3239
|
"""
|
3155
3240
|
self.info2(msg, title, style)
|
3156
3241
|
|
3157
|
-
def error(self, msg, title = "
|
3242
|
+
def error(self, msg, title = "错误", style = Settings.ERR_STYLE):
|
3158
3243
|
"""
|
3159
3244
|
使用默认的ERR_STYLE(红色)输出信息,并自动换行。信息格式类似 [title] msg
|
3160
3245
|
|
pymud/settings.py
CHANGED
@@ -11,9 +11,9 @@ class Settings:
|
|
11
11
|
"APP 名称, 默认PYMUD"
|
12
12
|
__appdesc__ = "a MUD client written in Python"
|
13
13
|
"APP 简要描述"
|
14
|
-
__version__ = "0.20.
|
14
|
+
__version__ = "0.20.2"
|
15
15
|
"APP 当前版本"
|
16
|
-
__release__ = "2024-11-
|
16
|
+
__release__ = "2024-11-25"
|
17
17
|
"APP 当前版本发布日期"
|
18
18
|
__author__ = "本牛(newstart)@北侠"
|
19
19
|
"APP 作者"
|
@@ -69,6 +69,7 @@ class Settings:
|
|
69
69
|
"echo_input" : False,
|
70
70
|
"beautify" : True, # 专门为解决控制台下PKUXKX字符画对不齐的问题
|
71
71
|
|
72
|
+
"status_divider" : True, # 是否显示状态栏的分隔线
|
72
73
|
"status_display" : 1, # 状态窗口显示情况设置,0-不显示,1-显示在下方,2-显示在右侧
|
73
74
|
"status_width" : 30, # 右侧状态栏的宽度
|
74
75
|
"status_height" : 6, # 下侧状态栏的高度
|
@@ -84,6 +85,7 @@ class Settings:
|
|
84
85
|
"session" : "会话",
|
85
86
|
"connect" : "连接/重新连接",
|
86
87
|
"disconnect" : "断开连接",
|
88
|
+
"beautify" : "打开/关闭美化显示",
|
87
89
|
"echoinput" : "显示/隐藏输入指令",
|
88
90
|
"nosplit" : "取消分屏",
|
89
91
|
"copy" : "复制(纯文本)",
|
@@ -174,8 +176,8 @@ class Settings:
|
|
174
176
|
"value" : "fg:green",
|
175
177
|
}
|
176
178
|
|
177
|
-
INFO_STYLE = "\x1b[
|
178
|
-
WARN_STYLE = "\x1b[
|
179
|
-
ERR_STYLE = "\x1b[
|
179
|
+
INFO_STYLE = "\x1b[48;5;22m\x1b[38;5;252m" #"\x1b[38;2;0;128;255m"
|
180
|
+
WARN_STYLE = "\x1b[48;5;220m\x1b[38;5;238m"
|
181
|
+
ERR_STYLE = "\x1b[48;5;160m\x1b[38;5;252m"
|
180
182
|
CLR_STYLE = "\x1b[0m"
|
181
183
|
|
@@ -1,10 +1,10 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: pymud
|
3
|
-
Version: 0.20.
|
3
|
+
Version: 0.20.2
|
4
4
|
Summary: a MUD Client written in Python
|
5
5
|
Author-email: "newstart@pkuxkx" <crapex@hotmail.com>
|
6
6
|
Maintainer-email: "newstart@pkuxkx" <crapex@hotmail.com>
|
7
|
-
License:
|
7
|
+
License: GNU GENERAL PUBLIC LICENSE
|
8
8
|
Version 3, 29 June 2007
|
9
9
|
|
10
10
|
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
@@ -737,12 +737,23 @@ Requires-Dist: prompt-toolkit
|
|
737
737
|
|
738
738
|
## 版本更新信息
|
739
739
|
|
740
|
+
### 0.20.2 (2024-11-26)
|
741
|
+
+ 功能调整: MTTS协商中,将256 Color明确写入协商回复。原先仅包含ANSI 和 TrueColor。推测武庙特殊颜色偶尔不正常与此有关(已测试无关)。
|
742
|
+
+ 功能调整: 修复了纯文本正则处理,目前理论上支持所有ANSI控制代码的处置,以正确响应纯文本触发器。
|
743
|
+
+ 功能调整: 修改了#var和#global的显示实现,提高了变量打印排列的整齐度和辨识度,以适应长值变量和复杂变量。
|
744
|
+
+ 问题修复: 修复了单行颜色代码跨行无法显示问题。现在星宿毒草可以正常辨认颜色了。
|
745
|
+
+ 功能调整: 调整了info/warning/error的显示处理,默认样式进行了修改。
|
746
|
+
+ 功能新增: 新增菜单选项:打开/关闭美化,以便于更好的在触发器时复制出正确的内容(以前计算可能不准确)。
|
747
|
+
+ 功能新增: 状态栏的分隔符可以通过本地设置取消了。在pymud.cfg的client中新增设置,将 status_divider 设置为 false 即可。
|
748
|
+
+ 功能调整: 在pymud.cfg的client中可以支持将buffer_lines设置为0了,表示不清除缓存。
|
749
|
+
+ 功能新增: 为状态栏显示函数增加了异常保护,再有status_maker出错的时候,状态栏会显示出错信息。
|
750
|
+
|
740
751
|
### 0.20.1 (2024-11-16)
|
741
752
|
+ 功能调整: 会话中触发器匹配实现进行部分调整,减少循环次数以提高响应速度
|
742
753
|
+ 功能调整: #test / #show 触发器测试功能调整,现在会对使能的和未使能的触发器均进行匹配测试。其中,#show 命令仅测试,而 #test 命令会导致触发器真正响应。
|
743
754
|
+ 功能新增: pymud对象新增了一个持续运行的1s的周期定时任务。该任务中会刷新页面显示。可以使用 session.application.addTimerTickCallback 和 session.application.removeTimerTickCallback 来注册和解除定时器回调。
|
744
755
|
|
745
|
-
|
756
|
+
### 0.20.0 (2024-08-25)
|
746
757
|
+ 功能调整: 将模块主入口函数从__main__.py中移动到main.py中,以使可以在当前目录下,可直接使用pymud,也可使用python -m pymud启动
|
747
758
|
+ 功能调整: 使用argsparser标准模块来配置命令行,可以使用 pymud -h 查看命令行具体参数及说明
|
748
759
|
+ 功能新增: 命令行参数增加指定启动目录的功能,参数为 -s, --startup_dir。即可以从任意目录通过指定脚本目录方式启动PyMUD了。
|
@@ -804,21 +815,21 @@ Requires-Dist: prompt-toolkit
|
|
804
815
|
+ 问题修复: 修复 #var 等命令中,若含有中文则等号位置不对齐的问题
|
805
816
|
+ 功能调整: 在 #tri 等命令中,当对象的 group 为空时,将不再显示 group 属性,减少无用信息
|
806
817
|
|
807
|
-
|
818
|
+
### 0.19.4 (2024-04-20)
|
808
819
|
+ 功能调整: info 现在 msg 恢复为可接受任何类型参数,不一定是 str
|
809
820
|
+ 功能调整: #var, #global 指令中,现在可以使用参数扩展了,例如 #var max_qi @qi
|
810
821
|
+ 功能调整: #var, #global 指令中,现在对字符串会先使用 eval 转换类型,转换失败时使用 str 类型。例如, #var myvar 1 时,myvar类型将为int
|
811
822
|
+ 功能调整: 变量替代时,会自动实现类型转化,当被替代变量值为非 str 类型时不会再报错
|
812
823
|
+ 问题修复: 修复之前从后向前选择时,无法复制的问题
|
813
824
|
|
814
|
-
|
825
|
+
### 0.19.3post2 (2024-04-05)
|
815
826
|
+ 问题修复: 一次发送多个命令时,发送顺序可能不正确的情况
|
816
827
|
+ 功能增加: 新增一个exec_async函数,是exec函数的异步形式。可以在其他会话中异步执行一段代码
|
817
828
|
+ 帮助完善: 帮助文档逻辑完善,已完成整个包的内置文档的编写和修改
|
818
829
|
+ 注: 由于我没弄太明白 readthedocs.io 网站对于读取github源代码的逻辑,目前只能通过新发布正式版本的形式来使 readthedocs.io 网站的文档中的类参考自动更新。
|
819
830
|
+ 问题修复: 修复退出程序时的小bug
|
820
831
|
|
821
|
-
|
832
|
+
### 0.19.2post2 (2024-03-24)
|
822
833
|
+ 错误修复:订正部分错别字、错误帮助、错别格式
|
823
834
|
+ 系统完善:完善帮助体系,按reST格式重写所有有关的docstring
|
824
835
|
+ 功能调整:session.exec_command / exec_command_async / exec 系列命令调整,现在可以在exec时带变量参数了。例如 session.exec("dazuo @dzpt"),直接调用 dzpt的变量值
|
@@ -832,14 +843,14 @@ Requires-Dist: prompt-toolkit
|
|
832
843
|
+ 帮助完善: 帮助文档逻辑完善
|
833
844
|
+ 实现调整: 改用官方示例的task清除方式,每个任务结束后清除
|
834
845
|
|
835
|
-
|
846
|
+
### 0.19.1 (2024-03-06)
|
836
847
|
+ 功能新增: 新增鼠标启用禁用功能,以适用于ssh远程情况下的复制功能。F2快捷键可以切换状态。当鼠标禁用时,底部状态栏右侧会显示“鼠标已禁用状态”
|
837
848
|
+ 功能新增: 新增快捷键F1会直接通过浏览器打开帮助网址 https://pymud.readthedocs.io/
|
838
849
|
+ 功能新增: 新增默认快捷键F3=#ig, F4=#cls, F11=#close, F12=#exit。此几个快捷键通过配置文件进行配置,可以自行定义或修改。F1、F2为写死的系统功能。
|
839
850
|
+ 功能调整: 将除#session之外的所有其他#命令实现统一到Session类中实现,这些命令均支持通过Session.exec_command运行
|
840
851
|
+ 功能调整: python -m pymud init时,创建的pymud.cfg文件增加了keys字典
|
841
852
|
|
842
|
-
|
853
|
+
### 0.19.0 (2024-03-01)
|
843
854
|
+ 实现调整: session.info/warning/error处理多行时,会给每一行加上同样颜色
|
844
855
|
+ 功能新增: 初次运行时,可以使用python -m pymud init来初始化环境,自动创建目录并在该目录中建立配置文件和样例脚本文件
|
845
856
|
+ 实现调整: 将缓冲清除行数的实现调整到SessionBuffer中,减少代码耦合并进一步降低内存占用
|
@@ -849,7 +860,7 @@ Requires-Dist: prompt-toolkit
|
|
849
860
|
+ 实现调整: 调整COPY-RAW模式复制,即使仅选中行中的部分内容,也自动识别整行(多行模式也是整个多行)
|
850
861
|
+ 功能新增: Settings中新增keys字典,用于定义快捷键。可定义快捷键参见prompt_toolkit中Keys的定义。其值为可在session.exec_command运行支持的所有内容。该字典内容可以被pymud.cfg所覆盖。
|
851
862
|
|
852
|
-
|
863
|
+
### 0.18.4post4 (2024-02-23)
|
853
864
|
+ 功能新增:新增Settings.client["buffer_lines"],表示保留的缓冲行数(默认5000)。当Session内容缓冲行数达到该值2倍时(10000行),将截取一半(5000行),后一半内容进行保留,前一半丢弃。此功能是为了减少长时挂机的内存消耗和响应时间。
|
854
865
|
+ 功能修复:解决在显示美化(Settings.client["beautify"])打开之后,复制部分文字不能正确判断起始终止的问题。
|
855
866
|
+ 功能调整:修改缓冲行数判断逻辑,加快客户端判断响应速度。
|
@@ -857,21 +868,21 @@ Requires-Dist: prompt-toolkit
|
|
857
868
|
+ 功能调整:将帮助窗口中的链接改到帮助网址: https://pymud.readthedocs.org
|
858
869
|
+ 问题修复:修复了随包提供的pkuxkx.py样例脚本中的几处错误
|
859
870
|
|
860
|
-
|
871
|
+
### 0.18.3 (2024-02-07)
|
861
872
|
+ 功能调整:原#unload时通过调用__del__来实现卸载的时间不可控,现将模块卸载改为调用unload函数。若需卸载时人工清除有关定时器、触发器等,请在Configuration类下新增unload函数(参数仅self),并在其中进行实现
|
862
873
|
+ 功能新增:新增会话Variable和全局Global的删除接口。可以通过session.delVariable(name)删除一个变量,可以通过session.delGlobal(name)来删除一个全局Global变量
|
863
874
|
|
864
|
-
|
875
|
+
### 0.18.2 (2024-02-06)
|
865
876
|
+ 问题修复:修改了定时器实现,以避免出现递归调用超限异常
|
866
877
|
+ 问题修复:修改了参数替代时的默认值,从None改为字符串"None",以避免替代时报None异常
|
867
878
|
|
868
|
-
|
879
|
+
### 0.18.1 (2024-02-05)
|
869
880
|
+ 问题修复:统一处置了task.cancel的参数和create_task的name属性,以适应更低版本的python环境(低至3.7)
|
870
881
|
+ 实现调整:为解决同步/异步执行问题,在CodeLine和CodeBlock的实现中,会通过调用命令来判断是否使用同步模式(默认为异步)。#gag、#replace为强制同步,#wa为强制异步。当同时存在时,同步失效,异步执行。
|
871
882
|
+ 实现调整:将%line、%raw的访问传递到触发器内部的执行中,避免同步异步问题。
|
872
883
|
+ 新增文档:将帮助文档添加到本项目,帮助文档自动同步到 pymud.readthedocs.org (文档内容暂未更新)
|
873
884
|
|
874
|
-
|
885
|
+
### 0.18.0 (2024-01-24)
|
875
886
|
+ 问题修复:修复了delTrigger/delAlias等等无法删除对象的问题
|
876
887
|
+ 功能调整:delTrigger等函数,修改为既可以接受Trigger对象本身,也可以接受其id。其他类似
|
877
888
|
+ 功能增加:增加了delTriggers(注意,带s)等函数,可以删除多个指定对象。可接受列表、元组等可迭代对象,并且其内容既可以为对象本身,也可以为id。
|
@@ -890,13 +901,13 @@ Requires-Dist: prompt-toolkit
|
|
890
901
|
+ 功能调整:从菜单里点击创建会话时,会自动以登录名为本会话创建id变量
|
891
902
|
+ 当前已知问题:由于同步/异步执行问题,在SimpleTrigger中,#gag和#replace的执行结果会很奇怪,可能会隐藏和替换掉非触发行。可行的办法为在onSuccess里,调用session.replace进行处理。
|
892
903
|
|
893
|
-
|
904
|
+
### 0.17.4 (2024-01-08)
|
894
905
|
+ 问题修复:修复了DotDict在dump时出现错误的问题
|
895
906
|
+ 问题修复:修改了reconnect的实现方式,修复了断开重连时报错的问题
|
896
907
|
+ 功能增加:为Session增加两个事件属性,分别为event_connected和event_disconnected,接受一个带有session参数的函数,在连接和连接断开时触发。
|
897
908
|
+ 功能调整:调整了时间显示格式,只显示到秒,不显示毫秒数。
|
898
909
|
|
899
|
-
|
910
|
+
### 0.17.3 (2024-01-02)
|
900
911
|
+ 问题修复:修复了原有的#repeat功能。命令行#repeat/#rep可以重复输入上一次命令(这个基本没用,主要是我在远程连接时,手机上没有方向键...)
|
901
912
|
+ 问题修复:修改定时器的实现方式,真正修复了定时器每reload后会新增一个的bug。
|
902
913
|
+ 功能增加:命令行使用#tri, #ali, #cmd, #ti时,除了接受on/off参数外,增加了del参数,可以删除对应的触发器、别名、命令、定时器。例如:#ti tm_test del 可以删除id为“tm_test”的定时器。
|
@@ -904,7 +915,7 @@ Requires-Dist: prompt-toolkit
|
|
904
915
|
+ 功能调整:调整了Timer和SimpleTimer在#timer时的显示格式。
|
905
916
|
+ 实现调整:调整了Session.clean实现中各对象清理的顺序,将任务清除移到了最后。
|
906
917
|
|
907
|
-
|
918
|
+
### 0.17.2post4 (2023-12-29)
|
908
919
|
+ 功能修改:会话菜单 "显示/隐藏命令" 和 "打开/关闭自动重连" 操作后,增加在当前会话中提示状态信息。
|
909
920
|
+ 功能修改:Timer实现进行修改,以确保一个定时器仅创建一个任务。
|
910
921
|
+ 功能调整:Timer对象在复位Session对象时,也同时复位。目的是确保reload时不重新创建定时器任务。
|
@@ -913,7 +924,7 @@ Requires-Dist: prompt-toolkit
|
|
913
924
|
+ 问题修复:修复会话特定脚本模块会被其他会话加载的bug。
|
914
925
|
+ 问题修复:修复定时器Timer中的bug。
|
915
926
|
|
916
|
-
|
927
|
+
### 0.17.1post1 (2023-12-27)
|
917
928
|
本版对模块功能进行了整体调整,支持加载/卸载/重载/预加载多个模块,具体内容如下:
|
918
929
|
+ 当模块中存在名为Configuration类时,以主模块形式加载,即:自动创建该Configuration类的实例(与原脚本相同)
|
919
930
|
+ 当模块中不存在名为Configuration类时,以子模块形式加载,即:仅加载该模块,但不会创建Configuration的实例
|
@@ -954,7 +965,7 @@ Requires-Dist: prompt-toolkit
|
|
954
965
|
+ 功能修改:auto_reconnect设定目前对正常/异常断开均有效。若设置为True,当连接断开后15s后自动重连
|
955
966
|
+ 功能修改:会话菜单下增加“打开/关闭自动重连”子菜单,可以动态切换自动重连是否打开。
|
956
967
|
|
957
|
-
|
968
|
+
### 0.17.0 (2023-12-24)
|
958
969
|
+ 功能修改:调整修改GMCP数据的wildcards处理方式,恢复为eval,其余不变。(回滚0.16.2版更改)
|
959
970
|
+ 功能修改:将本地pymud.cfg文件的读取默认编码调整为utf8,以避免加载出现问题
|
960
971
|
+ 问题修复:sessions.py中,修复系统command与会话command重名的问题(这次才发现)
|
@@ -980,7 +991,7 @@ Requires-Dist: prompt-toolkit
|
|
980
991
|
+ 功能修改:所有匹配类对象的匹配模式patterns支持动态修改,涉及Alias,Trigger,Command。修改方式为直接对其patterns属性赋值。如tri.patterns = aNewPattern
|
981
992
|
+ 功能修改:连接/断开连接时刻都会在提示中增加时刻信息,而不论是否异常。
|
982
993
|
|
983
|
-
|
994
|
+
### 0.16.2 (2023-12-19)
|
984
995
|
+ 功能修改:归一化#命令和非#命令处理,使session.exec_command、exec_command_async、exec_command_after均可以处理#命令,例如session.exec_command("#save")。同时,也可以在命令行使用#all发送#命令,如"#all #save"此类
|
985
996
|
+ 功能修改:调整脚本加载与变量自动加载的顺序。当前为连接自动加载时,首先加载变量,然后再加载脚本。目的是使脚本的变化可以覆盖加载的变量内容,而不是反向覆盖。
|
986
997
|
+ 功能修改:会话变量保存和加载可以配置是否打开,默认为打开。见Settings.client["var_autosave] 和 Settings.client["var_autoload"]。同理,该配置可以被本地pymud.cfg所覆盖
|
@@ -989,7 +1000,7 @@ Requires-Dist: prompt-toolkit
|
|
989
1000
|
+ 功能修改:增加GMCP默认处理。当未使用GMCPTrigger对对应的GMCP消息进行处理时,默认使用[GMCP] name: value的形式输出GMCP收到的消息,以便于个人脚本调试。
|
990
1001
|
+ 功能修改:修改GMCP数据的处理方式从eval修改为json.load,其余不变。
|
991
1002
|
|
992
|
-
|
1003
|
+
### 0.16.1.post2 (2023-12-12)
|
993
1004
|
+ 问题修复:修复__init__.py中的__all__变量为字符串
|
994
1005
|
+ 功能增加:可以加载自定义Settings。在执行python -m pymud时,会自动从当前目录读取pymud.cfg文件。使用json格式将配置信息写在该文件中即可。支持模块中settings.py里的sessions, client, server, styles, text字段内容。
|
995
1006
|
+ 功能增加:增加全局变量集,可以使用session.setGlobal和session.getGlobal进行访问,以便于跨session通信。也可以使用#global在命令行访问
|
@@ -1010,5 +1021,5 @@ Requires-Dist: prompt-toolkit
|
|
1010
1021
|
+ 功能修改:CodeLine的执行运行处理修改为不删除中间的多余空白
|
1011
1022
|
+ 问题修复:修改github项目地址为原pymud地址
|
1012
1023
|
|
1013
|
-
|
1014
|
-
首次发布到pip
|
1024
|
+
### 0.15.8 (2023-12-05)
|
1025
|
+
首次发布到pip。
|
@@ -0,0 +1,19 @@
|
|
1
|
+
pymud/__init__.py,sha256=AP4Edhx90gMKrNfD1O_KVciA3SOnyX5Qt9fZY_JhsTY,574
|
2
|
+
pymud/__main__.py,sha256=hFzZjadLlcOuoLM7D8wFiFVO8mqF7vMuo9y-9xfIhRc,64
|
3
|
+
pymud/dialogs.py,sha256=p-LidObSuDyOeMif5CsqhF5qq3rizZ1lmThWHrxDyRg,6726
|
4
|
+
pymud/extras.py,sha256=lCRlNIaYqrcCLGe8zrulFvh7aC5nU_jHFMD2sI9JCrU,40955
|
5
|
+
pymud/logger.py,sha256=sq9HhZ6-prY34NnDUO1NjaCRy-e5-fr2j0na8FKp9ks,5789
|
6
|
+
pymud/main.py,sha256=b_Ui_cN4W8IfhYNyc1duwr3Bp7pYYZQusKTSafCWZIA,6534
|
7
|
+
pymud/modules.py,sha256=XoqTeYfZCgqDsV3SYxeehzsbkTzs0swelAUIxyWuL9g,7423
|
8
|
+
pymud/objects.py,sha256=qSOFuVZvMh3lxjg6x5JUzcr_sTSgakWWySh801x7TNQ,39457
|
9
|
+
pymud/pkuxkx.py,sha256=jRQRUs2xtw7GzYHtLYZXOASnqMumKh0iCoOeKZs8NnU,11467
|
10
|
+
pymud/protocol.py,sha256=nlsyXMBAHEf_067mPNGDHzN_zIm9808D8YDIZTNrygg,49118
|
11
|
+
pymud/pymud.py,sha256=nfCOgKKBU1rPU7sSiZhD1GxJ2HqaPV3entB4T0kH_Wk,52478
|
12
|
+
pymud/session.py,sha256=dO6asU6Pva9JjuhecyUHDCp1qH59FkSIUsmJbTjtFDI,139001
|
13
|
+
pymud/settings.py,sha256=zGVSAo7pHdBiFhYhdS8xf-RJV99cceb24gjRcvgkJBY,7353
|
14
|
+
pymud-0.20.2.dist-info/LICENSE.txt,sha256=IwGE9guuL-ryRPEKi6wFPI_zOhg7zDZbTYuHbSt_SAk,35823
|
15
|
+
pymud-0.20.2.dist-info/METADATA,sha256=5hRMzKEYu0WEfscG9qIkhjPhbjq6zf_K4sk4pOzQDyw,75639
|
16
|
+
pymud-0.20.2.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
17
|
+
pymud-0.20.2.dist-info/entry_points.txt,sha256=diPUOtTkhgC1hVny7Cdg4aRhaHSynMQoraE7ZhJxUcw,37
|
18
|
+
pymud-0.20.2.dist-info/top_level.txt,sha256=8Gp1eXjxixXjqhhti6tLCspV_8s9sNV3z5Em2_KRhD4,6
|
19
|
+
pymud-0.20.2.dist-info/RECORD,,
|
@@ -1,19 +0,0 @@
|
|
1
|
-
pymud/__init__.py,sha256=AP4Edhx90gMKrNfD1O_KVciA3SOnyX5Qt9fZY_JhsTY,574
|
2
|
-
pymud/__main__.py,sha256=hFzZjadLlcOuoLM7D8wFiFVO8mqF7vMuo9y-9xfIhRc,64
|
3
|
-
pymud/dialogs.py,sha256=p-LidObSuDyOeMif5CsqhF5qq3rizZ1lmThWHrxDyRg,6726
|
4
|
-
pymud/extras.py,sha256=Gr-gX7YRWZMmeKV73sk7h_Gf5eZVJ6GcAXvSEfJ4uMI,41124
|
5
|
-
pymud/logger.py,sha256=gtGm8y9RY_CpRpJ0udgKknRxyjsEPrrRyWecUDgBgZM,5662
|
6
|
-
pymud/main.py,sha256=b_Ui_cN4W8IfhYNyc1duwr3Bp7pYYZQusKTSafCWZIA,6534
|
7
|
-
pymud/modules.py,sha256=XoqTeYfZCgqDsV3SYxeehzsbkTzs0swelAUIxyWuL9g,7423
|
8
|
-
pymud/objects.py,sha256=HNWoE6mhUV9SgRsD-2yISt5rVi8h2mTR1sapEZVklNI,39441
|
9
|
-
pymud/pkuxkx.py,sha256=jRQRUs2xtw7GzYHtLYZXOASnqMumKh0iCoOeKZs8NnU,11467
|
10
|
-
pymud/protocol.py,sha256=QfDXjlg2OcJXmVoXf_3mAemnYotRXDUlEZNQjhkfXdA,49106
|
11
|
-
pymud/pymud.py,sha256=pkTb21UV2ccYhG44JjfGdvUSPIWbu_y-vUwBqqzeHaM,51425
|
12
|
-
pymud/session.py,sha256=GAxenTtUy75A-LAfznMOx753BWd-tyozgngeNdJux4Q,135208
|
13
|
-
pymud/settings.py,sha256=ZsKGkjcNGNwqeb2DztwhIrCcRDPYYN1SxtdjWNAJ9CY,7145
|
14
|
-
pymud-0.20.1.post1.dist-info/LICENSE.txt,sha256=IwGE9guuL-ryRPEKi6wFPI_zOhg7zDZbTYuHbSt_SAk,35823
|
15
|
-
pymud-0.20.1.post1.dist-info/METADATA,sha256=TD5fbQU363NwQw5jDJc4Me_6gkLvkaYcGyyn1BnpCwU,74350
|
16
|
-
pymud-0.20.1.post1.dist-info/WHEEL,sha256=R06PA3UVYHThwHvxuRWMqaGcr-PuniXahwjmQRFMEkY,91
|
17
|
-
pymud-0.20.1.post1.dist-info/entry_points.txt,sha256=diPUOtTkhgC1hVny7Cdg4aRhaHSynMQoraE7ZhJxUcw,37
|
18
|
-
pymud-0.20.1.post1.dist-info/top_level.txt,sha256=8Gp1eXjxixXjqhhti6tLCspV_8s9sNV3z5Em2_KRhD4,6
|
19
|
-
pymud-0.20.1.post1.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|