pymud 0.20.0a4__py3-none-any.whl → 0.20.1__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/session.py CHANGED
@@ -4,10 +4,11 @@ from collections import OrderedDict
4
4
  import logging, queue
5
5
  from logging import FileHandler
6
6
  from logging.handlers import QueueHandler, QueueListener
7
-
7
+ from wcwidth import wcswidth, wcwidth
8
8
  from .logger import Logger
9
- from .extras import SessionBuffer, DotDict, Plugin
9
+ from .extras import SessionBuffer, DotDict
10
10
  from .protocol import MudClientProtocol
11
+ from .modules import ModuleInfo
11
12
  from .objects import BaseObject, Trigger, Alias, Command, Timer, SimpleAlias, SimpleTrigger, SimpleTimer, GMCPTrigger, CodeBlock, CodeLine
12
13
  from .settings import Settings
13
14
 
@@ -99,6 +100,7 @@ class Session:
99
100
  "ig" : "ignore",
100
101
  "t+" : "ignore",
101
102
  "t-" : "ignore",
103
+ "show": "test",
102
104
  }
103
105
 
104
106
  def __init__(self, app, name, host, port, encoding = None, after_connect = None, loop = None, **kwargs):
@@ -256,11 +258,6 @@ class Session:
256
258
  if self.connected:
257
259
  self.write_eof()
258
260
 
259
- # 两次保存,删掉一次
260
- # # 断开时自动保存变量数据
261
- # if Settings.client["var_autosave"]:
262
- # self.handle_save()
263
-
264
261
  def onDisconnected(self, protocol):
265
262
  "当从服务器连接断开时执行的操作。包括保存变量(若设置)、打印断开时间、执行自定义事件(若设置)等。"
266
263
  # 断开时自动保存变量数据
@@ -562,7 +559,7 @@ class Session:
562
559
 
563
560
  半数的数量由 Settings.client['buffer_lines'] 确定,默认为5000行。
564
561
  """
565
- if (self._line_count >= 2 * Settings.client["buffer_lines"]) and self.buffer.document.is_cursor_at_the_end:
562
+ if (Settings.client["buffer_lines"] > 0) and (self._line_count >= 2 * Settings.client["buffer_lines"]) and self.buffer.document.is_cursor_at_the_end:
566
563
  self._line_count = self.buffer.clear_half()
567
564
 
568
565
  def feed_data(self, data) -> None:
@@ -657,24 +654,25 @@ class Session:
657
654
  self.display_line = raw_line
658
655
 
659
656
  if not self._ignore:
660
- all_tris = list(self._triggers.values())
657
+ # 修改实现,形成列表时即排除非使能状态触发器,加快响应速度
658
+ #all_tris = list(self._triggers.values())
659
+ all_tris = [tri for tri in self._triggers.values() if isinstance(tri, Trigger) and tri.enabled]
661
660
  all_tris.sort(key = lambda tri: tri.priority)
662
661
 
663
662
  for tri in all_tris:
664
- if isinstance(tri, Trigger) and tri.enabled:
665
- if tri.raw:
666
- state = tri.match(raw_line, docallback = True)
667
- else:
668
- state = tri.match(tri_line, docallback = True)
663
+ if tri.raw:
664
+ state = tri.match(raw_line, docallback = True)
665
+ else:
666
+ state = tri.match(tri_line, docallback = True)
669
667
 
670
- if state.result == Trigger.SUCCESS:
671
- if tri.oneShot: # 仅执行一次的trigger,匹配成功后,删除该Trigger(从触发器列表中移除)
672
- self._triggers.pop(tri.id)
668
+ if state.result == Trigger.SUCCESS:
669
+ if tri.oneShot: # 仅执行一次的trigger,匹配成功后,删除该Trigger(从触发器列表中移除)
670
+ self._triggers.pop(tri.id)
673
671
 
674
- if not tri.keepEval: # 非持续匹配的trigger,匹配成功后停止检测后续Trigger
675
- break
676
- else:
677
- pass
672
+ if not tri.keepEval: # 非持续匹配的trigger,匹配成功后停止检测后续Trigger
673
+ break
674
+ else:
675
+ pass
678
676
 
679
677
  # 将数据写入缓存添加到此处
680
678
  if len(self.display_line) > 0:
@@ -768,7 +766,7 @@ class Session:
768
766
  lines = line.split(self.seperator)
769
767
  for ln in lines:
770
768
  if Settings.client["echo_input"]:
771
- self.writetobuffer(f"\x1b[32m{ln}\x1b[0m")
769
+ self.writetobuffer(f"\x1b[32m{ln}\x1b[0m", True)
772
770
  else:
773
771
  self.log.log(f"\x1b[32m{ln}\x1b[0m\n")
774
772
 
@@ -777,7 +775,7 @@ class Session:
777
775
 
778
776
  else:
779
777
  if Settings.client["echo_input"]:
780
- self.writetobuffer(f"\x1b[32m{line}\x1b[0m")
778
+ self.writetobuffer(f"\x1b[32m{line}\x1b[0m", True)
781
779
  else:
782
780
  self.log.log(f"\x1b[32m{line}\x1b[0m\n")
783
781
 
@@ -808,7 +806,7 @@ class Session:
808
806
  return await awaitable
809
807
 
810
808
  def exec(self, cmd: str, name = None, *args, **kwargs):
811
- """
809
+ r"""
812
810
  在名称为name的会话中使用exec_command执行MUD命令。当不指定name时,在当前会话中执行。
813
811
 
814
812
  - exec 与 writeline 都会向服务器写入数据。其差异在于,exec执行的内容,会先经过Alias处理和Command处理,实际向远程发送内容与cmd可以不一致。
@@ -1284,6 +1282,8 @@ class Session:
1284
1282
  """
