跳转至内容
  • 新版功能测试中

    PyMUD讨论区
    3
    2
    0 赞同
    3 帖子
    39 浏览
    T
    @newstart skills[literate]就是嵌套的字典变量呗?
  • 初步完成状态窗口

    PyMUD讨论区
    1
    1
    1 赞同
    1 帖子
    9 浏览
    T
    后边慢慢优化 [image: 1767879258627-2555386c-a39e-456b-bf6e-695debf3c628-image-resized.png] # 清华西游记状态栏和状态窗口 import webbrowser, asyncio, ast from pymud import Session, IConfig, alias, trigger, timer, gmcp, exception, Trigger, SimpleTrigger, SimpleAlias, GMCPTrigger, Command from wcwidth import wcswidth from utils.utils import experience_in_M class MyConfig(IConfig): def __init__(self, session: Session, *args, **kwargs) -> None: super().__init__(session, *args, **kwargs) # 将自定义的状态窗口函数赋值给会话的status_maker属性,这样会话就会使用该函数来显示状态信息。 self.session.application.set_status('当前任务:未知') # 未来可以作为当前任务的状态栏 self.session.status_maker = self.status_window def __unload__(self): super().__unload__() # 创建自定义的健康条用作分隔符 def create_status_bar(self, current, maximum, actual_max, bar_length = 20, bar_char = "—"): """ 创建一个健康条,用于显示当前健康值、有效健康值和剩余健康值。 :param current: 当前健康值 :param maximum: 当前最大健康值 :param actual_max: 真实最大值 :param bar_length: 健康条的长度 :param bar_char: 健康条的填充字符 :return: 一个包含健康条的列表 """ # 气血: 1153/ 1702 ( 64%) 内力: 10943 / 5659 (100%) (+360) bar_line = list() char_width = wcswidth(bar_char) # 计算有效健康值 filled_length = int(round(bar_length * current / actual_max / char_width)) # 计算有效健康值部分的长度 current_max_length = int(round(bar_length * maximum / actual_max / char_width)) # 计算剩余部分长度 remaining_length = bar_length - current_max_length # 构造健康条 bar_line.append(("fg:lightcyan", bar_char * filled_length)) bar_line.append(("fg:yellow", bar_char * (current_max_length - filled_length))) bar_line.append(("fg:red", bar_char * remaining_length)) return bar_line # 自定义状态栏窗口。该函数会被渲染框架高频调用,请注意不要在该函数中 info 或者执行其他输出信息!!! def status_window(self): styles = { "title" : "bold", "value" : "lightgreen", "value.better" : "lightcyan", "value.worse" : "yellow", "value.worst" : "red" } try: formatted_list = list() # line 0. hp bar kee = int(self.session.getVariable("kee", 0)) max_kee = int(self.session.getVariable("max_kee", 1)) kee_percent = int(self.session.getVariable("kee_percent", 1)) actual_max_kee = int(round(max_kee / (kee_percent / 100))) sen = int(self.session.getVariable("sen", 0)) max_sen = int(self.session.getVariable("max_sen", 1)) sen_percent = int(self.session.getVariable("sen_percent", 1)) actual_max_sen = int(round(max_sen / (sen_percent / 100))) bar_char = "━" screenwidth = self.session.application.get_width() bar_length = screenwidth // 2 - 1 span = screenwidth - 2 * bar_length kee_bar = self.create_status_bar(kee, max_kee, actual_max_kee, bar_length, bar_char) sen_bar = self.create_status_bar(sen, max_sen, actual_max_sen, bar_length, bar_char) formatted_list.extend(kee_bar) formatted_list.append(("", " " * span)) formatted_list.extend(sen_bar) formatted_list.append(("", "\n")) # line 1. char, family, master, deposit, food, water, exp, pot formatted_list.append((styles["title"], "【角色】")) formatted_list.append((styles["value"], "{0}({1})".format(self.session.getVariable('name'), self.session.getVariable('id')))) formatted_list.append(("", " ")) formatted_list.append((styles["title"], "【门派】")) formatted_list.append((styles["value"], "{}".format(self.session.getVariable('family')))) formatted_list.append(("", " ")) formatted_list.append((styles["title"], "【师父】")) formatted_list.append((styles["value"], "{}".format(self.session.getVariable('master')))) formatted_list.append(("", " ")) formatted_list.append((styles["title"], "【武学】")) formatted_list.append((styles["value"], "{}".format(self.session.getVariable('combat_exp')))) formatted_list.append((styles["value"], "({})".format(experience_in_M(self.session.getVariable('combat_exp'))))) formatted_list.append(("", " ")) formatted_list.append((styles["title"], "【道行】")) formatted_list.append((styles["value"], "{}".format(self.session.getVariable('daoxing_exp')))) formatted_list.append((styles["value"], "({})".format(experience_in_M(self.session.getVariable('daoxing_exp'))))) formatted_list.append(("", " ")) formatted_list.append((styles["title"], "【潜能】")) formatted_list.append((styles["value"], "{}".format(self.session.getVariable('potential')))) # line 2. hp # a new-line formatted_list.append(("", "\n")) formatted_list.append((styles["title"], "【气血】")) if max_kee < actual_max_sen: style = styles["value.worst"] elif kee < 0.8 * max_kee: style = styles["value.worse"] else: style = styles["value"] if max_kee == 0: pct1 = pct2 = 0 else: pct1 = 100.0 * kee / actual_max_kee pct2 = 100.0 * max_kee / actual_max_kee formatted_list.append((style, "{0}[{1:3.0f}%] / {2}[{3:3.0f}%]".format(kee, pct1, max_kee, pct2))) formatted_list.append(("", " ")) formatted_list.append((styles["title"], "【精神】")) if max_sen < actual_max_sen: style = styles["value.worst"] elif sen < 0.8 * max_sen: style = styles["value.worse"] else: style = styles["value"] if max_sen == 0: pct1 = pct2 = 0 else: pct1 = 100.0 * sen / actual_max_sen pct2 = 100.0 * max_sen / actual_max_sen formatted_list.append((style, "{0}[{1:3.0f}%] / {2}[{3:3.0f}%]".format(sen, pct1, max_sen, pct2))) formatted_list.append(("", " ")) formatted_list.append((styles["title"], "【食物】")) food = int(self.session.getVariable('food', '0')) max_food = int(self.session.getVariable('max_food', 350)) if food < 100: style = styles["value.worst"] elif food < 200: style = styles["value.worse"] elif food < max_food: style = styles["value"] else: style = styles["value.better"] formatted_list.append((style, "{0}/{1}".format(food, max_food))) formatted_list.append(("", " ")) formatted_list.append((styles["title"], "【饮水】")) water = int(self.session.getVariable('water', '0')) max_water = int(self.session.getVariable('max_water', 350)) if water < 100: style = styles["value.worst"] elif water < 200: style = styles["value.worse"] elif water < max_water: style = styles["value"] else: style = styles["value.better"] formatted_list.append((style, "{0}/{1}".format(water, max_water))) formatted_list.append(("", " ")) formatted_list.append((styles["title"], "【存款】")) formatted_list.append((styles["value"], "{}金".format(self.session.getVariable('deposit')))) formatted_list.append(("", " ")) # line 3. hp # a new-line formatted_list.append(("", "\n")) force = int(self.session.getVariable("force", 0)) max_force = int(self.session.getVariable("max_force", 1)) mana = int(self.session.getVariable("mana", 0)) max_mana = int(self.session.getVariable("max_mana", 1)) # 内力 formatted_list.append((styles["title"], "【内力】")) if force < 0.6 * max_force: style = styles["value.worst"] elif force < 0.8 * max_force: style = styles["value.worse"] elif force < 1.2 * max_force: style = styles["value"] else: style = styles["value.better"] if max_force == 0: pct = 0 else: pct = 100.0 * force / max_force formatted_list.append((style, "{0} / {1}[{2:3.0f}%]".format(force, max_force, pct))) formatted_list.append(("", " ")) # 精力 formatted_list.append((styles["title"], "【法力】")) if mana < 0.6 * max_mana: style = styles["value.worst"] elif mana < 0.8 * max_mana: style = styles["value.worse"] elif mana < 1.2 * max_mana: style = styles["value"] else: style = styles["value.better"] if max_mana == 0: pct = 0 else: pct = 100.0 * mana / max_mana formatted_list.append((style, "{0} / {1}[{2:3.0f}%]".format(mana, max_mana, pct))) formatted_list.append(("", " ")) return formatted_list except Exception as e: return f"{e}"
  • 如何加载不同模块(.py)?

    PyMUD讨论区
    12
    0 赞同
    12 帖子
    69 浏览
    T
    哦,我不上xkx的论坛的~~~ 不过这个帖子好像之前搜索pyMud的时候看过, 但现在又忘记了 这记性真不行了
  • 关于多行Trigger的讨论

    PyMUD讨论区
    5
    0 赞同
    5 帖子
    37 浏览
    N
    很多时候都是这么实现的,比如抓房间描述不确定多少行时,用房间名做首行触发,用出口描述做结束触发,中间再判断是什么。
  • RFC #854 TELNET 协议规范

    个人博客区
    1
    0 赞同
    1 帖子
    11 浏览
    N
    TELNET RFC文档概述 RFC #854 TELNET 协议规范 0. 基本信息 WEB页面:https://www.rfc-editor.org/rfc/rfc854.html 发布日期:1983-05 本RFC为ARPA互联网社区指定了一个标准。ARPA互联网上的主机预计将采用并实施本标准。 1. 引言 TELNET协议的目的是提供一种相当通用的、双向的、面向八位字节的通信设施。它的主要目标是允许一种将终端设备和面向终端的过程相互连接的标准方法。可以设想,该协议也可以用于终端通信(“链接”)和过程过程通信(分布式计算)。 2. 一般考虑 TELNET连接是一种传输控制协议(TCP)连接,用于传输带有穿插TELNET控制信息的数据。 TELNET协议建立在三个主要思想之上:第一,“网络虚拟终端”(Network Virtual Terminal, NVT)的概念;第二,协商选择权原则;第三,终端和过程的对称视图。 当首次建立TELNET连接时,假设每一端都在“网络虚拟终端”(NVT)发起和终止。NVT是一种假想的设备,它提供规范终端的标准、网络范围的中间表示。这就不需要“服务器”和“用户”主机来保存有关彼此终端特性和终端处理约定的信息。所有主机,包括用户和服务器,都映射其本地设备特性和约定,以便看起来是在处理网络上的NVT,并且每个主机都可以由另一方进行类似的映射。NVT旨在在过度限制(没有为主机提供足够丰富的词汇表来映射到其本地字符集)和过度包容(惩罚使用普通终端的用户)之间取得平衡。 注:“用户”主机是物理终端通常连接到的主机,“服务器”主机是通常提供某些服务的主机。作为另一种观点,即使在终端到终端或过程到过程通信中也适用,“用户”主机是发起通信的主机。 协商选项的原则考虑到这样一个事实,即许多主机将希望提供超过NVT内可用服务的额外服务,并且许多用户将拥有复杂的终端,并且希望拥有优雅的服务,而不是最低限度的服务。独立于TELNET协议,但在TELNET协议中结构化的是各种“选项”,这些选项将被批准,并可与“DO,DON’T,WILL,WON’T”结构(如下所述)一起使用,以允许用户和服务器同意为其TELNET连接使用一组更详细(或可能只是不同)的约定。这些选项可能包括更改字符集、回声模式等。 设置选项使用的基本策略是让任何一方(或双方)发起请求,要求某些选项生效。然后,另一方可以接受或拒绝该请求。如果请求被接受,该选项将立即生效;如果被拒绝,则连接的相关方面保持为针对NVT所指定的。显然,一方可能总是拒绝启用某个选项的请求,并且决不能拒绝禁用某个选项,因为所有各方都必须准备好支持NVT。 选项协商的语法已经设置好,如果双方同时请求一个选项,双方都会将对方的请求视为对自己的肯定确认。 协商语法的对称性可能会导致非终止确认循环——各方将传入的命令视为必须确认的新请求,而不是确认。为防止此类循环,以以下规则为准: a. 缔约方只能要求更改选项状态;也就是说,一方不能仅仅为了宣布其处于何种模式而发出“请求”。 b. 如果一方收到似乎是进入其已经处于的某种模式的请求,则不应确认该请求。这种不回应对于防止谈判中无休止的循环至关重要。即使模式没有更改,也需要对更改模式的请求发送响应。 c. 当一方向第二方发送选项命令时,无论是作为请求还是确认,并且该选项的使用将对从第一方发送到第二方的数据的处理产生任何影响,则该命令必须在希望其生效的点插入数据流中。(需要注意的是,从发送请求到收到确认之间会经过一段时间,这可能是否定的。因此,主机可能希望在请求选项后缓冲数据,直到它知道请求是被接受还是被拒绝,以向用户隐藏“不确定期”。) 当TELNET连接首次建立时,选项请求可能会来回奔波,因为各方都试图从另一方获得尽可能好的服务。然而,除此之外,还可以使用选项来动态修改连接的特性,以适应不断变化的本地条件。例如,正如稍后将要解释的那样,NVT使用的传输规程非常适合许多“一次一行”的应用程序,如BASIC,但不太适合许多“每次一个字符”的应用,如NLS。当“一次一个字符”规则适合本地进程并协商合适的选项时,服务器可能会选择投入该规则所需的额外处理器开销。然而,当不再需要详细控制时,它可以切换(即协商)回NVT,而不是永久地承受额外的处理开销。 如果进程仅通过重新请求选项来响应拒绝,则进程发起的请求可能会激发非终止请求循环。为了防止这种循环的发生,被拒绝的请求不应该重复,直到发生变化。在操作上,这可能意味着进程正在运行不同的程序,或者用户已经给出了另一个命令,或者在给定进程和给定选项的上下文中有意义的任何东西。一个很好的经验法则是,重新请求只应作为来自连接另一端的后续信息的结果或当本地人工干预要求时发生。 选项设计者不应感到受到可用于选项谈判的有限语法的约束。简单语法的目的是让选择变得容易——因为相应地,人们很容易声称对它们一无所知。如果某个特定选项需要比“DO,DON’T,WILL,WON’T”更丰富的谈判结构,那么正确的策略是使用“DO,DO’T,WILL,WON”来确定双方都理解该选项,一旦完成,就可以自由使用更奇特的语法。例如,一方可能会发送更改(建立)线路长度的请求。如果它被接受,那么可以使用不同的语法来实际协商线路长度——这样的“子协商”可能包括最小允许、最大允许和所需线路长度的字段。重要的概念是,在之前的一些(标准)谈判已经确定双方都能够解析扩展的语法之前,这种扩展的谈判永远不应该开始。 总之,任何一方发送WILL XXX,以表明该方希望(要约)开始执行选项XXX,DO XXX和DOT XXX是其肯定和否定的确认;类似地,发送DO XXX以指示希望(请求)另一方(即DO的接收方)开始执行选项XXX,WILL XXX和WON’T XXX作为肯定和否定的确认。由于NVT是在没有启用选项的情况下剩下的,因此“DON'T”和“WON'T”响应保证使连接处于两端都可以处理的状态。因此,所有主机都可以在完全不知道不支持的选项的情况下实现其TELNET进程,只需对无法理解的任何选项请求返回拒绝(即拒绝)即可。 TELNET协议已经尽可能地实现了服务器-用户对称,因此它可以轻松自然地涵盖用户-用户(链接)和服务器-服务器(协作进程)的情况。人们希望,但并非绝对需要,各种选择将进一步推动这一意图。无论如何,人们明确承认对称是一种操作原理,而不是一条铁律。 应查阅配套文件“TELNET选项规范”,以获取有关建立新选项的程序的信息。 3. 网络虚拟终端(Network Virtual Terminal, NVT) 网络虚拟终端(NVT)是一种双向字符设备。NVT有一台打印机和一个键盘。打印机响应输入数据,键盘产生输出数据,通过TELNET连接发送,如果需要“回声”,也发送到NVT的打印机。“回声”将不会穿过网络(尽管存在启用“远程”回声操作模式的选项,但不需要主机来实现此选项)。代码集是八位字段中的七位USASCII,此处修改的除外。任何代码转换和时序考虑都是局部问题,不会影响NVT。 3.1 传输数据 尽管通过网络的TELNET连接本质上是全双工的,但NVT应被视为在线路缓冲模式下运行的半双工设备。也就是说,除非协商了相反的选项,否则以下默认条件适用于通过TELNET连接传输数据: 只要本地缓冲区空间的可用性允许,数据应该在生成数据的主机中累积,直到完整的数据线准备好传输,或者直到出现一些本地定义的要传输的显式信号。该信号可以由过程产生,也可以由人类用户产生。 这条规则的动机是,对一些主机来说,处理网络输入中断的成本很高,再加上默认的NVT规范,即“回声”不会穿过网络。因此,在其源处缓冲一定数量的数据是合理的。许多系统在每条输入线的末端进行一些处理操作(即使是行式打印机或打卡器也经常以这种方式工作),因此传输应该在线的末端触发。另一方面,用户或过程有时可能会发现有必要或希望提供不终止于线路末端的数据;因此,提醒实施者提供本地发信号通知所有缓冲数据应立即传输的方法。 当进程已完成向NVT打印机发送数据,并且没有来自NVT键盘的排队输入以供进一步处理时(即,当TELNET连接一端的进程在没有另一端输入的情况下无法继续进行时),该进程必须发送TELNET Go Ahead(GA)命令。 此规则并不要求从每行末尾的终端发送TELNET GA命令,因为服务器主机通常不需要特殊信号(除了行末尾或其他本地定义的字符)来开始处理。相反,TELNET GA旨在帮助用户的本地主机操作物理上的半双工终端,该终端具有“可锁定”键盘,如IBM 2741。对这种类型的终端的描述可能有助于解释GA命令的正确使用。 终端计算机连接始终处于用户或计算机的控制之下。双方都不能单方面从对方手中夺取控制权;相反,控制端必须明确地重新隐藏其控制。在终端,硬件的构造是为了在每次“线路”终止时(即,当用户键入“新行”键时)放弃控制。当这种情况发生时,连接的(本地)计算机处理输入数据,决定是否应该生成输出,如果不应该,则将控制权返回给终端。如果应该生成输出,则计算机将保留控制权,直到所有输出都已传输。 通过网络使用这种类型的终端的困难应该是显而易见的。“本地”计算机在看到线路末端信号后不再能够决定是否保留控制;这个决定只能由处理数据的“远程”计算机做出。因此,TELNET GA命令提供了一种机制,“远程”(服务器)计算机可以向“本地”(用户)计算机发出信号,表示是时候将控制权交给终端用户了。它应该在这些时间发送,并且只有在用户应该被赋予终端控制权时才发送。请注意,GA命令的过早传输可能会导致输出阻塞,因为用户可能会认为传输系统已经暂停,因此他将无法手动扭转线路。 当然,上述内容不适用于用户到服务器的通信方向。在这个方向上,GA可以在任何时候发送,但不需要发送。此外,如果TELNET连接用于进程到进程的通信,则无需向任何方向发送GA。最后,对于终端到终端的通信,可能在任一方向、一个方向或两个方向上都不需要GA。如果主机计划支持终端到终端的通信,则建议主机向用户提供手动发信号通知通过TELNET连接发送GA的时间到了的方式;然而,这并不是TELNET流程实现者的要求。 请注意,TELNET模型的对称性要求TELNET连接的每一端都有一个NVT,至少在概念上是这样。 3.2 控制功能的标准表示 如本文件导言所述,TELNET协议的主要目标是通过网络提供终端设备和面向终端的进程的标准接口。这种类型的互连的早期经验表明,某些功能是由大多数服务器实现的,但调用这些功能的方法差异很大。对于与多个服务器系统交互的人类用户来说,这些差异非常令人沮丧。因此,TELNET为其中五个函数定义了一个标准表示,如下所述。这些标准表示具有标准但非必需的含义(使用TELNET的其他协议可能需要中断处理(IP)功能除外);也就是说,不向本地用户提供功能的系统不需要向网络用户提供功能,并且可以将功能的标准表示视为“否”操作。另一方面,向本地用户提供功能的系统必须向发送该功能的标准表示的网络用户提供相同的功能。 3.2.1 中断过程 Interrupt Process (IP) 许多系统提供暂停、中断、中止或终止用户进程操作的功能。当用户认为自己的进程处于无休止的循环中,或者无意中激活了不需要的进程时,经常使用此功能。IP是调用此函数的标准表示形式。实施人员应注意,使用TELNET的其他协议可能需要IP,因此,如果要支持这些其他协议,则应实施IP。 3.2.2 中止输出 Abort Output (AO) 许多系统提供了一种功能,该功能允许正在生成输出的进程运行到完成(或达到与运行到完成时相同的停止点),但不将输出发送到用户的终端。此外,该功能通常清除已经产生但尚未实际打印(或显示)在用户终端上的任何输出。AO是调用此函数的标准表示。例如,一些子系统通常可以接受用户的命令,向用户的终端发送一个长文本字符串作为响应,最后通过向用户的端子发送一个“提示”字符(前面有<CR><LF>)来表示准备接受下一个命令。如果在传输文本字符串的过程中接收到AO,则合理的实现方式是抑制文本字符串的剩余部分,但传输提示字符和前面的<CR><LF>。(这可能与接收到IP时可能采取的操作不同;IP可能会导致文本字符串被抑制并退出子系统。) 应该注意的是,通过提供该功能的服务器系统,可能存在系统外部的缓冲区(在网络和用户的本地主机中),这些缓冲区应该被清除;实现这一点的适当方法是向用户系统发送“Synch”信号(如下所述)。 3.3.3 你在吗 Are You There (AYT) 许多系统提供一种功能,该功能向用户提供一些可见(例如,可打印)的证据,表明系统仍在运行。当系统出乎意料地长时间“静默”时,用户可能会调用此函数,因为计算的长度出乎意料(用户),系统负载异常重等。AYT是调用此函数的标准表示。 3.3.4 删除字符 Erase Character (EC) 许多系统提供了一种功能,可以从用户提供的数据流中删除最后一个在前未删除的字符或“打印位置”*。此功能通常用于在出现键入错误时编辑键盘输入。EC是调用此函数的标准表示。 注意:“打印位置”可能包含几个字符,这些字符是重击的结果,或是<char1>BS<char2>等序列的结果。。。 3.3.5 删除行 Erase Line (EL) 许多系统提供删除当前输入“行”中所有数据的功能。此功能通常用于编辑键盘输入。EL是调用此函数的标准表示。
  • PyMUD运行的好伙伴 - uv包管理器

    个人博客区
    1
    0 赞同
    1 帖子
    16 浏览
    N
    因为PyMUD开发需要经常在多个Python版本下测试是否有效,以前使用传统的pyenv来创建虚拟环境解决问题,后来发现操作起来实在太过于繁琐,因此找到了一款新的基于命令行的包管理工具, uv,完整替代了我所有的需求,包括不同Python版本的管理、虚拟环境、依赖管理等等,而且 uv 是通过 rust 开发,支持并发操作,碰到新环境搭建需要 pip 拉取一堆依赖项时,速度极快,已经是我正常开发和运行过程中不可缺少的工具了,现在推荐给大家。 相关链接 代码 https://github.com/astral-sh/uv 文档 https://docs.astral.sh/uv/ 官宣的我觉得很有用的特点 用一个工具替代 pip,pip-tools,pipx,poetry,pyenv,twine,virtualenv 等等 ️ 比标准pip快10~100倍。 可用于安装和管理 Python 版本。 ️ 管理pypi发布的 Python 包。 可以通过curl或者pip安装,而无需安装 Rust 或 Python。 ️ 跨平台,支持 macOS、Linux 和 Windows。 详细信息可参见官方文档
  • 最近发现一个奇怪的BUG

    PyMUD讨论区 bug报告
    1
    0 赞同
    1 帖子
    12 浏览
    N
    不知道为什么,在某些时候,PyMUD启动时偶尔会出现无法识别类型的情况,报错为类似 "Trigger对象不包含session属性" 这种,似乎是不同地方import的Session类型不一致,比如pymud.Session和pymud.session.Session不能被识别为一个类型,因此导致 isinstance(session, Session)判断失败,再进一步就是所有脚本加载均会被认为是从脚本。最终处理都是通过重新处理venv环境、重新指定python版本来解决的,更进一步的原因还没有查到。 不知道各位用户有没有碰到过类似的问题?
  • PyMUD 0.22.2a2预览版发布

    已固定 公告与通知 信息发布 新版发布 预览版
    1
    0 赞同
    1 帖子
    22 浏览
    N
    PyMUD 0.22.2a2预览版已推送到 pypi.org, 感兴趣的可以尝鲜。相对于 0.22.2a1 版和 0.22.1 版主要修改包括: 功能新增: 可以通过.cfg文件指定 "auto_chars" 来定义启动pymud时自动打开的会话。(0.22.2a1新增) 问题修复: 修复GMCPTrigger类型enabled属性不生效问题。 (0.22.2a1新增) 问题修复: 修复添加/删除系统时钟回调时,可能会导致的异常问题。(0.22.2a2新增) 问题修复: 修复在增加系统时钟回调时,如果回调函数代码错误产生异常,会导致系统时钟停顿的问题。(0.22.2a2新增) auto_chars 示例 以下 pymud.cfg 代码可以让运行pymud时自动打开会话并登录 mychar2 和 mychar4 : { "sessions": { "pkuxkx": { "host": "mud.pkuxkx.net", "port": "8081", "encoding": "utf8", "autologin": "{0};{1}", "default_script": ["main"], "chars": { "mychar1": ["mycharid1", "mypassword1"], "mychar2": ["mycharid2", "mypassword2"], "mychar3": ["mycharid3", "mypassword3"], "mychar4": ["mycharid4", "mypassword4"], } } }, "auto_chars": ["mychar2", "mychar4"] } 0.22.2a2版修复的错误概述 本版修复的两个错误主要解决 websocket 插件会调用系统时钟回调来处理sendOverview,但添加/移除回调或者回调写错都会导致系统时钟停止的问题。系统时钟停止后,会导致sendOverview不会再被调用刷新,且控制台ui的连接时间也不会保持每秒自动刷新,而只是随数据变化刷新。
  • 国外的mud有啥推荐的?

    MUD游戏讨论区
    4
    0 赞同
    4 帖子
    48 浏览
    N
    还有这个网站也是: https://mudstats.com/
  • PyMUD论坛上线啦

    公告与通知
    6
    0 赞同
    6 帖子
    34 浏览
    N
    @bigb 在 PyMUD论坛上线啦 中说: 邮件确认也可以了,但是等了一天邮件才到。 邮件确认问题是因为我在前天晚上不小心改错了配置,昨天上午修复后重新发的,现在应该没问题。
  • PyMUD运行的好伙伴 - tmux终端复用器

    个人博客区
    3
    0 赞同
    3 帖子
    34 浏览
    N
    windows都有窗口,不需要使用tmux也没关系
  • 本站点服务器已升级

    公告与通知
    1
    0 赞同
    1 帖子
    24 浏览
    N
    由于中国大陆访问回程不稳定问题,已将运行服务器升级为中国大陆访问更优的套餐,本站点已完成迁移,欢迎大家继续支持本论坛。 2026年1月1日
  • PyMUD 帮助文档

    PyMUD讨论区
    1
    0 赞同
    1 帖子
    29 浏览
    T
    https://doc.pymud.cn/
  • 后天就是2026年了

    闲聊灌水区
    2
    1 赞同
    2 帖子
    18 浏览
    T
    明天就是2026了