pymud 0.19.4__py3-none-any.whl → 0.20.0a1__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
@@ -1,8 +1,10 @@
1
1
  from .settings import Settings
2
+ from .pymud import PyMudApp
2
3
  from .objects import CodeBlock, Alias, SimpleAlias, Trigger, SimpleTrigger, Command, SimpleCommand, Timer, SimpleTimer, GMCPTrigger
3
4
  from .extras import DotDict
4
5
  from .session import Session
6
+ from .logger import Logger
5
7
 
6
8
  __all__ = [
7
- "Settings", "CodeBlock", "Alias", "SimpleAlias", "Trigger", "SimpleTrigger", "Command", "SimpleCommand", "Timer", "SimpleTimer", "GMCPTrigger", "Session", "PyMudApp", "DotDict"
9
+ "PyMudApp", "Settings", "CodeBlock", "Alias", "SimpleAlias", "Trigger", "SimpleTrigger", "Command", "SimpleCommand", "Timer", "SimpleTimer", "GMCPTrigger", "Session", "PyMudApp", "DotDict", "Logger"
8
10
  ]
pymud/__main__.py CHANGED
@@ -1,4 +1,4 @@
1
- import sys, os, json, platform, shutil, logging
1
+ import sys, os, json, platform, shutil, logging, argparse
2
2
  from .pymud import main
3
3
  from .settings import Settings
4
4
 
@@ -40,63 +40,101 @@ CFG_TEMPLATE = {
40
40
  }
41
41
  }
42
42
 
43
- if __name__ == "__main__":
44
- args = sys.argv
45
- if (len(args) == 2) and (args[1] == "init"):
46
- print(f"欢迎使用PyMUD, 版本{Settings.__version__}. 使用PyMUD时, 建议建立一个新目录(任意位置),并将自己的脚本以及配置文件放到该目录下.")
47
- print("即将开始为首次运行初始化环境...")
48
- uname = platform.uname()
49
- system = uname[0].lower()
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:
50
53
  if system == "windows":
51
- dir = input("检测到当前系统为Windows, 请指定游戏脚本的目录(若目录不存在会自动创建),直接回车表示使用默认值[d:\pkuxkx\]:")
52
- if not dir: dir = "d:\\pkuxkx\\"
54
+ dir = input("检测到当前系统为Windows, 请指定游戏脚本的目录(若目录不存在会自动创建),直接回车表示使用默认值[d:\pkuxkx]:")
55
+ if not dir: dir = "d:\\pkuxkx"
56
+
53
57
  elif system == "linux":
54
- dir = input("检测到当前系统为Linux, 请指定游戏脚本的目录(若目录不存在会自动创建),直接回车表示使用默认值[~/pkuxkx/]:")
55
- if not dir: dir = "~/pkuxkx/"
58
+ dir = input("检测到当前系统为Linux, 请指定游戏脚本的目录(若目录不存在会自动创建),直接回车表示使用默认值[~/pkuxkx]:")
59
+ if not dir: dir = "~/pkuxkx"
56
60
 
57
- if not os.path.exists(dir):
58
- print(f'检测到给定目录 {dir} 不存在,正在创建目录...', end = "")
59
- os.mkdir(dir)
60
- os.chdir(dir)
61
- print(f'完成!')
61
+ elif system == "darwin":
62
+ dir = input("检测到当前系统为MacOS, 请指定游戏脚本的目录(若目录不存在会自动创建),直接回车表示使用默认值[~/pkuxkx]:")
63
+ if not dir: dir = "~/pkuxkx"
62
64
 
63
- if os.path.exists('pymud.cfg'):
64
- print(f'检测到脚本目录下已存在pymud.cfg文件,将直接使用此文件进入PyMUD...')
65
- else:
66
- print(f'检测到脚本目录下不存在pymud.cfg文件,将使用默认内容创建该配置文件...')
67
- with open('pymud.cfg', mode = 'x') as fp:
68
- fp.writelines(json.dumps(CFG_TEMPLATE, indent = 4))
69
-
70
- if not os.path.exists('examples.py'):
71
- from pymud import pkuxkx
72
- module_dir = pkuxkx.__file__
73
- shutil.copyfile(module_dir, 'examples.py')
74
- print(f'已将样例脚本拷贝至脚本目录,并加入默认配置文件')
75
-
76
- print(f"后续可自行修改 {dir} 目录下的 pymud.cfg 文件以进行配置。")
77
- if system == "linux":
78
- print(f"后续运行PyMUD, 请在 {dir} 目录下键入命令: python3 -m pymud")
79
65
  else:
80
- print(f"后续运行PyMUD, 请在 {dir} 目录下键入命令: python -m pymud")
81
-
82
- input('所有内容已初始化完毕, 请按回车进入PyMUD.')
83
-
84
- if (len(args) == 2) and (args[1] == "withlog"):
85
- # 指定带log时,打印log信息
86
- # 所有级别log都存入文件
87
- logging.basicConfig(level=logging.NOTSET,
88
- format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s',
89
- datefmt='%m-%d %H:%M',
90
- filename='myapp.log',
91
- filemode='a')
92
-
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")
93
91
  else:
94
- logging.disable()
92
+ print(f"后续运行PyMUD, 请在 {dir} 目录下键入命令: python3 -m pymud")
95
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
+
96
115
  cfg = "pymud.cfg"
97
116
  if os.path.exists(cfg):
98
117
  with open(cfg, "r", encoding="utf8", errors="ignore") as fp:
99
118
  cfg_data = json.load(fp)
100
119
  main(cfg_data)
101
120
  else:
102
- main()
121
+ main()
122
+
123
+
124
+ 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)
pymud/dialogs.py CHANGED
@@ -1,7 +1,7 @@
1
1
  import asyncio, webbrowser
2
2
 