1285
1283
  从会话中移除一个对象,可直接删除 Alias, Trigger, GMCPTrigger, Command, Timer 或它们的子类本身
1286
1284
 
1285
+ ** 注 ** 现在 delObject 和 delObjects 使用结果相同,都可以清除单个对象、对个对象的list, tuple或dict, 可以有效防止代码写错
1286
+
1287
1287
  :param obj: 要删除的多个特定对象组成的元组、列表或者字典,可以为 Alias, Trigger, GMCPTrigger, Command, Timer 或其子类
1288
1288
 
1289
1289
  示例:
@@ -1320,10 +1320,15 @@ class Session:
1320
1320
  elif isinstance(obj, GMCPTrigger):
1321
1321
  self._gmcp.pop(obj.id, None)
1322
1322
 
1323
+ elif isinstance(obj, (list, tuple, dict)):
1324
+ self.delObjects(obj)
1325
+
1323
1326
  def delObjects(self, objs):
1324
1327
  """
1325
1328
  从会话中移除一组对象,可直接删除多个 Alias, Trigger, GMCPTrigger, Command, Timer
1326
1329
 
1330
+ ** 注 ** 现在 delObject 和 delObjects 使用结果相同,都可以清除单个对象、对个对象的list, tuple或dict, 可以有效防止代码写错
1331
+
1327
1332
  :param objs: 要删除的一组对象的元组、列表或者字典(保持兼容性),其中对象可以为 Alias, Trigger, GMCPTrigger, Command, Timer 或它们的子类
1328
1333
 
1329
1334
  示例:
@@ -1355,6 +1360,9 @@ class Session:
1355
1360
  for key, item in objs.items():
1356
1361
  self.delObject(item)
1357
1362
 
1363
+ elif isinstance(objs, BaseObject):
1364
+ self.delObject(objs)
1365
+
1358
1366
  def addAliases(self, alis):
1359
1367
  """
1360
1368
  向会话中增加多个别名
@@ -1956,7 +1964,7 @@ class Session:
1956
1964
  else:
1957
1965
  vars_simple[k] = v
1958
1966
 
1959
- width = self.application.get_width()
1967
+ width = self.application.get_width() - 2 # 保留2个字符,防止 > 导致换行
1960
1968
 
1961
1969
  title = f" VARIABLE LIST IN SESSION {self.name} "
