pymud 0.20.4__py3-none-any.whl → 0.21.0__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 +7 -2
- pymud/decorators.py +234 -0
- pymud/dialogs.py +37 -31
- pymud/extras.py +7 -135
- pymud/i18n.py +63 -0
- pymud/lang/i18n_chs.py +227 -0
- pymud/lang/i18n_eng.py +851 -0
- pymud/logger.py +9 -4
- pymud/main.py +123 -44
- pymud/modules.py +104 -31
- pymud/objects.py +102 -98
- pymud/pkuxkx.py +267 -142
- pymud/protocol.py +15 -13
- pymud/pymud.py +80 -75
- pymud/session.py +582 -356
- pymud/settings.py +15 -2
- {pymud-0.20.4.dist-info → pymud-0.21.0.dist-info}/METADATA +118 -5
- pymud-0.21.0.dist-info/RECORD +23 -0
- {pymud-0.20.4.dist-info → pymud-0.21.0.dist-info}/WHEEL +1 -1
- pymud-0.20.4.dist-info/RECORD +0 -19
- {pymud-0.20.4.dist-info → pymud-0.21.0.dist-info}/entry_points.txt +0 -0
- {pymud-0.20.4.dist-info → pymud-0.21.0.dist-info}/licenses/LICENSE.txt +0 -0
- {pymud-0.20.4.dist-info → pymud-0.21.0.dist-info}/top_level.txt +0 -0
pymud/pkuxkx.py
CHANGED
@@ -1,156 +1,281 @@
|
|
1
1
|
# 示例脚本:如何在PyMud中玩PKUXKX
|
2
2
|
|
3
3
|
import webbrowser
|
4
|
-
from pymud import Alias, Trigger,
|
4
|
+
from pymud import Session, IConfig, alias, trigger, timer, gmcp, Alias, Trigger, Timer, SimpleTrigger, SimpleAlias
|
5
5
|
|
6
6
|
# 在PyMud中,使用#load {filename}可以加载对应的配置作为脚本文件以提供支撑。支持多脚本加载
|
7
|
-
# 本示例脚本对PyMud支持的变量(Variable)、触发器(Trigger,包含单行与多行触发)、别名(Alias)、定时器(Timer)
|
8
|
-
# 使用#load {filename}
|
7
|
+
# 本示例脚本对PyMud支持的变量(Variable)、触发器(Trigger,包含单行与多行触发)、别名(Alias)、定时器(Timer)进行了代码示例
|
8
|
+
# 使用#load {filename}加载的配置文件中,若有一个类型继承自IConfig,则在#load操作时,会自动创建此类型;若没有继承自IConfig的类,则仅将文件引入
|
9
9
|
# 例如,加载本文件指定的配置,则使用 #load pymud.pkuxkx即可
|
10
10
|
|
11
|
-
#
|
12
|
-
#
|
13
|
-
|
11
|
+
# 定义一个自定义配置类,并继承自IConfig。
|
12
|
+
# 目前不在推荐使用Configuration类,而是使用IConfig接口。因为只有使用IConfig接口,才能在类型函数中自动管理由装饰器创建的对象
|
13
|
+
class MyConfig(IConfig):
|
14
|
+
# 类的构造函数,传递参数session,是会话本身。另外请保留*args和**kwargs,以便后续扩展
|
15
|
+
def __init__(self, session: Session, *args, **kwargs) -> None:
|
16
|
+
# 建议将 super().__init__()放在类型init的首句代码,该代码用于对装饰器@alias等函数所装饰的对象进行管理
|
17
|
+
# 调用super().__init__()时,会自动将session传递给父类,以便后续使用。
|
18
|
+
# 因此此处无需再使用self.session = session来保存传递的会话类型
|
19
|
+
#
|
20
|
+
super().__init__(session, *args, **kwargs)
|
14
21
|
|
22
|
+
# 所有自行构建的对象, 建议统一放到self._objs中,方便管理和卸载。
|
23
|
+
# 目前加载卸载可以支持字典、列表、单个对象均可。此处使用字典,是为了方便后续处理其中某个单个对象。
|
24
|
+
# 对象创建时将自动增加到会话中,不需要手动调用session.addObject操作了
|
25
|
+
self._objs = {
|
26
|
+
# 别名,触发器可以通过创建一个对应类型的实例来生成
|
27
|
+
"tri_gem" : SimpleTrigger(self.session ,r'^[> ]*从.+身上.+[◎☆★].+', "pack gem", group = "sys"),
|
28
|
+
"ali_yz_xm" : SimpleAlias(self.session ,'^yz_xm$', "w;#wa 100;w;#wa 100;w;#wa 100;w", group = "sys")
|
29
|
+
}
|
15
30
|
|
16
|
-
|
31
|
+
# 将自定义的状态窗口函数赋值给会话的status_maker属性,这样会话就会使用该函数来显示状态信息。
|
32
|
+
self.session.status_maker = self.status_window
|
17
33
|
|
18
|
-
# hpbrief long情况下的含义
|
19
|
-
HP_KEYS = (
|
20
|
-
"combat_exp", "potential", "max_neili", "neili", "max_jingli", "jingli",
|
21
|
-
"max_qi", "eff_qi", "qi", "max_jing", "eff_jing", "jing",
|
22
|
-
"vigour/qi", "vigour/yuan", "food", "water", "fighting", "busy"
|
23
|
-
)
|
24
|
-
|
25
|
-
# 类的构造函数,传递参数session,是会话本身
|
26
|
-
def __init__(self, session) -> None:
|
27
|
-
self.session = session
|
28
|
-
self._triggers = {}
|
29
|
-
self._commands = {}
|
30
|
-
self._aliases = {}
|
31
|
-
self._timers = {}
|
32
|
-
|
33
|
-
self._initTriggers()
|
34
|
-
self._initCommands()
|
35
|
-
self._initAliases()
|
36
|
-
self._initTimers()
|
37
|
-
|
38
|
-
def _initTriggers(self):
|
39
|
-
'''
|
40
|
-
初始化触发器。
|
41
|
-
本示例中创建了2个触发器,分别对应fullme的链接触发自动打开浏览器访问该网址,以及hpbrief命令的触发器
|
42
|
-
'''
|
43
|
-
# Trigger的构造函数中,给定的位置参数仅有session(会话)和pattern(匹配模式)两个,其他所有参数都是使用命名参数进行实现
|
44
|
-
# 支持的命名参数详细可参见 BaseObject与MatchObject 的构造方法,此处简单列举
|
45
|
-
# id : 唯一标识,不指定时自动生成
|
46
|
-
# group : 组名,不指定时为空
|
47
|
-
# enabled : 使能状态,默认True
|
48
|
-
# priority : 优先级,默认100,越小越高
|
49
|
-
# oneShot : 单次匹配,默认False
|
50
|
-
# ignoreCase : 忽略大小写,默认False
|
51
|
-
# isRegExp : 正则表达式模式,默认True
|
52
|
-
# keepEval : 持续匹配,默认False
|
53
|
-
# raw : 原始匹配方式,默认False。原始匹配方式下,不对VT100下的ANSI颜色进行解码,因此可以匹配颜色;正常匹配仅匹配文本
|
54
34
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
#
|
60
|
-
#
|
61
|
-
self.
|
62
|
-
|
63
|
-
#
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
def _initCommands(self):
|
70
|
-
'''初始化命令,本示例中创建了1个命令,支持hpbrief命令'''
|
71
|
-
|
72
|
-
# Command是异步执行的命令,可以理解为Alias+Trigger+Timer的组合。在MUD中发出一条命令后,会有成功、失败、超时的不同状态,在这三种状态下,会分别调用onSuccess、onFailure、onTimeout方法
|
73
|
-
# 举个Command的应用例子说明:加入把移动(s/e/n/w等等)实现为一个Command。
|
74
|
-
# 1. 当向某个方向移动时,成功的时候会移动到下一个房间;
|
75
|
-
# 2. 不成功的时候,会出现“这个方向没有出路”等描述;
|
76
|
-
# 3. 而当角色处于晕倒状态时,移动命令是不会有任何反应的,超出设定超时时间之后,会调用onTimeout
|
77
|
-
# 在上述实现情况下,我们在执行命令时,可以明确的根据命令的执行结果再判断下一步该做什么
|
78
|
-
# 本示例使用了简单命令SimpleCommand,其在MatchObject的基础上,增加了以下参数:
|
79
|
-
# 1. succ_tri: 命令执行成功时的触发器,不能为空
|
80
|
-
# 2. fail_tri: 命令执行失败时的触发器,可以为空
|
81
|
-
# 3. retry_tri: 需要重新尝试命令的触发器,可以为空(仍以移动为例,当向某个方向移动,出现“你现在正忙着呢”之后,可以在等待2s之后,再次尝试该命令,知道到达最大尝试次数
|
82
|
-
|
83
|
-
# 命令可以同步调用,也可以在异步函数(async)中使用await语法异步调用
|
84
|
-
# 例如,下面的hpbrief可以在这样使用:
|
85
|
-
# self.session.exec_command("hpbrief")
|
86
|
-
# self.session.exec_command_after(2, "hpbrief")
|
87
|
-
# await self.cmd_hpbrief.execute("hpbrief")
|
88
|
-
|
89
|
-
# 异步实现意味着,在函数实现过程中可以以循环实现,而不是以回调实现,有利于代码的可读性
|
90
|
-
# 假设已经实现了一个 cmd_move 的 Command,现在要从ct 执行"s;s;w"行走指令到达春来茶馆,然后根据当前的hpbrief结果判断是否需要drink,然后走回中央广场,可以在函数中这样实现:
|
91
|
-
# async def gotodrink(self):
|
92
|
-
# for step in "s;s;w".split(";"):
|
93
|
-
# await self.cmd_move.execute(step)
|
94
|
-
# await self.cmd_hpbrief.execute("hpbrief")
|
95
|
-
# await asyncio.sleep(1)
|
96
|
-
# water = self.session.getVariable("water")
|
97
|
-
# if int(water) < 300:
|
98
|
-
# self.session.writeline("drink")
|
99
|
-
# await asyncio.sleep(1)
|
100
|
-
# for step in "e;n;n".split(";"):
|
101
|
-
# await self.cmd_move.execute(step)
|
102
|
-
|
103
|
-
|
104
|
-
self._commands['cmd_hpbrief'] = self.cmd_hpbrief = SimpleCommand(self.session, id = "cmd_hpbrief", patterns = "^hpbrief$", succ_tri = self.tri_hp, group = "status", onSuccess = self.oncmd_hpbrief)
|
105
|
-
self.session.addCommands(self._commands)
|
106
|
-
|
107
|
-
def _initAliases(self):
|
108
|
-
'''初始化别名,本示例中创建了1个别名,是get xxx from corpse'''
|
109
|
-
|
110
|
-
# get xxx from corpse的别名操作,匹配成功后会自动调用getfromcorpse函数
|
111
|
-
# 例如, gp silver 相当于 get silver from corpse
|
112
|
-
self._aliases['ali_get'] = Alias(self.session, r"^gp\s(.+)$", id = "ali_get", onSuccess = self.getfromcorpse)
|
113
|
-
|
114
|
-
# 3. 现在支持简单Alias了,在其中也可以支持#wait(缩写为#wa操作)等待,当然,Trigger也支持
|
115
|
-
# 从扬州中心广场到西门的行走,每步中间插入100ms等待
|
116
|
-
self._aliases["ali_yz_xm"] = SimpleAlias(self.session ,'^yz_xm$', "w;#wa 100;w;#wa 100;w;#wa 100;w", group = "sys")
|
117
|
-
|
118
|
-
self.session.addAliases(self._aliases)
|
119
|
-
|
120
|
-
def _initTimers(self):
|
121
|
-
'''初始化定时器,本示例中创建了1个定时器,每隔2秒打印信息'''
|
122
|
-
|
123
|
-
self._timers["tm_test"] = self.tm_test = Timer(self.session, timeout = 2, id = "tm_test", onSuccess = self.onTimer)
|
124
|
-
self.session.addTimers(self._timers)
|
125
|
-
|
126
|
-
def getfromcorpse(self, name, line, wildcards):
|
35
|
+
# 如果仅使用了装饰器定义的PyMUD对象(Alias,Trigger等),则无需实现__unload__方法。
|
36
|
+
# 但如果自定义了PyMUD对象,那么必须实现__unload__方法,否则会导致加载的对象无法被正常卸载。
|
37
|
+
# 如果实现了__unload__方法,那么在该方法中必须调用super().__unload__(),否则会导致@alias等函数装饰器生成的对象不能被正常卸载
|
38
|
+
def __unload__(self):
|
39
|
+
# 在__unload__方法中定义卸载时需要从会话中清除的对象。
|
40
|
+
# 目前加载卸载可以支持字典、列表、单个对象均可。
|
41
|
+
self.session.delObjects(self._objs)
|
42
|
+
|
43
|
+
# 不要遗漏 super().__unload__(),否则会导致@alias等函数装饰器生成的对象不能被正常卸载
|
44
|
+
super().__unload__()
|
45
|
+
|
46
|
+
# 别名, gp gold = get gold from corpse
|
47
|
+
@alias(r"^gp\s(.+)$", id = "ali_get", group = "sys")
|
48
|
+
def getfromcorpse(self, id, line, wildcards):
|
127
49
|
cmd = f"get {wildcards[0]} from corpse"
|
128
50
|
self.session.writeline(cmd)
|
129
51
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
52
|
+
# 定时器,每5秒打印一次信息
|
53
|
+
@timer(5)
|
54
|
+
def onTimer(self, id, *args, **kwargs):
|
55
|
+
self.session.info("每5秒都会打印本信息", "定时器测试")
|
56
|
+
|
57
|
+
# 导航触发器示例
|
58
|
+
@trigger('^http://fullme.pkuxkx.net/robot.php.+$', group = "sys")
|
59
|
+
def ontri_webpage(self, id, line, wildcards):
|
60
|
+
webbrowser.open(line)
|
61
|
+
|
62
|
+
# 若多个对象共用同一个处理函数,也可以同时使用多个装饰器实现
|
63
|
+
@trigger(r"^\s+你可以获取(.+)")
|
64
|
+
@trigger(r"^\s+这里位于(.+)和(.+)的.+")
|
65
|
+
def ontri_multideco(self, id, line, wildcards):
|
66
|
+
self.session.info("触发器触发,ID: {0}, 内容: {1}, 匹配项: {2}".format(id, line, wildcards), "测试")
|
67
|
+
|
68
|
+
# 多行触发器示例
|
69
|
+
@trigger([r'^[> ]*#(\d+.?\d*[KM]?),(\d+),(\d+),(\d+),(\d+),(\d+)$', r'^[> ]*#(\d+),(\d+),(\d+),(\d+),(\d+),(\d+)$', r'^[> ]*#(\d+),(\d+),(-?\d+),(-?\d+),(\d+),(\d+)$'], group = "sys")
|
70
|
+
def ontri_hpbrief_3lines(self, id, line, wildcards):
|
71
|
+
# 注意注意,此处捕获的额内容在wildcards里都是str类型,直接用下面这种方式赋值的时候,保存的变量也是str类型,因此这种在status_window直接调用并用于计算时,需要另行处理
|
72
|
+
self.session.setVariables([
|
73
|
+
"combat_exp", "potential", "max_neili", "neili", "max_jingli", "jingli",
|
74
|
+
"max_qi", "eff_qi", "qi", "max_jing", "eff_jing", "jing",
|
75
|
+
"vigour/qi", "vigour/yuan", "food", "water", "fighting", "busy"
|
76
|
+
]
|
77
|
+
, wildcards)
|
78
|
+
# 因为GMCP.Status传递来的是busy和fighting,与hpbrief逻辑相反,因此重新处理下,保证hpbrief和GMCP.Status一致
|
79
|
+
is_busy = not wildcards[-1]
|
80
|
+
is_fighting = not wildcards[-2]
|
81
|
+
self.session.setVariables(['is_busy', 'is_fighting'], [is_busy, is_fighting])
|
82
|
+
|
83
|
+
# gmcp定义式,name的大小写必须与GMCP的大小写一致,否则无法触发
|
84
|
+
@gmcp("GMCP.Status")
|
85
|
+
def ongmcp_status(self, id, line, wildcards):
|
86
|
+
# GMCP.Status in pkuxkx
|
87
|
+
# 自己的Status和敌人的Status均会使用GMCP.Status发送
|
88
|
+
# 区别在于,敌人的Status会带有id属性。但登录首次自己也会发送id属性,但同时有很多属性,因此增加一个实战经验属性判定
|
89
|
+
if isinstance(wildcards, dict): # 正常情况下,GMCP.Status应该是一个dict
|
90
|
+
if ("id" in wildcards.keys()) and (not "combat_exp" in wildcards.keys()):
|
91
|
+
# 说明是敌人的,暂时忽略
|
92
|
+
#self.session.info(f"GMCP.Status 收到非自己信息: {wildcards}")
|
93
|
+
pass
|
94
|
+
|
95
|
+
else:
|
96
|
+
# GMCP.status收到的wildcards是一个json格式转换过来的字典信息,可以直接用于变量赋值
|
97
|
+
# 但json过来的true/false时全小写字符串,此处转换为bool类型使用
|
98
|
+
#self.session.info(f"GMCP.Status 收到个人信息: {wildcards}")
|
99
|
+
for key, value in wildcards.items():
|
100
|
+
if value == "false": value = False
|
101
|
+
elif value == "true": value = True
|
102
|
+
self.session.setVariable(key, value)
|
103
|
+
|
104
|
+
# 如果这些变量显示在状态窗口中,可以调用下面代码强制刷新状态窗口
|
105
|
+
self.session.application.invalidate()
|
106
|
+
|
107
|
+
# 创建自定义的健康条用作分隔符
|
108
|
+
def create_status_bar(self, current, effective, maximum, barlength = 20, barstyle = "—"):
|
109
|
+
from wcwidth import wcswidth
|
110
|
+
barline = list()
|
111
|
+
stylewidth = wcswidth(barstyle)
|
112
|
+
filled_length = int(round(barlength * current / maximum / stylewidth))
|
113
|
+
# 计算有效健康值部分的长度
|
114
|
+
effective_length = int(round(barlength * effective / maximum / stylewidth))
|
115
|
+
|
116
|
+
# 计算剩余部分长度
|
117
|
+
remaining_length = barlength - effective_length
|
118
|
+
|
119
|
+
# 构造健康条
|
120
|
+
barline.append(("fg:lightcyan", barstyle * filled_length))
|
121
|
+
barline.append(("fg:yellow", barstyle * (effective_length - filled_length)))
|
122
|
+
barline.append(("fg:red", barstyle * remaining_length))
|
123
|
+
|
124
|
+
return barline
|
125
|
+
|
126
|
+
# 自定义状态栏窗口
|
127
|
+
def status_window(self):
|
128
|
+
from pymud.settings import Settings
|
129
|
+
try:
|
130
|
+
formatted_list = list()
|
131
|
+
|
132
|
+
# line 0. hp bar
|
133
|
+
jing = self.session.getVariable("jing", 0)
|
134
|
+
effjing = self.session.getVariable("eff_jing", 0)
|
135
|
+
maxjing = self.session.getVariable("max_jing", 0)
|
136
|
+
jingli = self.session.getVariable("jingli", 0)
|
137
|
+
maxjingli = self.session.getVariable("max_jingli", 0)
|
138
|
+
qi = self.session.getVariable("qi", 0)
|
139
|
+
effqi = self.session.getVariable("eff_qi", 0)
|
140
|
+
maxqi = self.session.getVariable("max_qi", 0)
|
141
|
+
neili = self.session.getVariable("neili", 0)
|
142
|
+
maxneili = self.session.getVariable("max_neili", 0)
|
143
|
+
|
144
|
+
barstyle = "━"
|
145
|
+
screenwidth = self.session.application.get_width()
|
146
|
+
barlength = screenwidth // 2 - 1
|
147
|
+
span = screenwidth - 2 * barlength
|
148
|
+
qi_bar = self.create_status_bar(qi, effqi, maxqi, barlength, barstyle)
|
149
|
+
jing_bar = self.create_status_bar(jing, effjing, maxjing, barlength, barstyle)
|
150
|
+
|
151
|
+
formatted_list.extend(qi_bar)
|
152
|
+
formatted_list.append(("", " " * span))
|
153
|
+
formatted_list.extend(jing_bar)
|
154
|
+
formatted_list.append(("", "\n"))
|
155
|
+
|
156
|
+
# line 1. char, menpai, deposit, food, water, exp, pot
|
157
|
+
formatted_list.append((Settings.styles["title"], "【角色】"))
|
158
|
+
formatted_list.append((Settings.styles["value"], "{0}({1})".format(self.session.getVariable('name'), self.session.getVariable('id'))))
|
159
|
+
formatted_list.append(("", " "))
|
160
|
+
|
161
|
+
formatted_list.append((Settings.styles["title"], "【食物】"))
|
162
|
+
|
163
|
+
food = int(self.session.getVariable('food', '0'))
|
164
|
+
max_food = self.session.getVariable('max_food', 350)
|
165
|
+
if food < 100:
|
166
|
+
style = Settings.styles["value.worst"]
|
167
|
+
elif food < 200:
|
168
|
+
style = Settings.styles["value.worse"]
|
169
|
+
elif food < max_food:
|
170
|
+
style = Settings.styles["value"]
|
171
|
+
else:
|
172
|
+
style = Settings.styles["value.better"]
|
173
|
+
|
174
|
+
formatted_list.append((style, "{}".format(food)))
|
175
|
+
formatted_list.append(("", " "))
|
176
|
+
|
177
|
+
formatted_list.append((Settings.styles["title"], "【饮水】"))
|
178
|
+
water = int(self.session.getVariable('water', '0'))
|
179
|
+
max_water = self.session.getVariable('max_water', 350)
|
180
|
+
if water < 100:
|
181
|
+
style = Settings.styles["value.worst"]
|
182
|
+
elif water < 200:
|
183
|
+
style = Settings.styles["value.worse"]
|
184
|
+
elif water < max_water:
|
185
|
+
style = Settings.styles["value"]
|
186
|
+
else:
|
187
|
+
style = Settings.styles["value.better"]
|
188
|
+
formatted_list.append((style, "{}".format(water)))
|
189
|
+
formatted_list.append(("", " "))
|
190
|
+
formatted_list.append((Settings.styles["title"], "【经验】"))
|
191
|
+
formatted_list.append((Settings.styles["value"], "{}".format(self.session.getVariable('combat_exp'))))
|
192
|
+
formatted_list.append(("", " "))
|
193
|
+
formatted_list.append((Settings.styles["title"], "【潜能】"))
|
194
|
+
formatted_list.append((Settings.styles["value"], "{}".format(self.session.getVariable('potential'))))
|
195
|
+
formatted_list.append(("", " "))
|
196
|
+
|
197
|
+
formatted_list.append((Settings.styles["title"], "【门派】"))
|
198
|
+
formatted_list.append((Settings.styles["value"], "{}".format(self.session.getVariable('family/family_name'))))
|
199
|
+
formatted_list.append(("", " "))
|
200
|
+
formatted_list.append((Settings.styles["title"], "【存款】"))
|
201
|
+
formatted_list.append((Settings.styles["value"], "{}".format(self.session.getVariable('deposit'))))
|
202
|
+
formatted_list.append(("", " "))
|
203
|
+
|
204
|
+
# line 2. hp
|
205
|
+
# a new-line
|
206
|
+
formatted_list.append(("", "\n"))
|
207
|
+
|
208
|
+
formatted_list.append((Settings.styles["title"], "【精神】"))
|
209
|
+
if int(effjing) < int(maxjing):
|
210
|
+
style = Settings.styles["value.worst"]
|
211
|
+
elif int(jing) < 0.8 * int(effjing):
|
212
|
+
style = Settings.styles["value.worse"]
|
213
|
+
else:
|
214
|
+
style = Settings.styles["value"]
|
215
|
+
|
216
|
+
if maxjing == 0:
|
217
|
+
pct1 = pct2 = 0
|
218
|
+
else:
|
219
|
+
pct1 = 100.0*float(jing)/float(maxjing)
|
220
|
+
pct2 = 100.0*float(effjing)/float(maxjing)
|
221
|
+
formatted_list.append((style, "{0}[{1:3.0f}%] / {2}[{3:3.0f}%]".format(jing, pct1, effjing, pct2)))
|
222
|
+
|
223
|
+
formatted_list.append(("", " "))
|
224
|
+
|
225
|
+
formatted_list.append((Settings.styles["title"], "【气血】"))
|
226
|
+
if int(effqi) < int(maxqi):
|
227
|
+
style = Settings.styles["value.worst"]
|
228
|
+
elif int(qi) < 0.8 * int(effqi):
|
229
|
+
style = Settings.styles["value.worse"]
|
230
|
+
else:
|
231
|
+
style = Settings.styles["value"]
|
232
|
+
|
233
|
+
if maxqi == 0:
|
234
|
+
pct1 = pct2 = 0
|
235
|
+
else:
|
236
|
+
pct1 = 100.0*float(qi)/float(maxqi)
|
237
|
+
pct2 = 100.0*float(effqi)/float(maxqi)
|
238
|
+
formatted_list.append((style, "{0}[{1:3.0f}%] / {2}[{3:3.0f}%]".format(qi, pct1, effqi, pct2)))
|
239
|
+
formatted_list.append(("", " "))
|
240
|
+
|
241
|
+
# 内力
|
242
|
+
formatted_list.append((Settings.styles["title"], "【内力】"))
|
243
|
+
if int(neili) < 0.6 * int(maxneili):
|
244
|
+
style = Settings.styles["value.worst"]
|
245
|
+
elif int(neili) < 0.8 * int(maxneili):
|
246
|
+
style = Settings.styles["value.worse"]
|
247
|
+
elif int(neili) < 1.2 * int(maxneili):
|
248
|
+
style = Settings.styles["value"]
|
249
|
+
else:
|
250
|
+
style = Settings.styles["value.better"]
|
251
|
+
|
252
|
+
if maxneili == 0:
|
253
|
+
pct = 0
|
254
|
+
else:
|
255
|
+
pct = 100.0*float(neili)/float(maxneili)
|
256
|
+
formatted_list.append((style, "{0} / {1}[{2:3.0f}%]".format(neili, maxneili, pct)))
|
257
|
+
formatted_list.append(("", " "))
|
258
|
+
|
259
|
+
# 精力
|
260
|
+
formatted_list.append((Settings.styles["title"], "【精力】"))
|
261
|
+
if int(jingli) < 0.6 * int(maxjingli):
|
262
|
+
style = Settings.styles["value.worst"]
|
263
|
+
elif int(jingli) < 0.8 * int(maxjingli):
|
264
|
+
style = Settings.styles["value.worse"]
|
265
|
+
elif int(jingli) < 1.2 * int(maxjingli):
|
266
|
+
style = Settings.styles["value"]
|
267
|
+
else:
|
268
|
+
style = Settings.styles["value.better"]
|
269
|
+
|
270
|
+
if maxjingli == 0:
|
271
|
+
pct = 0
|
272
|
+
else:
|
273
|
+
pct = 100.0*float(jingli)/float(maxjingli)
|
274
|
+
|
275
|
+
formatted_list.append((style, "{0} / {1}[{2:3.0f}%]".format(jingli, maxjingli, pct)))
|
276
|
+
formatted_list.append(("", " "))
|
277
|
+
|
278
|
+
return formatted_list
|
279
|
+
|
280
|
+
except Exception as e:
|
281
|
+
return f"{e}"
|
pymud/protocol.py
CHANGED
@@ -146,7 +146,7 @@ class MudClientProtocol(Protocol):
|
|
146
146
|
subfunc = getattr(self, f"handle_{v.lower()}_sb", None) # 子协商处理函数
|
147
147
|
self._iac_subneg_handlers[k] = subfunc
|
148
148
|
|
149
|
-
self.encoding = Settings.server["default_encoding"] # 字节串基本编码
|
149
|
+
self.encoding = kwargs.get("encoding", Settings.server["default_encoding"]) # 字节串基本编码
|
150
150
|
self.encoding_errors = Settings.server["encoding_errors"] # 编码解码错误时的处理
|
151
151
|
self.mnes = Settings.mnes
|
152
152
|
|
@@ -164,7 +164,7 @@ class MudClientProtocol(Protocol):
|
|
164
164
|
def get_extra_info(self, name, default=None):
|
165
165
|
"""获取传输信息或者额外的协议信息."""
|
166
166
|
if self._transport:
|
167
|
-
default = self._transport.
|
167
|
+
default = self._transport.get_extra_info(name, default)
|
168
168
|
return self._extra.get(name, default)
|
169
169
|
|
170
170
|
def connection_made(self, transport: BaseTransport) -> None:
|
@@ -204,8 +204,9 @@ class MudClientProtocol(Protocol):
|
|
204
204
|
self.log.warning(f'由于异常连接已经断开: {self}, {exc}.')
|
205
205
|
self.session.set_exception(exc)
|
206
206
|
|
207
|
-
self._transport
|
208
|
-
|
207
|
+
if self._transport:
|
208
|
+
self._transport.close()
|
209
|
+
self._transport = None
|
209
210
|
#self.session.set_transport(None)
|
210
211
|
|
211
212
|
# 若设置了onConnected回调函数,则调用
|
@@ -324,7 +325,7 @@ class MudClientProtocol(Protocol):
|
|
324
325
|
# public protocol methods
|
325
326
|
def __repr__(self):
|
326
327
|
"%r下的表述"
|
327
|
-
hostport = self.get_extra_info("peername", ["-", "closing"])[:2]
|
328
|
+
hostport = self.get_extra_info("peername", ["-", "closing"])[:2] # type: ignore
|
328
329
|
return "<Peer {0} {1}>".format(*hostport)
|
329
330
|
|
330
331
|
def _iac_default_handler(self, cmd, option):
|
@@ -766,9 +767,9 @@ class MudClientProtocol(Protocol):
|
|
766
767
|
state_machine = "wait_val_in_table"
|
767
768
|
elif byte in (IAC, MSDP_VAR, MSDP_VAL): # 正常数据 value 结束
|
768
769
|
current_val = val_in_text.decode(self.encoding)
|
769
|
-
msdp_data[current_var] = current_val
|
770
|
+
msdp_data[current_var] = current_val # type: ignore
|
770
771
|
state_machine = "wait_end"
|
771
|
-
self.log.debug(f"收到文本形式的MSDP子协商数据: {current_var} = '{current_val}'")
|
772
|
+
self.log.debug(f"收到文本形式的MSDP子协商数据: {current_var} = '{current_val}'") # type: ignore
|
772
773
|
else: # value是正常数据
|
773
774
|
val_in_text.append(byte)
|
774
775
|
elif state_machine == "wait_val_in_array":
|
@@ -776,9 +777,9 @@ class MudClientProtocol(Protocol):
|
|
776
777
|
# 最后一个val 已结束
|
777
778
|
val_in_array.append(val_in_text.decode(self.encoding))
|
778
779
|
val_in_text.clear()
|
779
|
-
msdp_data[current_var] = val_in_array
|
780
|
+
msdp_data[current_var] = val_in_array # type: ignore
|
780
781
|
state_machine = "wait_end"
|
781
|
-
self.log.debug(f"收到数组形式的MSDP子协商数据: {current_var} = '{val_in_array}'")
|
782
|
+
self.log.debug(f"收到数组形式的MSDP子协商数据: {current_var} = '{val_in_array}'") # type: ignore
|
782
783
|
elif byte == MSDP_VAL:
|
783
784
|
if len(val_in_text) > 0: # 一个VAL已完成,保存到array,后面还有val
|
784
785
|
val_in_array.append(val_in_text.decode(self.encoding))
|
@@ -786,15 +787,16 @@ class MudClientProtocol(Protocol):
|
|
786
787
|
else:
|
787
788
|
val_in_text.append(byte)
|
788
789
|
elif state_machine == "wait_val_in_table":
|
790
|
+
state_machine_table = "not_initialized"
|
789
791
|
if byte == MSDP_TABLE_CLOSE:
|
790
792
|
# 最后一组已结束
|
791
|
-
val_in_table[table_var_name.decode
|
792
|
-
msdp_data[current_var] = val_in_table
|
793
|
+
val_in_table[table_var_name.decode(self.encoding)] = table_var_value.decode(self.encoding)
|
794
|
+
msdp_data[current_var] = val_in_table # type: ignore
|
793
795
|
state_machine = "wait_end"
|
794
|
-
self.log.debug(f"收到表格形式的MSDP子协商数据: {current_var} = '{val_in_table}'")
|
796
|
+
self.log.debug(f"收到表格形式的MSDP子协商数据: {current_var} = '{val_in_table}'") # type: ignore
|
795
797
|
elif byte == MSDP_VAR:
|
796
798
|
if len(table_var_name) > 0: # 上一个VAL已完成,保存到table,后面继续为VAR
|
797
|
-
val_in_table[table_var_name.decode
|
799
|
+
val_in_table[table_var_name.decode(self.encoding)] = table_var_value.decode(self.encoding)
|
798
800
|
table_var_name.clear()
|
799
801
|
table_var_value.clear()
|
800
802
|
state_machine_table = "wait_table_var"
|