3
3
  from prompt_toolkit.layout import AnyContainer, ConditionalContainer, Float, VSplit, HSplit, Window, WindowAlign, ScrollablePane, ScrollOffsets
4
- from prompt_toolkit.widgets import Button, Dialog, Label, MenuContainer, MenuItem, TextArea, SystemToolbar, Frame
4
+ from prompt_toolkit.widgets import Button, Dialog, Label, MenuContainer, MenuItem, TextArea, SystemToolbar, Frame, RadioList
5
5
  from prompt_toolkit.layout.dimension import Dimension, D
6
6
  from prompt_toolkit import ANSI, HTML
7
7
  from prompt_toolkit.mouse_events import MouseEvent, MouseEventType
@@ -127,3 +127,30 @@ class NewSessionDialog(BasicDialog):
127
127
  encoding = get_app().layout.get_buffer_by_name("encoding").text
128
128
  result = (name, host, port, encoding)
129
129
  self.set_done(result)
130
+
131
+
132
+ class LogSelectionDialog(BasicDialog):
133
+ def __init__(self, text, values, modal=True):
134
+ self._header_text = text
135
+ self._selection_values = values
136
+ self._radio_list = RadioList(values = self._selection_values)
137
+ super().__init__('选择查看的记录', modal)
138
+
139
+ def create_body(self) -> AnyContainer:
140
+
141
+
142
+ body=HSplit([
143
+ Label(text = self._header_text, dont_extend_height=True),
144
+ self._radio_list
145
+ ])
146
+ return body
147
+
148
+ def create_buttons(self):
149
+ ok_button = EasternButton(text="确定", handler=self.btn_ok_clicked)
150
+ cancel_button = EasternButton(text="取消", handler=(lambda: self.set_done(False)))
151
+ return [ok_button, cancel_button]
152
+
153
+ def btn_ok_clicked(self):
154
+ result = self._radio_list.current_value
155
+ self.set_done(result)
156
+
pymud/extras.py CHANGED
@@ -2,7 +2,7 @@
2
2
  from unicodedata import east_asian_width
3
3
  from wcwidth import wcwidth
4
4
  from typing import Any
5
- import time
5
+ import time, datetime
6
6
 
7
7
  from typing import Iterable
8
8
  from prompt_toolkit import ANSI
@@ -434,6 +434,7 @@ class SessionBufferControl(BufferControl):
434
434
  buffer.cursor_position = start
435
435
  buffer.start_selection(selection_type=SelectionType.CHARACTERS)
436
436
  buffer.cursor_position = end
437
+
437
438
  else:
438
439
  # Don't handle scroll events here.
439
440
  return NotImplemented
@@ -1078,4 +1079,5 @@ class Plugin:
1078
1079
 
1079
1080
  def __getattr__(self, __name: str) -> Any:
1080
1081
  if hasattr(self.mod, __name):
1081
- return self.mod.__getattribute__(__name)
1082
+ return self.mod.__getattribute__(__name)
1083
+
pymud/logger.py ADDED
@@ -0,0 +1,154 @@
1
+ import os, re, datetime, threading
2
+ from queue import SimpleQueue, Empty
3
+
4
+ class Logger:
5
+ """
6
+ PyMUD 的记录器类型,可用于会话中向文件记录数据
7
+
8
+ :param name: 记录器名称,各记录器名称应保持唯一。记录器名称会作为记录文件名称的主要参数
9
+ :param mode: 记录模式。可选模式包括 a, w, n 三种。
10
+ - a为添加模式,当新开始记录时,会添加在原有记录文件(name.log)之后。
11
+ - w为覆写模式,当新开始记录时,会覆写原记录文件(name.log)。
12
+ - n为新建模式,当新开始记录时,会以name和当前时间为参数新建一个文件(name.now.log)用于记录。
13
+ :param encoding: 记录文件的编码格式,默认为 "utf-8"
14
+ :param errors: 当编码模式失败时的处理方式,默认为 "ignore"
15
+ :param raw: 记录带ANSI标记的原始内容,还是记录纯文本内容,默认为True,即记录带ANSI标记的原始内容。
16
+ """
17
+
18
+ _esc_regx = re.compile("\x1b\\[[\d;]+[abcdmz]", flags = re.IGNORECASE)
19
+
20
+ def __init__(self, name, mode = 'a', encoding = "utf-8", errors = "ignore", raw = False):
21
+ self._name = name
22
+ self._enabled = False
23
+ self._raw = raw
24
+ self.mode = mode
25
+ self._encoding = encoding
26
+ self._errors = errors
27
+ self._lock = threading.RLock()
28
+ self._stream = None
29
+
30
+ self._queue = SimpleQueue()
31
+
32
+ @property
33
+ def name(self):
34
+ "记录器名称,仅在创建时设置,过程中只读"
35
+ return self._name
36
+
37
+ @property
38
+ def enabled(self):
39
+ """
40
+ 使能属性。
41
+ 从false切换到true时,会打开文件,创建后台线程进行记录。
42
+ 从true切换到false时,会终止后台记录线程,并关闭记录文件。
43
+ """
44
+ return self._enabled
45
+
46
+ @enabled.setter
47
+ def enabled(self, enabled):
48
+ if self._enabled != enabled:
49
+ if enabled:
50
+ mode = "a"
51
+
52
+ if self._mode in ("a", "w"):
53
+ filename = f"{self.name}.log"
54
+ mode = self._mode
55
+ elif self._mode == "n":
56
+ now = datetime.datetime.now().strftime("%Y_%m_%d_%H_%M_%S")
57
+ filename = f"{self.name}.{now}.log"
58
+
59
+ filename = os.path.abspath(filename)
60
+ self._stream = open(filename, mode = mode, encoding = self._encoding, errors = self._errors)
61
+ self._thread = t = threading.Thread(target=self._monitor)
62
+ t.daemon = True
63
+ t.start()
64
+
65
+ else:
66
+ self._queue.put_nowait(None)
67
+ self._thread.join()
68
+ self._thread = None
69
+ self._closeFile()
70
+
71
+ self._enabled = enabled
72
+
73
+ @property
74
+ def raw(self):
75
+ "属性,设置和获取是否记录带有ANSI标记的原始记录"
76
+ return self._raw
77
+
78
+ @raw.setter
79
+ def raw(self, val: bool):
80
+ self._raw = val
81
+
82
+ @property
83
+ def mode(self):
84
+ "属性,记录器模式,可为 a, w, n"
85
+ return self._mode
86
+
87
+ @mode.setter
88
+ def mode(self, value):
89
+ if value in ("a", "w", "n"):
90
+ self._mode = value
91
+
92
+ def _closeFile(self):
93
+ """
94
+ Closes the stream.
95
+ """
96
+ self._lock.acquire()
97
+ try:
98
+ if self._stream:
99
+ try:
100
+ self._stream.flush()
101
+ finally:
102
+ stream = self._stream
103
+ self._stream = None
104
+ stream.close()
105
+ finally:
106
+ self._lock.release()
107
+
108
+ def log(self, msg):
109
+ """
110
+ 向记录器记录信息。记录的信息会通过队列发送到独立的记录线程。
111
+ 当记录器未使能时,使用该函数调用也不会记录。
112
+
113
+ :param msg: 要记录的信息
114
+ """
115
+ if self._enabled:
116
+ self._queue.put_nowait(msg)
117
+
118
+ def _monitor(self):
119
+ """
120
+ Monitor the queue for records, and ask the handler
121
+ to deal with them.
122
+
123
+ This method runs on a separate, internal thread.
124
+ The thread will terminate if it sees a sentinel object in the queue.
125
+ """
126
+ newline = True
127
+ while True:
128
+ try:
129
+ data = self._queue.get(block = True)
130
+ if data:
131
+ self._lock.acquire()
132
+
133
+ if newline:
134
+ now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
135
+ header = f"{now} {self._name}: "
136
+ self._stream.write(header)
137
+ newline = False
138
+
139
+ if data.endswith("\n"):
140
+ data = data.rstrip("\n").rstrip("\r") + "\n"
141
+ newline = True
142
+
143
+ if not self._raw:
144
+ data = self._esc_regx.sub("", data)
145
+
146
+ self._stream.write(data)
147
+
148
+ self._stream.flush()
149
+ self._lock.release()
150
+
151
+ else:
152
+ break
153
+ except Empty:
154
+ break
pymud/objects.py CHANGED
@@ -5,7 +5,6 @@ MUD会话(session)中, 支持的对象列表
5
5
  import asyncio, logging, re