1962
1970
  left = (width - len(title)) // 2
@@ -1964,6 +1972,7 @@ class Session:
1964
1972
  self.writetobuffer("="*left + title + "="*right, newline = True)
1965
1973
 
1966
1974
  # print vars in simple, 每个变量占40格,一行可以多个变量
1975
+ # 这里可以考虑调整一下,默认40, 但如果一个变量值太长,则选择占两个位置
1967
1976
  var_count = len(vars_simple)
1968
1977
  var_per_line = (width - 2) // 40
1969
1978
  lines = math.ceil(var_count / var_per_line)
@@ -1979,14 +1988,18 @@ class Session:
1979
1988
  self.writetobuffer(" " * left_space)
1980
1989
  line_vars = var_keys[start:end]
1981
1990
  for var in line_vars:
1982
- self.writetobuffer("{0:>18} = {1:<19}".format(var, vars_simple[var].__repr__()))
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__()))
1983
1995
 
1984
1996
  self.writetobuffer("", newline = True)
1985
1997
 
1986
1998
  # print vars in complex, 每个变量占1行
1987
- for k, v in vars_complex.items():
1999
+ var_keys = sorted(vars_complex.keys())
2000
+ for key in var_keys:
1988
2001
  self.writetobuffer(" " * left_space)
1989
- self.writetobuffer("{0:>18} = {1}".format(k, v.__repr__()), newline = True)
2002
+ self.writetobuffer("{0:>20} = {1}".format(key, vars_complex[key].__repr__()), newline = True)
1990
2003
 
1991
2004
  self.writetobuffer("="*width, newline = True)
1992
2005
 
@@ -2039,7 +2052,7 @@ class Session:
2039
2052
  else:
2040
2053
  vars_simple[k] = v
2041
2054
 
2042
- width = self.application.get_width()
2055
+ width = self.application.get_width() - 2 # 保留2个字符,防止 > 导致换行
2043
2056
 
2044
2057
  title = f" GLOBAL VARIABLES LIST "
2045
2058
  left = (width - len(title)) // 2
@@ -2062,21 +2075,23 @@ class Session:
2062
2075
  self.writetobuffer(" " * left_space)
2063
2076
  line_vars = var_keys[start:end]
2064
2077
  for var in line_vars:
2065
- self.writetobuffer("{0:>18} = {1:<19}".format(var, vars_simple[var].__repr__()))
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)))
2066
2081
 
2067
2082
  self.writetobuffer("", newline = True)
2068
2083
 
2069
2084
  # print vars in complex, 每个变量占1行
2070
2085
  for k, v in vars_complex.items():
2071
2086
  self.writetobuffer(" " * left_space)
2072
- self.writetobuffer("{0:>18} = {1}".format(k, v.__repr__()), newline = True)
2087
+ self.writetobuffer("{0:>20} = {1}".format(k, v.__repr__()), newline = True)
2073
2088
 
2074
2089
  self.writetobuffer("="*width, newline = True)
2075
2090
 
2076
2091
  elif len(args) == 1:
2077
2092
  var = args[0]
2078
2093
  if var in self.application.globals.keys():
2079
- self.info("{0:>18} = {1:<19}".format(var, self.application.get_globals(var).__repr__()), "全局变量")
2094
+ self.info("{0:>20} = {1:<22}".format(var, self.application.get_globals(var).__repr__()), "全局变量")
2080
2095
  else:
2081
2096
  self.info("全局空间不存在名称为 {} 的变量".format(var), "全局变量")
2082
2097
 
@@ -2151,7 +2166,7 @@ class Session:
2151
2166
  self.info("创建Timer {} 成功: {}".format(ti.id, ti.__repr__()))
2152
2167
 
2153
2168
  def handle_alias(self, code: CodeLine = None, *args, **kwargs):
2154
- """
2169
+ r"""
2155
2170
  嵌入命令 #alias / #ali 的执行函数,操作别名。该命令可以不带参数、带一个参数或者两个参数。
2156
2171
  该函数不应该在代码中直接调用。
2157
2172
 