6
6
  from collections.abc import Iterable
7
7
  from collections import namedtuple
8
- import functools
9
8
  from typing import Any
10
9
  from .settings import Settings
11
10
 
@@ -991,3 +990,4 @@ class SimpleTimer(Timer):
991
990
 
992
991
  def __detailed__(self) -> str:
993
992
  return f'<{self.__class__.__name__}> id = "{self.id}" group = "{self.group}" enabled = {self.enabled} timeout = {self.timeout} code = "{self._code}"'
993
+
pymud/pymud.py CHANGED
@@ -1,6 +1,7 @@
1
- import asyncio, functools, re, logging, math, json, os, webbrowser
1
+ import asyncio, functools, re, logging, math, json, os, webbrowser, threading
2
+ from datetime import datetime
2
3
  import importlib.util
3
- from prompt_toolkit.shortcuts import set_title
4
+ from prompt_toolkit.shortcuts import set_title, radiolist_dialog
4
5
  from prompt_toolkit.output import ColorDepth
5
6
  from prompt_toolkit.clipboard.pyperclip import PyperclipClipboard
6
7
  from prompt_toolkit import HTML
@@ -8,7 +9,7 @@ from prompt_toolkit.buffer import Buffer
8
9
  from prompt_toolkit.application import Application
9
10
  from prompt_toolkit.filters import Condition
10
11
  from prompt_toolkit.key_binding import KeyBindings
11
- from prompt_toolkit.layout import ConditionalContainer, Float, VSplit, HSplit, Window, WindowAlign
12
+ from prompt_toolkit.layout import ConditionalContainer, Float, VSplit, HSplit, Window, WindowAlign, ScrollbarMargin, NumberedMargin
12
13
  from prompt_toolkit.layout.layout import Layout
13
14
  from prompt_toolkit.layout.controls import FormattedTextControl
14
15
  from prompt_toolkit.layout.dimension import D
@@ -38,7 +39,7 @@ from .objects import CodeBlock
38
39
  from .extras import MudFormatProcessor, SessionBuffer, EasternMenuContainer, VSplitWindow, SessionBufferControl, DotDict, Plugin
39
40
  from .session import Session
40
41
  from .settings import Settings
41
- from .dialogs import MessageDialog, WelcomeDialog, QueryDialog, NewSessionDialog
42
+ from .dialogs import MessageDialog, WelcomeDialog, QueryDialog, NewSessionDialog, LogSelectionDialog
42
43
 
43
44
  from enum import Enum
44
45
 
@@ -140,6 +141,12 @@ class PyMudApp:
140
141
  set_title("{} {}".format(Settings.__appname__, Settings.__version__))
141
142
  self.set_status(Settings.text["welcome"])
142
143
 
144
+ self.loggers = dict() # 所有记录字典
145
+ self.showLog = False # 是否显示记录页
146
+ self.logFileShown = '' # 记录页显示的记录文件名
147
+ self.logSessionBuffer = SessionBuffer()
148
+ self.logSessionBuffer.name = "LOGBUFFER"
149
+
143
150
  self.load_plugins()
144
151
 
145
152
  def initUI(self):
@@ -193,6 +200,7 @@ class PyMudApp:
193
200
  width = D(preferred = Settings.client["naws_width"]),
194
201
  height = D(preferred = Settings.client["naws_height"]),
195
202
  wrap_lines=Condition(lambda: is_true(self.wrap_lines)),
203
+ #left_margins=[NumberedMargin()],
196
204
  #right_margins=[ScrollbarMargin(True)],
197
205
  style="class:text-area"
198
206
  )
@@ -363,16 +371,26 @@ class PyMudApp:
363
371
  menu.children.append(sub)
364
372
  menus.append(menu)
365
373
 
374
+ menus.append(MenuItem("-", disabled=True))
375
+ menus.append(MenuItem(Settings.text["show_log"], handler = self.show_logSelectDialog))
366
376
  menus.append(MenuItem("-", disabled=True))
367
377
  menus.append(MenuItem(Settings.text["exit"], handler=self.act_exit))
368
378
 
369
379
  return menus
370
380
 
381
+ def invalidate(self):
382
+ "刷新显示界面"
383
+ self.app.invalidate()
384
+
371
385
  def scroll(self, lines = 1):
372
386
  "内容滚动指定行数,小于0为向上滚动,大于0为向下滚动"
373
387
  if self.current_session:
374
388
  s = self.current_session
375
389
  b = s.buffer
390
+ elif self.showLog:
391
+ b = self.logSessionBuffer
392
+
393
+ if isinstance(b, Buffer):
376
394
  if lines < 0:
377
395
  b.cursor_up(-1 * lines)
378
396
  elif lines > 0:
@@ -439,11 +457,22 @@ class PyMudApp:
439
457
  new_key = keys[idx+1]
440
458
  self.activate_session(new_key)
441
459
 
460
+ elif (idx == count -1) and self.showLog:
461
+ self.showLogInTab()
462
+
442
463
  elif event.key_sequence[-1].key == Keys.ControlLeft:
443
464
  if idx > 0:
444
465
  new_key = keys[idx-1]
445
466
  self.activate_session(new_key)
446
467
 
468
+ else:
469
+ if self.showLog:
470
+ if event.key_sequence[-1].key == Keys.ControlLeft:
471
+ keys = list(self.sessions.keys())
472
+ if len(keys) > 0:
473
+ new_key = keys[-1]
474
+ self.activate_session(new_key)
475
+
447
476
  def toggle_mousesupport(self, event: KeyPressEvent):
448
477
  """快捷键F2: 切换鼠标支持状态。用于远程连接时本地复制命令执行操作"""
449
478
  self._mouse_support = not self._mouse_support
@@ -482,8 +511,8 @@ class PyMudApp:
482
511
  selection = line_plain[start:end]
483
512
  self.app.clipboard.set_text(selection)
484
513
  self.set_status("已复制:{}".format(selection))
485
-
486
- self.current_session.setVariable("%copy", selection)
514
+ if self.current_session:
515
+ self.current_session.setVariable("%copy", selection)
487
516
  else:
488
517
  # 多行只认行
489
518
  lines = []
@@ -494,8 +523,9 @@ class PyMudApp:
494
523
 
495
524
  self.app.clipboard.set_text("\n".join(lines))
496
525
  self.set_status("已复制:行数{}".format(1 + erow - srow))
497
-
498
- self.current_session.setVariable("%copy", "\n".join(lines))
526
+
527
+ if self.current_session:
528
+ self.current_session.setVariable("%copy", "\n".join(lines))
499
529
 
500
530
  else:
501
531
  # Control-R 复制带有ANSI标记的原始内容(对应字符关系会不正确,因此RAW复制时自动整行复制)
@@ -503,15 +533,18 @@ class PyMudApp:
503
533
  line = b.document.current_line
504
534
  self.app.clipboard.set_text(line)
505
535
  self.set_status("已复制:{}".format(line))
506
-
507
- self.current_session.setVariable("%copy", line)
536
+
537
+ if self.current_session:
538
+ self.current_session.setVariable("%copy", line)
508
539
 
509
540
  else:
510
541
  lines = b.document.lines[srow:erow+1]
511
542
  copy_raw_text = "".join(lines)
512
543
  self.app.clipboard.set_text(copy_raw_text)
513
544
  self.set_status("已复制:行数{}".format(1 + erow - srow))
514
- self.current_session.setVariable("%copy", copy_raw_text)
545
+
546
+ if self.current_session:
547
+ self.current_session.setVariable("%copy", copy_raw_text)
515
548
 
516
549
  # data = self.consoleView.buffer.copy_selection()
517
550
  # self.app.clipboard.set_data(data)
@@ -552,6 +585,52 @@ class PyMudApp:
552
585
 
553
586
  return result
554
587
 
588
+ def show_logSelectDialog(self):
589
+ async def coroutine():
590
+ head_line = " {}{}{}".format('记录文件名'.ljust(15), '文件大小'.rjust(16), '最后修改时间'.center(17))
591
+
592
+ log_list = list()
593
+ files = [f for f in os.listdir('.') if os.path.isfile(f) and f.endswith('.log')]
594
+ for file in files:
595
+ filename = os.path.basename(file).ljust(20)
596
+ filesize = f"{os.path.getsize(file):,} Bytes".rjust(20)
597
+ # ctime = datetime.fromtimestamp(os.path.getctime(file)).strftime('%Y-%m-%d %H:%M:%S').rjust(23)
598
+ mtime = datetime.fromtimestamp(os.path.getmtime(file)).strftime('%Y-%m-%d %H:%M:%S').rjust(23)
599
+
600
+ file_display_line = "{}{}{}".format(filename, filesize, mtime)
601
+ log_list.append((file, file_display_line))
602
+
603
+ dialog = LogSelectionDialog(
604
+ text = head_line,
605
+ values = log_list
606
+ )
607
+
608
+ result = await self.show_dialog_as_float(dialog)
609
+
610
+ if result:
611
+ self.logFileShown = result
612
+ self.showLogInTab()
613
+
614
+ asyncio.ensure_future(coroutine())
615
+
616
+ def showLogInTab(self):
617
+ "在记录也显示LOG记录"
618
+ self.current_session = None
619
+ self.showLog = True
620
+
621
+ if self.logFileShown:
622
+ filename = os.path.abspath(self.logFileShown)
623
+ if os.path.exists(filename):
624
+ lock = threading.RLock()
625
+ lock.acquire()
626
+ with open(filename, 'r', encoding = 'utf-8', errors = 'ignore') as file:
627
+ self.logSessionBuffer._set_text(file.read())
628
+ lock.release()
629
+
630
+ self.logSessionBuffer.cursor_position = len(self.logSessionBuffer.text)
631
+ self.consoleView.buffer = self.logSessionBuffer
632
+ self.app.invalidate()
633
+
555
634
  def activate_session(self, key):