@@ -2541,6 +2556,7 @@ class Session:
2541
2556
  self._variables.clear()
2542
2557
  self._tasks.clear()
2543
2558
 
2559
+
2544
2560
  def load_module(self, module_names):
2545
2561
  """
2546
2562
  模块加载函数。
@@ -2564,33 +2580,12 @@ class Session:
2564
2580
  "加载指定名称模块"
2565
2581
  try:
2566
2582
  if module_name not in self._modules.keys():
2567
- mod = importlib.import_module(module_name)
2568
- if hasattr(mod, 'Configuration'):
2569
- config = mod.Configuration(self)
2570
- self._modules[module_name] = {"module": mod, "config": config}
2571
- self.info(f"主配置模块 {module_name} 加载完成.")
2572
- else:
2573
- self._modules[module_name] = {"module": mod, "config": None}
2574
- self.info(f"子配置模块 {module_name} 加载完成.")
2583
+ self._modules[module_name] = ModuleInfo(module_name, self)
2575
2584
 
2576
2585
  else:
2577
- mod = self._modules[module_name]["module"]
2578
- config = self._modules[module_name]["config"]
2579
- if config:
2580
- if hasattr(config, "__unload__") or hasattr(config, "unload"):
2581
- unload = getattr(config, "__unload__", None) or getattr(config, "unload", None)
2582
- if callable(unload):
2583
- unload()
2584
-
2585
- del config
2586
- mod = importlib.reload(mod)
2587
- if hasattr(mod, 'Configuration'):
2588
- config = mod.Configuration(self)
2589
- self._modules[module_name] = {"module": mod, "config": config}
2590
- self.info(f"主配置模块 {module_name} 重新加载完成.")
2591
- else:
2592
- self._modules[module_name] = {"module": mod, "config": None}
2593
- self.info(f"子配置模块 {module_name} 重新加载完成.")
2586
+ mod = self._modules[module_name]
2587
+ if isinstance(mod, ModuleInfo):
2588
+ mod.reload()
2594
2589
 
2595
2590
  except Exception as e:
2596
2591
  import traceback
@@ -2618,18 +2613,9 @@ class Session:
2618
2613
  def _unload_module(self, module_name):
2619
2614
  "卸载指定名称模块。卸载支持需要模块的Configuration实现 __unload__ 或 unload 方法"
2620
2615
  if module_name in self._modules.keys():
2621
- mod = self._modules[module_name]["module"]
2622
- config = self._modules[module_name]["config"]
2623
- if config:
2624
- if hasattr(config, "__unload__") or hasattr(config, "unload"):
2625
- unload = getattr(config, "__unload__", None) or getattr(config, "unload", None)
2626
- if callable(unload):
2627
- unload()
2628
-
2629
- del config
2630
- del mod
2631
- self._modules.pop(module_name)
2632
- self.info(f"配置模块 {module_name} 已成功卸载.")
2616
+ mod = self._modules.pop(module_name)
2617
+ if isinstance(mod, ModuleInfo):
2618
+ mod.unload()
2633
2619
 
2634
2620
  else:
2635
2621
  self.warning(f"指定模块名称 {module_name} 并未加载.")
@@ -2643,9 +2629,9 @@ class Session:
2643
2629
  :param module_names: 要重新加载的模块清单。为元组/列表时,卸载指定名称的系列模块,当名称为字符串时,卸载单个模块。当不指定时,重新加载所有已加载模块。
2644
2630
  """
2645
2631
  if module_names is None:
2646
- self.clean()
2647
- mods = list(self._modules.keys())
2648
- self.load_module(mods)
2632
+ for name, module in self._modules.items():
2633
+ if isinstance(module, ModuleInfo):
2634
+ module.reload()
2649
2635
 
2650
2636
  self.info(f"所有配置模块全部重新加载完成.")
2651
2637
 
@@ -2653,14 +2639,17 @@ class Session:
2653
2639
  for mod in module_names:
2654
2640
  mod = mod.strip()
2655
2641
  if mod in self._modules.keys():
2656
- self.load_module(mod)
2642
+ module = self._modules[mod]
2643
+ if isinstance(module, ModuleInfo):
2644
+ module.reload()
2657
2645
  else:
2658
2646
  self.warning(f"指定模块名称 {mod} 并未加载,无法重新加载.")
2659
2647
 
2660
2648
  elif isinstance(module_names, str):
2661
2649
  if module_names in self._modules.keys():
2662
- mod = module_names.strip()
2663
- self.load_module(mod)
2650
+ module = self._modules[module_names]
2651
+ if isinstance(module, ModuleInfo):
2652
+ module.reload()
2664
2653
  else:
2665
2654
  self.warning(f"指定模块名称 {module_names} 并未加载,无法重新加载.")
2666
2655
 
@@ -2744,7 +2733,7 @@ class Session:
2744
2733
 
2745
2734
  elif mod in self.plugins.keys():
2746
2735
  self.application.reload_plugin(self.plugins[mod])
2747
-
2736
+ self.info(f'插件 {mod} 重新加载完成!')
2748
2737
  else:
2749
2738
  self.warning(f"指定名称 {mod} 既未找到模块,也未找到插件,重新加载失败..")
2750
2739
 
@@ -2798,12 +2787,29 @@ class Session:
2798
2787
  - #reload
2799
2788
  '''
2800
2789
 
2801
- count = len(self._modules.keys())
2802
- if count == 0:
2803
- self.info("当前会话并未加载任何模块。", "MODULES")
2804
- else:
2805
- self.info(f"当前会话已加载 {count} 个模块,包括(按加载顺序排列):{list(self._modules.keys())}", "MODULES")
2790
+ args = code.code[2:]
2791
+
2792
+ if len(args) == 0:
2793
+ count = len(self._modules.keys())
2794
+ if count == 0:
2795
+ self.info("当前会话并未加载任何模块。", "MODULES")
2796
+ else:
2797
+ self.info(f"当前会话已加载 {count} 个模块,包括(按加载顺序排列):{list(self._modules.keys())}", "MODULES")
2806
2798
 
2799
+ elif len(args) >= 1:
2800
+ modules = ",".join(args).split(",")
2801
+ for mod in modules:
2802
+ if mod in self._modules.keys():
2803
+ module = self._modules[mod]
2804
+ if isinstance(module, ModuleInfo):
2805
+ if module.ismainmodule:
2806
+ self.info(f"模块 {module.name} 中包含的配置包括: {', '.join(module.config.keys())}")
2807
+ else:
2808
+ self.info(f"模块 {module.name} 为子模块,不包含配置。")
2809
+
2810
+ else:
2811
+ self.info(f"本会话中不存在指定名称 {mod} 的模块,可能是尚未加载到本会话中")
2812
+
2807
2813
  def handle_reset(self, code: CodeLine = None, *args, **kwargs):
2808
2814
  '''
2809
2815
  嵌入命令 #reset 的执行函数,复位全部脚本。该命令不带参数。
@@ -2823,7 +2829,8 @@ class Session:
2823
2829
 
2824
2830
  def handle_save(self, code: CodeLine = None, *args, **kwargs):
2825
2831
  '''
2826
- 嵌入命令 #save 的执行函数,保存当前会话变量(系统变量除外)至文件。该命令不带参数。
2832
+ 嵌入命令 #save 的执行函数,保存当前会话变量(系统变量和临时变量除外)至文件。该命令不带参数。
2833
+ 系统变量包括 %line, %copy 和 %raw 三个,临时变量是指变量名已下划线开头的变量
2827
2834
  该函数不应该在代码中直接调用。
2828
2835
 
2829
2836
  使用:
@@ -2844,10 +2851,10 @@ class Session:
2844
2851
  with open(file, "wb") as fp:
2845
2852
  saved = dict()
2846
2853
  saved.update(self._variables)
2847
- # keys = list(saved.keys())
2848
- # for key in keys:
2849
- # if key.startswith("%"):
2850
- # saved.pop(key)
2854
+ keys = list(saved.keys())
2855
+ for key in keys:
2856
+ if key.startswith("_"):
2857
+ saved.pop(key)
2851
2858
  saved.pop("%line", None)
2852
2859
  saved.pop("%raw", None)
2853
2860
  saved.pop("%copy", None)
@@ -2867,19 +2874,26 @@ class Session:
2867
2874
 
2868
2875
  def handle_test(self, code: CodeLine = None, *args, **kwargs):
2869
2876
  '''