556
635
  "激活指定名称的session,并将该session设置为当前session"
557
636
  session = self.sessions.get(key, None)
@@ -627,9 +706,22 @@ class PyMudApp:
627
706
  b.exit_selection()
628
707
  b.cursor_position = len(b.text)
629
708
 
709
+ elif self.showLog:
710
+ b = self.logSessionBuffer
711
+ b.exit_selection()
712
+ b.cursor_position = len(b.text)
713
+
630
714
  def act_close_session(self):
631
715
  "菜单: 关闭当前会话"
632
- self.close_session()
716
+ if self.current_session:
717
+ self.close_session()
718
+
719
+ elif self.showLog:
720
+ self.showLog = False
721
+ self.logSessionBuffer.text = ""
722
+ if len(self.sessions.keys()) > 0:
723
+ new_sess = list(self.sessions.keys())[0]
724
+ self.activate_session(new_sess)
633
725
 
634
726
  def act_echoinput(self):
635
727
  "菜单: 显示/隐藏输入指令"
@@ -722,12 +814,21 @@ class PyMudApp:
722
814
  def btn_title_clicked(self, name, mouse_event: MouseEvent):
723
815
  "顶部会话标签点击切换鼠标事件"
724
816
  if mouse_event.event_type == MouseEventType.MOUSE_UP:
725
- self.activate_session(name)
817
+ if name == '[LOG]':
818
+ self.showLogInTab()
819
+ else:
820
+ self.activate_session(name)
726
821
 
727
822
  def get_frame_title(self):
728
823
  "顶部会话标题选项卡"
729
824
  if len(self.sessions.keys()) == 0:
730
- return Settings.__appname__ + " " + Settings.__version__
825
+ if not self.showLog:
826
+ return Settings.__appname__ + " " + Settings.__version__
827
+ else:
828
+ if self.logFileShown:
829
+ return f'[LOG] {self.logFileShown}'
830
+ else:
831
+ return f'[LOG]'
731
832
 
732
833
  title_formatted_list = []
733
834
  for key, session in self.sessions.items():
@@ -746,6 +847,17 @@ class PyMudApp:
746
847
  title_formatted_list.append((style, key, functools.partial(self.btn_title_clicked, key)))
747
848
  title_formatted_list.append(("", " | "))
748
849
 
850
+ if self.showLog:
851
+ if self.current_session is None:
852
+ style = style = Settings.styles["selected"]
853
+ else:
854
+ style = Settings.styles["normal"]
855
+
856
+ title = f'[LOG] {self.logFileShown}' if self.logFileShown else f'[LOG]'
857
+
858
+ title_formatted_list.append((style, title, functools.partial(self.btn_title_clicked, '[LOG]')))
859
+ title_formatted_list.append(("", " | "))
860
+
749
861
  return title_formatted_list[:-1]
750
862
 
751
863
  def get_statusbar_text(self):
@@ -880,13 +992,20 @@ class PyMudApp:
880
992
  self.current_session.writeline("")
881
993
  else:
882
994
  try:
995
+ self.current_session.log.log(f"命令行键入: {cmd_line}\n")
996
+
883
997
  cb = CodeBlock(cmd_line)
884
998
  cb.execute(self.current_session)
885
999
  except Exception as e:
886
1000
  self.current_session.warning(e)
887
1001
  self.current_session.exec_command(cmd_line)
888
1002
  else:
889
- self.set_status("当前没有正在运行的session.")
1003
+ if cmd_line == "#exit":
1004
+ self.act_exit()
1005
+ elif (cmd_line == "#close") and self.showLog:
1006
+ self.act_close_session()
1007
+ else:
1008
+ self.set_status("当前没有正在运行的session.")
890
1009
 
891
1010
  # 配置:命令行内容保留
892
1011
  if Settings.client["remain_last_input"]:
@@ -1013,7 +1132,7 @@ class PyMudApp:
1013
1132
  self._plugins[plugin.name] = plugin
1014
1133
  plugin.onAppInit(self)
1015
1134
  except Exception as e:
1016
- self.set_status(f"文件: {plugins_dir}\{file} 不是一个合法的插件文件,加载错误,信息为: {e}")
1135
+ self.set_status(f"文件: {plugins_dir}\\{file} 不是一个合法的插件文件,加载错误,信息为: {e}")
1017
1136
 
1018
1137
  # 然后加载当前目录下的插件
1019
1138
  current_dir = os.path.abspath(".")
@@ -1028,8 +1147,7 @@ class PyMudApp:
1028
1147
  self._plugins[plugin.name] = plugin
1029
1148
  plugin.onAppInit(self)
1030
1149
  except Exception as e:
1031
- self.set_status(f"文件: {plugins_dir}\{file} 不是一个合法的插件文件. 加载错误,信息为: {e}")
1032
-
1150
+ self.set_status(f"文件: {plugins_dir}\\{file} 不是一个合法的插件文件. 加载错误,信息为: {e}")
1033
1151
 