2870
- 嵌入命令 #test 的执行函数,触发器测试命令。类似于zmud的#show命令。
2877
+ 嵌入命令 #test / #show 的执行函数,触发器测试命令。类似于zmud的#show命令。
2871
2878
  该函数不应该在代码中直接调用。
2872
2879
 
2873
2880
  使用:
2874
- - #test {some_text}: 测试服务器收到{some_text}时的触发器响应情况
2881
+ - #test {some_text}: 测试服务器收到{some_text}时的触发器响应情况。此时,触发器不会真的响应。
2882
+ - #tt {some_test}: 与#test 的差异是,若存在匹配的触发器,无论其是否被使能,该触发器均会实际响应。
2875
2883
 
2876
2884
  示例:
2877
- - ``#test 你深深吸了口气,站了起来。`` : 模拟服务器收到“你深深吸了口气,站了起来。”时的情况进行触发测试
2885
+ - ``#test 你深深吸了口气,站了起来。`` : 模拟服务器收到“你深深吸了口气,站了起来。”时的情况进行触发测试(仅显示触发测试情况)
2878
2886
  - ``#test %copy``: 复制一句话,模拟服务器再次收到复制的这句内容时的情况进行触发器测试
2887
+ - ``#test 你深深吸了口气,站了起来。`` : 模拟服务器收到“你深深吸了口气,站了起来。”时的情况进行触发测试(会实际导致触发器动作)
2879
2888
 
2880
2889
  注意:
2881
- - #test命令测试触发器时,enabled为False的触发器不会响应。
2890
+ - #test命令测试触发器时,触发器不会真的响应。
2891
+ - #tt命令测试触发器时,触发器无论是否使能,均会真的响应。
2882
2892
  '''
2893
+ cmd = code.code[1].lower()
2894
+ docallback = False
2895
+ if cmd == "test":
2896
+ docallback = True
2883
2897
 
2884
2898
  new_cmd_text, new_code = code.expand(self, *args, **kwargs)
2885
2899
  line = new_cmd_text[6:] # 取出#test 之后的所有内容
@@ -2890,32 +2904,82 @@ class Session:
2890
2904
  lines = []
2891
2905
  lines.append(line)
2892
2906
 
2907
+ info_all = []
2908
+ info_enabled = [] # 组织好每一行显示的内容之后,统一输出,不逐行info
2909
+ info_disabled = []
2910
+ triggered = 0
2911
+ triggered_enabled = 0
2912
+ triggered_disabled = 0
2913
+
2914
+
2915
+ tris_enabled = [tri for tri in self._triggers.values() if isinstance(tri, Trigger) and tri.enabled]
2916
+ tris_enabled.sort(key = lambda tri: tri.priority)
2917
+
2918
+ tris_disabled = [tri for tri in self._triggers.values() if isinstance(tri, Trigger) and not tri.enabled]
2919
+ tris_disabled.sort(key = lambda tri: tri.priority)
2920
+
2893
2921
  for raw_line in lines:
2894
- #raw_line = "".join(args)
2895
2922
  tri_line = self.getPlainText(raw_line)
2923
+
2924
+ block = False
2925
+ for tri in tris_enabled:
2926
+ if tri.raw:
2927
+ state = tri.match(raw_line, docallback = docallback)
2928
+ else:
2929
+ state = tri.match(tri_line, docallback = docallback)
2930
+
2931
+ if state.result == Trigger.SUCCESS:
2932
+ triggered_enabled += 1
2933
+ if not block:
2934
+ 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}")
2937
+
2938
+ if not tri.keepEval: # 非持续匹配的trigger,匹配成功后停止检测后续Trigger
2939
+ info_enabled.append(f" {Settings.WARN_STYLE}该触发器未开启keepEval, 会阻止后续触发器。{Settings.CLR_STYLE}")
2940
+ block = True
2941
+ else:
2942
+ info_enabled.append(f" {Settings.WARN_STYLE}{tri.__detailed__()} 可以触发,但由于优先级与keepEval设定,触发器不会触发。{Settings.CLR_STYLE}")
2943
+
2944
+
2945
+ for tri in tris_disabled:
2946
+ if tri.raw:
2947
+ state = tri.match(raw_line, docallback = docallback)
2948
+ else:
2949
+ state = tri.match(tri_line, docallback = docallback)
2896
2950
 
2897
- all_tris = list(self._triggers.values())
2898
- all_tris.sort(key = lambda tri: tri.priority)
2951
+ if state.result == Trigger.SUCCESS:
2952
+ triggered_disabled += 1
2953
+ info_disabled.append(f" {Settings.INFO_STYLE}{tri.__detailed__()} 可以匹配触发。{Settings.CLR_STYLE}")
2899
2954
 
2900
- for tri in all_tris:
2901
- if isinstance(tri, Trigger) and tri.enabled:
2902
- if tri.raw:
2903
- state = tri.match(raw_line, docallback = True)
2904
- else:
2905
- state = tri.match(tri_line, docallback = True)
2955
+ if triggered_enabled + triggered_disabled == 0:
2956
+ info_all.append("")
2906
2957
 
2907
- if state.result == Trigger.SUCCESS:
2908
- self.info(f"TRIGGER {tri.id} 被触发", "PYMUD TRIGGER TEST")
2909
- if tri.oneShot: # 仅执行一次的trigger,匹配成功后,删除该Trigger(从触发器列表中移除)
2910
- self._triggers.pop(tri.id)
2958
+ if triggered_enabled == 0:
2959
+ info_enabled.insert(0, f"使能的触发器中,没有可以触发的。")
2960
+ elif triggered < triggered_enabled:
2961
+ info_enabled.insert(0, f"使能的触发器中,共有 {triggered_enabled} 个可以触发,实际触发 {triggered} 个,另有 {triggered_enabled - triggered} 个由于 keepEval 原因实际不会触发。")
2962
+ else:
2963
+ info_enabled.insert(0, f"使能的触发器中,共有 {triggered_enabled} 个全部可以被正常触发。")
2911
2964
 
2912
- if not tri.keepEval: # 非持续匹配的trigger,匹配成功后停止检测后续Trigger
2913
- break
2914
- else:
2915
- pass
2965
+ if triggered_disabled > 0:
2966
+ info_disabled.insert(0, f"未使能的触发器中,共有 {triggered_disabled} 个可以匹配。")
2967
+ else:
2968
+ info_disabled.insert(0, f"未使能触发器,没有可以匹配的。")
2969
+
2970
+ if triggered_enabled + triggered_disabled == 0:
2971
+ info_all.append(f"PYMUD 触发器测试: {'响应模式' if docallback else '测试模式'}")
2972
+ info_all.append(f" 测试内容: {line}")
2973
+ info_all.append(f" 测试结果: 没有可以匹配的触发器。")
2974
+ else:
2975
+ info_all.append(f"PYMUD 触发器测试: {'响应模式' if docallback else '测试模式'}")
2976
+ info_all.append(f" 测试内容: {line}")
2977
+ info_all.append(f" 测试结果: 有{triggered}个触发器可以被正常触发,一共有{triggered_enabled + triggered_disabled}个满足匹配触发要求。")
2978
+ info_all.extend(info_enabled)
2979
+ info_all.extend(info_disabled)
2916
2980
 
2917
- if len(raw_line) > 0:
2918
- self.info(raw_line, "PYMUD TRIGGER TEST")
2981
+ self.info("\n".join(info_all), "PYMUD 触发器测试")
2982
+ #self.info("PYMUD 触发器测试 完毕")
2919
2983
 
2920
2984
  def handle_plugins(self, code: CodeLine = None, *args, **kwargs):
2921
2985
  '''