1034
1152
  def reload_plugin(self, plugin: Plugin):
1035
1153
  "重新加载指定插件"
pymud/session.py CHANGED
@@ -1,7 +1,11 @@
1
1
  import asyncio, logging, re, math, os, pickle, datetime, importlib, importlib.util, sysconfig
2
2
  from collections.abc import Iterable
3
3
  from collections import OrderedDict
4
+ import logging, queue
5
+ from logging import FileHandler
6
+ from logging.handlers import QueueHandler, QueueListener
4
7
 
8
+ from .logger import Logger
5
9
  from .extras import SessionBuffer, DotDict, Plugin
6
10
  from .protocol import MudClientProtocol
7
11
  from .objects import Trigger, Alias, Command, Timer, SimpleAlias, SimpleTrigger, SimpleTimer, GMCPTrigger, CodeBlock, CodeLine
@@ -73,6 +77,8 @@ class Session:
73
77
  "py", # 直接执行python语句
74
78
 
75
79
  "all", # 所有会话执行
80
+
81
+ "log", # 记录处置
76
82
  )
77
83
 
78
84
  _commands_alias = {
@@ -96,7 +102,8 @@ class Session:
96
102
  def __init__(self, app, name, host, port, encoding = None, after_connect = None, loop = None, **kwargs):
97
103
  self.pyversion = sysconfig.get_python_version()
98
104
  self.loop = loop or asyncio.get_running_loop()
99
- self.log = logging.getLogger("pymud.Session")
105
+ self.syslog = logging.getLogger("pymud.Session")
106
+
100
107
  self.application = app
101
108
  self.name = name
102
109
  self._transport = None
@@ -109,6 +116,9 @@ class Session:
109
116
  self._events["connected"] = None
110
117
  self._events["disconnected"] = None
111
118
 
119
+ self._loggers = dict()
120
+ self.log = self.getLogger(name)
121
+
112
122
  self._auto_script = kwargs.get("scripts", None)
113
123
 
114
124
  self._cmds_handler = dict() # 支持的命令的处理函数字典
@@ -159,7 +169,6 @@ class Session:
159
169
  self.warning(f"自动从{file}中加载变量失败,错误消息为: {e}")
160
170
 
161
171
 
162
-
163
172
  if self._auto_script:
164
173
  self.info(f"即将自动加载以下模块:{self._auto_script}")
165
174
  self.load_module(self._auto_script)
@@ -340,6 +349,33 @@ class Session:
340
349
  def event_disconnected(self, event):
341
350
  self._events["disconnected"] = event
342
351
 
352
+ def getLogger(self, name, mode = 'a', encoding = 'utf-8', encoding_errors = 'ignore', raw = False) -> Logger:
353
+ """
354
+ 根据指定名称和参数获取并返回一个记录器。若指定名称不存在,则创建一个该名称记录器。
355
+
356
+ :param name: 指定的记录器名称
357
+ :param mode: 记录器的模式,可接受值为 a, w, n。 具体请参见 Logger 对象中 mode 参数
358
+ :param encoding: 记录文件的编码格式
359
+ :param encoding_errors: 编码错误的处理方式
360
+ :param raw: 是否以带ANSI标记的原始格式进行记录
361
+
362
+ :return 指定名称的记录器 Logger 对象
363
+ """
364
+ if name not in self.application.loggers.keys():
365
+ logger = Logger(name, mode, encoding, encoding_errors, raw)
366
+ self._loggers[name] = logger
367
+ self.application.loggers[name] = logger
368
+
369
+ else:
370
+ if name not in self._loggers.keys():
371
+ self.warning(f"其它会话中已存在一个名为 {name} 的记录器,将直接返回该记录器")
372
+
373
+ logger = self.application.loggers[name]
374
+ logger.mode = mode
375
+ logger.raw = raw
376
+
377
+ return logger
378
+
343
379
  @property
344
380
  def modules(self) -> OrderedDict:
345
381
  """
@@ -484,6 +520,7 @@ class Session:
484
520
  :param newline: 是否额外增加换行符
485
521
  """
486
522
  self.buffer.insert_text(data)
523
+ self.log.log(data)
487
524
 
488
525
  if len(data) > 0 and (data[-1] == "\n"):
489
526
  self._line_count += 1
@@ -491,6 +528,7 @@ class Session:
491
528
  if newline:
492
529
  self.buffer.insert_text(self.newline_cli)
493
530
  self._line_count += 1
531
+ self.log.log(self.newline_cli)
494
532
 
495
533
  def clear_half(self):
496
534
  """
@@ -520,7 +558,7 @@ class Session:
520
558
  if self.connected:
521
559
  self._transport.write_eof()
522
560
  self.state = "DISCONNECTED"
523
- self.log.info(f"服务器断开连接! {self._protocol.__repr__}")
561
+ self.syslog.info(f"服务器断开连接! {self._protocol.__repr__}")
524
562
 
525
563
  def feed_gmcp(self, name, value) -> None:
526
564
  """
@@ -703,19 +741,22 @@ class Session:
703
741
  if self.seperator in line:
704
742
  lines = line.split(self.seperator)
705
743
  for ln in lines:
744
+ if Settings.client["echo_input"]:
745
+ self.writetobuffer(f"\x1b[32m{ln}\x1b[0m")
746
+ else:
747
+ self.log.log(f"\x1b[32m{ln}\x1b[0m\n")
748
+
706
749
  cmd = ln + self.newline
707
750
  self.write(cmd.encode(self.encoding, Settings.server["encoding_errors"]))
708
751
 
709
- if Settings.client["echo_input"]:
710
- self.writetobuffer(f"\x1b[32m{cmd}\x1b[0m")
711
-
712
752
  else:
753
+ if Settings.client["echo_input"]:
754
+ self.writetobuffer(f"\x1b[32m{line}\x1b[0m")
755
+ else:
756
+ self.log.log(f"\x1b[32m{line}\x1b[0m\n")
757
+
713
758
  cmd = line + self.newline
714
759
  self.write(cmd.encode(self.encoding, Settings.server["encoding_errors"]))
715
-
716
- #if Settings.client["echo_input"]:
717
- if Settings.client["echo_input"] and (len(cmd) > len(self.newline)): # 修改2023-12-3, 向服务器发送空回车时,不回显
718
- self.writetobuffer(f"\x1b[32m{cmd}\x1b[0m")
719
760
 
720
761
  def exec(self, cmd: str, name = None, *args, **kwargs):
721
762
  """
@@ -1604,7 +1645,8 @@ class Session:
1604
1645
  - #session
1605
1646
  '''
1606
1647
 
1607
- self.application.close_session()
1648
+ #self.application.close_session()
1649
+ self.application.act_close_session()
1608
1650
 
1609
1651
  async def handle_wait(self, code: CodeLine = None, *args, **kwargs):
1610
1652
  '''
@@ -2316,7 +2358,12 @@ class Session:
2316
2358
  else:
2317
2359
  mod = self._modules[module_name]["module"]
2318
2360
  config = self._modules[module_name]["config"]
2319
- if config: del config
2361
+ if config:
2362
+ if hasattr(config, "unload"):
2363
+ unload = getattr(config, "unload", None)
2364
+ if callable(unload):
2365
+ unload(config)
2366
+ del config
2320
2367
  mod = importlib.reload(mod)
2321
2368
  if hasattr(mod, 'Configuration'):
2322
2369
  config = mod.Configuration(self)
@@ -2832,3 +2879,94 @@ class Session:
2832
2879
  :param style: 要输出信息的格式(ANSI), 默认为 ERR_STYLE, \x1b[31m
2833
2880
  """
2834
2881
  self.info2(msg, title, style)
2882
+
2883
+ def handle_log(self, code: CodeLine = None, *args, **kwargs):
2884
+ '''
2885
+ 嵌入命令 #log 的执行函数,控制当前会话的记录状态。
2886
+ 该函数不应该在代码中直接调用。
2887
+
2888
+ 使用:
2889
+ - #log : 显示所有记录器的状态情况
2890
+ - #log start [logger-name] [-a|-w|-n] [-r] : 启动一个记录器
2891
+
2892
+ 参数:
2893
+ - :logger-name: 记录器名称。当不指定时,选择名称为会话名称的记录器(会话默认记录器)
2894
+ - :-a|-w|-n: 记录器模式选择。 -a 为添加模式(未指定时默认值),在原记录文件后端添加; -w 为覆写模式,清空原记录文件并重新记录; -n 为新建模式,以名称和当前时间为参数,使用 name.now.log 形式创建新的记录文件
2895
+ - :-r: 指定记录器是否使用 raw 模式
2896
+
2897
+ - #log stop [logger-name] : 停止一个记录器
2898
+
2899
+ 参数:
2900
+ - :logger-name: 记录器名称。当不指定时,选择名称为会话名称的记录器(会话默认记录器)
2901
+
2902
+ - #log show [loggerFile]: 显示全部日志记录或指定记录文件
2903
+
2904
+ 参数:
2905
+ - :loggerFile: 要显示的记录文件名称。当不指定时,弹出对话框列出当前目录下所有记录文件
2906
+
2907
+ 示例:
2908
+ - ``#log`` : 在当前会话窗口列出所有记录器状态
2909
+ - ``#log start`` : 启动本会话默认记录器(记录器名为会话名)。该记录器以纯文本模式,将后续所有屏幕输出、键盘键入、命令输入等记录到 name.log 文件的后端
2910
+ - ``#log start -r`` : 启动本会话默认记录器。该记录器以raw模式,将后续所有屏幕输出、键盘键入、命令输入等记录到 name.log 文件的后端
2911
+ - ``#log start chat`` : 启动名为 chat 的记录器。该记录器以纯文本模式,记录代码中调用过该记录器 .log 进行记录的信息
2912
+ - ``#log stop`` : 停止本会话默认记录器(记录器名为会话名)。
2913
+
2914
+ 注意:
2915
+ - 记录器文件模式(-a|-w|-n)在修改后,只有在下一次该记录器启动时才会生效
2916
+ - 记录器记录模式(-r)在修改后立即生效
2917
+ '''
2918
+
2919
+ args = list()
2920
+ if isinstance(code, CodeLine):
2921
+ args = code.code[2:]
2922
+
2923
+ if len(args) == 0:
2924
+ session_loggers = set(self._loggers.keys())
2925
+ app_loggers = set(self.application.loggers.keys()).difference(session_loggers)
2926
+ self.info("本会话中的记录器情况:")
2927
+ for name in session_loggers:
2928
+ logger = self.application.loggers[name]
2929
+ self.info(f"记录器 {logger.name}, 当前状态: {'开启' if logger.enabled else '关闭'}, 文件模式: {logger.mode}, 记录模式: {'ANSI' if logger.raw else '纯文本'}")
2930
+
2931
+ if len(app_loggers) > 0:
2932
+ self.info("本应用其他会话中的记录器情况:")
2933
+ for name in app_loggers:
2934
+ logger = self.application.loggers[name]
2935
+ self.info(f"记录器 {logger.name}, 当前状态: {'开启' if logger.enabled else '关闭'}, 文件模式: {logger.mode}, 记录模式: {'ANSI' if logger.raw else '纯文本'}")
2936
+
2937
+ else:
2938
+ name = self.name
2939
+ if len(args) > 1 and not args[1].startswith('-'):
2940
+ name = args[1]
2941
+
2942
+ if (args[0] == "start"):
2943
+ if "-n" in args:
2944
+ mode = "n"
2945
+ mode_name = '新建'
2946
+ elif "-w" in args:
2947
+ mode = "w"
2948
+ mode_name = '覆写'
2949
+ else:
2950
+ mode = "a"
2951
+ mode_name = '添加'
2952
+
2953
+ raw = True if "-r" in args else False
2954
+ raw_name = '原始ANSI' if raw else '纯文本'
2955
+
2956
+ logger = self.getLogger(name = name, mode = mode, raw = raw)
2957
+ logger.enabled = True
2958
+
2959
+ self.info(f"{datetime.datetime.now()}: 记录器{name}以{mode_name}文件模式以及{raw_name}记录模式开启。")
2960
+
2961
+ elif (args[0] == "stop"):
2962
+ self.info(f"{datetime.datetime.now()}: 记录器{name}记录已关闭。")
2963
+ self.log.enabled = False
2964
+
2965
+ elif (args[0] == "show"):
2966
+ if len(args) > 1 and not args[1].startswith('-'):
2967
+ file = args[1]
2968
+ self.application.logFileShown = file
2969
+ self.application.showLogInTab()
2970
+ else:
2971
+ self.application.show_logSelectDialog()
2972
+
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.19.4"
14
+ __version__ = "0.20.0"
15
15
  "APP 当前版本"
16
- __release__ = "2024-04-20"
16
+ __release__ = "2024-08-14"
17
17
  "APP 当前版本发布日期"
18
18
  __author__ = "本牛(newstart)@北侠"
19
19
  "APP 作者"
@@ -79,6 +79,7 @@ class Settings:
79
79
  "welcome" : "欢迎使用PYMUD客户端 - 北大侠客行,最好的中文MUD游戏",
80
80
  "world" : "世界",
81
81
  "new_session" : "创建新会话...",
82
+ "show_log" : "显示记录信息",
82
83
  "exit" : "退出",
83
84
  "session" : "会话",
84
85
  "connect" : "连接/重新连接",
@@ -88,7 +89,7 @@ class Settings:
88
89
  "copy" : "复制(纯文本)",
89
90
  "copyraw" : "复制(ANSI)",
90
91
  "clearsession" : "清空会话内容",
91
- "closesession" : "关闭当前会话",
92
+ "closesession" : "关闭当前页面",
92
93
  "autoreconnect" : "打开/关闭自动重连",
93
94
  "loadconfig" : "加载脚本配置",
94
95
  "reloadconfig" : "重新加载脚本配置",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pymud
3
- Version: 0.19.4
3
+ Version: 0.20.0a1
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>
@@ -970,3 +970,9 @@ Requires-Dist: prompt-toolkit
970
970
  + 功能调整: #var, #global 指令中,现在对字符串会先使用 eval 转换类型,转换失败时使用 str 类型。例如, #var myvar 1 时,myvar类型将为int
971
971
  + 功能调整: 变量替代时,会自动实现类型转化,当被替代变量值为非 str 类型时不会再报错
972
972
  + 问题修复: 修复之前从后向前选择时,无法复制的问题
973
+
974
+ ### 0.19.5 (2024-08-XX)
975
+ + 功能调整: 恢复在__init__.py中增加PyMudApp的导出,可以恢复使用from pymud import PyMudApp了
976
+ + 功能调整: 在没有session的时候,也可以执行#exit命令
977
+ + 功能调整: 模块加载和重新加载前,会自动调用模块的unload方法(若有)
978
+ + 功能新增: 增加log功能(待设计)
@@ -0,0 +1,17 @@
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,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.43.0)
2
+ Generator: setuptools (72.2.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,16 +0,0 @@
1
- pymud/__init__.py,sha256=5SteBKAKAizt98QOxDRuGxRHhoDAijdG8lC5U7az2SM,422
2
- pymud/__main__.py,sha256=I6jFLnTErSuM0G828WUx5EIAmMNAzqYr2AT1zdtGmp0,4636
3
- pymud/dialogs.py,sha256=SRNHOashupNi7128Bg_35J1rrI2TsZEakTbaMW5d9PU,5596
4
- pymud/extras.py,sha256=p2fgnzKFtaAvcYqayMNSZhrC3BpvfkbF_la3SfGaWBA,42400
5
- pymud/objects.py,sha256=hERTIctGF660CoHwZf00-5RZ1-bqCPK2QuwmTZ7NceU,38088
6
- pymud/pkuxkx.py,sha256=vWXHU6GF0HQ0eWb3LmxFVRP0cKnigffCX7Z-LJvwVtw,11496
7
- pymud/protocol.py,sha256=QfDXjlg2OcJXmVoXf_3mAemnYotRXDUlEZNQjhkfXdA,49106
8
- pymud/pymud.py,sha256=HgmAXBXNMjo8xKAVQRS2XOshp-hieEy7bjvaXGq_0Y0,43230
9
- pymud/session.py,sha256=18QIArVTPKEBETv4pTWSXssy12XbM4cFj0IhCyio49U,114503
10
- pymud/settings.py,sha256=p1YPxDAh0isRsoRVGOgklawXYkzUE490irNiizWylB4,7092
11
- pymud-0.19.4.dist-info/LICENSE.txt,sha256=IwGE9guuL-ryRPEKi6wFPI_zOhg7zDZbTYuHbSt_SAk,35823
12
- pymud-0.19.4.dist-info/METADATA,sha256=ApUZTZZRwQvR0vXUAd1gMTlcfLjT3NWYz2mKtmuHJmo,67839
13
- pymud-0.19.4.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
14
- pymud-0.19.4.dist-info/entry_points.txt,sha256=diPUOtTkhgC1hVny7Cdg4aRhaHSynMQoraE7ZhJxUcw,37
15
- pymud-0.19.4.dist-info/top_level.txt,sha256=8Gp1eXjxixXjqhhti6tLCspV_8s9sNV3z5Em2_KRhD4,6
16
- pymud-0.19.4.dist-info/RECORD,,