@@ -2946,7 +3010,7 @@ class Session:
2946
3010
  if name in self.plugins.keys():
2947
3011
  plugin = self.plugins[name]
2948
3012
  self.info(f"{plugin.desc['DESCRIPTION']}, 版本 {plugin.desc['VERSION']} 作者 {plugin.desc['AUTHOR']} 发布日期 {plugin.desc['RELEASE_DATE']}", f"PLUGIN {name}")
2949
- self.writetobuffer(plugin.help)
3013
+ self.writetobuffer(plugin.help, True)
2950
3014
 
2951
3015
  def handle_replace(self, code: CodeLine = None, *args, **kwargs):
2952
3016
  '''
@@ -3066,7 +3130,8 @@ class Session:
3066
3130
  new_lines.append("{}{}".format(style, line))
3067
3131
 
3068
3132
  msg = Settings.client["newline"].join(new_lines)
3069
-
3133
+
3134
+ # 将颜色跨行显示移动到了MudFormatProcessor中,此处无需再处理(不行,还得恢复)
3070
3135
  self.writetobuffer("{}[{}] {}{}".format(style, title, msg, Settings.CLR_STYLE), newline = True)
3071
3136
 
3072
3137
  def info(self, msg, title = "PYMUD INFO", style = Settings.INFO_STYLE):
@@ -3125,8 +3190,8 @@ class Session:
3125
3190
 
3126
3191
  示例:
3127
3192
  - ``#log`` : 在当前会话窗口列出所有记录器状态
3128
- - ``#log start`` : 启动本会话默认记录器(记录器名为会话名)。该记录器以纯文本模式,将后续所有屏幕输出、键盘键入、命令输入等记录到 name.log 文件的后端
3129
- - ``#log start -r`` : 启动本会话默认记录器。该记录器以raw模式,将后续所有屏幕输出、键盘键入、命令输入等记录到 name.log 文件的后端
3193
+ - ``#log start`` : 启动本会话默认记录器(记录器名为会话名)。该记录器以纯文本模式,将后续所有屏幕输出、键盘键入、命令输入等记录到 log 目录下 name.log 文件的后端
3194
+ - ``#log start -r`` : 启动本会话默认记录器。该记录器以raw模式,将后续所有屏幕输出、键盘键入、命令输入等记录到 log 目录下 name.log 文件的后端
3130
3195
  - ``#log start chat`` : 启动名为 chat 的记录器。该记录器以纯文本模式,记录代码中调用过该记录器 .log 进行记录的信息
3131
3196
  - ``#log stop`` : 停止本会话默认记录器(记录器名为会话名)。
3132
3197
 
@@ -3184,8 +3249,19 @@ class Session:
3184
3249
  elif (args[0] == "show"):
3185
3250
  if len(args) > 1 and not args[1].startswith('-'):
3186
3251
  file = args[1]
3187
- self.application.logFileShown = file
3188
- self.application.showLogInTab()
3252
+ if os.path.exists(file):
3253
+ filepath = os.path.abspath(file)
3254
+ #self.info(f'file {filepath} exists, will be shown.')
3255
+ self.application.logFileShown = filepath
3256
+ self.application.showLogInTab()
3257
+ elif os.path.exists(os.path.join('./log', file)):
3258
+ filepath = os.path.abspath(os.path.join('./log', file))
3259
+ #self.info(f'file {filepath} exists, will be shown.')
3260
+ self.application.logFileShown = filepath
3261
+ self.application.showLogInTab()
3262
+ else:
3263
+ self.warning(f'指定记录文件 {file} 不存在!')
3264
+
3189
3265
  else:
3190
3266
  self.application.show_logSelectDialog()
3191
3267
 
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.0"
14
+ __version__ = "0.20.1"
15
15
  "APP 当前版本"
16
- __release__ = "2024-08-19"
16
+ __release__ = "2024-11-16"
17
17
  "APP 当前版本发布日期"
18
18
  __author__ = "本牛(newstart)@北侠"
19
19
  "APP 作者"