@shanghua 在 如何加载不同模块(.py)? 中说:
https://www.pkuxkx.net/forum/thread-50002-1-1.html
跟这帖子不一样吗?
一样的,就是这个
Forum wide moderators
@shanghua 在 如何加载不同模块(.py)? 中说:
https://www.pkuxkx.net/forum/thread-50002-1-1.html
跟这帖子不一样吗?
一样的,就是这个
很好,就是需要有探索精神

你可以试试看,我不太确定行不行,main.py不知道放什么的话,不用也没关系
步骤一:先把目录结构改成这样的
>
> |-----mud_main.py # 主入口
> |-----pymud.cfg # 配置文件,设置default_script": [" mud_main"]
> |-----xkx/ # 此处增加一个目录,作为package管理所有的下级模块
> |----- __init__.py # 此处增加一个空文件,文件名必须为 __init__.py 用来指示 xkx 这个目录是一个 package。下级每一个子目录都要增加一个 __init__.py 的空文件
> |----- main.py # 建议增加一个包内的最终管理文件,通用的配置都放在这里,mud_main.py仅保留入口检查功能
> |----- triggers/
> |----- __init__.py # 空文件,指示为package
> |----- common_triggers.py # 通用trigger
> |----- commands/
> |----- __init__.py # 空文件,指示为package
> |----- fight.py # fight 模块
步骤二: mud_main.py 的内容写成这样的
import os
from pymud import IConfig, Session
class EntryConfig(IConfig):
def __init__(self, session: Session, *args, **kwargs):
reload = kwargs.get("reload", False) # 处理参数里的 reload, 用于支持 #reload mud_main 就可以重新加载所有脚本
self.session = session
# 脚本加载顺序
mods = list()
current_dir = os.path.dirname(__file__) # 获取当前文件所在的目录
root_dir = os.path.join(current_dir, "xkx") # 当前目录下的xkx目录
dir = os.path.join(root_dir, "triggers") # 处理triggers子目录下的所有文件
if os.path.exits(dir):
for file in os.listdir(dir):
if file.endswith(".py") and (not file.startswith("__")): #获取目录下的所有.py文件,并排除 __init__.py
mods.append(f"xkx.triggers.{file[:-3]}")
dir = os.path.join(root_dir, "commands") # 处理 commands目录下的所有文件,原理同上
if os.path.exists(dir):
for file in os.listdir(dir):
if file.endswith(".py") and (not file.startswith("__")): #获取目录下的所有.py文件,并排除 __init__.py
mods.append(f"xkx.commands.{file[:-3]}")
mods.append("xkx.main") # 通用的main文件可能会调用其他模块内容,放在最后
session.load_module(mods)
if reload: # 处理 reload
session.reload_module(mods)
self.mods = mods # 保存mods供unload使用
def __unload__(self):
# 卸载时按照加载的反向顺序逐个卸载
for mod in reversed(self.mods):
self.session.unload_module(mod)
后续要往子目录里增加文件,直接加 xxx.py 就可以了,在子目录下的文件会被自动获取并加载。
可以进行内存占用分析了(不过分析自身很占资源)

#var命令可以处理嵌套命令了!

本版本还在测试中,尽请期待 
很多时候都是这么实现的,比如抓房间描述不确定多少行时,用房间名做首行触发,用出口描述做结束触发,中间再判断是什么。
或者可以自己定义多个触发器来配合实现总行数不确定的情况,比如第一个触发器tri1仅触发首行,第二个tri2仅触发尾行,第三个触发器tri3用 .* 作为全部匹配。当第一个触发器触发时,打开第三个触发器,然后对每一个进行判断;当第二个触发器触发时,关闭第三个触发器,这样就能全部手动处理了。
PyMUD的多行触发是以开头第一行作为标志的,所以patterns的第一行必须要是一个有明显特征的行。只要首行不会匹配到其他内容,那么后续N行的出发匹配都OK。
WEB页面:https://www.rfc-editor.org/rfc/rfc854.html
发布日期:1983-05
本RFC为ARPA互联网社区指定了一个标准。ARPA互联网上的主机预计将采用并实施本标准。
TELNET协议的目的是提供一种相当通用的、双向的、面向八位字节的通信设施。它的主要目标是允许一种将终端设备和面向终端的过程相互连接的标准方法。可以设想,该协议也可以用于终端通信(“链接”)和过程过程通信(分布式计算)。
TELNET连接是一种传输控制协议(TCP)连接,用于传输带有穿插TELNET控制信息的数据。
TELNET协议建立在三个主要思想之上:第一,“网络虚拟终端”(Network Virtual Terminal, NVT)的概念;第二,协商选择权原则;第三,终端和过程的对称视图。
网络虚拟终端(NVT)是一种双向字符设备。NVT有一台打印机和一个键盘。打印机响应输入数据,键盘产生输出数据,通过TELNET连接发送,如果需要“回声”,也发送到NVT的打印机。“回声”将不会穿过网络(尽管存在启用“远程”回声操作模式的选项,但不需要主机来实现此选项)。代码集是八位字段中的七位USASCII,此处修改的除外。任何代码转换和时序考虑都是局部问题,不会影响NVT。
尽管通过网络的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,至少在概念上是这样。
如本文件导言所述,TELNET协议的主要目标是通过网络提供终端设备和面向终端的进程的标准接口。这种类型的互连的早期经验表明,某些功能是由大多数服务器实现的,但调用这些功能的方法差异很大。对于与多个服务器系统交互的人类用户来说,这些差异非常令人沮丧。因此,TELNET为其中五个函数定义了一个标准表示,如下所述。这些标准表示具有标准但非必需的含义(使用TELNET的其他协议可能需要中断处理(IP)功能除外);也就是说,不向本地用户提供功能的系统不需要向网络用户提供功能,并且可以将功能的标准表示视为“否”操作。另一方面,向本地用户提供功能的系统必须向发送该功能的标准表示的网络用户提供相同的功能。
许多系统提供暂停、中断、中止或终止用户进程操作的功能。当用户认为自己的进程处于无休止的循环中,或者无意中激活了不需要的进程时,经常使用此功能。IP是调用此函数的标准表示形式。实施人员应注意,使用TELNET的其他协议可能需要IP,因此,如果要支持这些其他协议,则应实施IP。
许多系统提供了一种功能,该功能允许正在生成输出的进程运行到完成(或达到与运行到完成时相同的停止点),但不将输出发送到用户的终端。此外,该功能通常清除已经产生但尚未实际打印(或显示)在用户终端上的任何输出。AO是调用此函数的标准表示。例如,一些子系统通常可以接受用户的命令,向用户的终端发送一个长文本字符串作为响应,最后通过向用户的端子发送一个“提示”字符(前面有<CR><LF>)来表示准备接受下一个命令。如果在传输文本字符串的过程中接收到AO,则合理的实现方式是抑制文本字符串的剩余部分,但传输提示字符和前面的<CR><LF>。(这可能与接收到IP时可能采取的操作不同;IP可能会导致文本字符串被抑制并退出子系统。)
应该注意的是,通过提供该功能的服务器系统,可能存在系统外部的缓冲区(在网络和用户的本地主机中),这些缓冲区应该被清除;实现这一点的适当方法是向用户系统发送“Synch”信号(如下所述)。
许多系统提供一种功能,该功能向用户提供一些可见(例如,可打印)的证据,表明系统仍在运行。当系统出乎意料地长时间“静默”时,用户可能会调用此函数,因为计算的长度出乎意料(用户),系统负载异常重等。AYT是调用此函数的标准表示。
许多系统提供了一种功能,可以从用户提供的数据流中删除最后一个在前未删除的字符或“打印位置”*。此功能通常用于在出现键入错误时编辑键盘输入。EC是调用此函数的标准表示。
注意:“打印位置”可能包含几个字符,这些字符是重击的结果,或是<char1>BS<char2>等序列的结果。。。
许多系统提供删除当前输入“行”中所有数据的功能。此功能通常用于编辑键盘输入。EL是调用此函数的标准表示。
因为PyMUD开发需要经常在多个Python版本下测试是否有效,以前使用传统的pyenv来创建虚拟环境解决问题,后来发现操作起来实在太过于繁琐,因此找到了一款新的基于命令行的包管理工具, uv,完整替代了我所有的需求,包括不同Python版本的管理、虚拟环境、依赖管理等等,而且 uv 是通过 rust 开发,支持并发操作,碰到新环境搭建需要 pip 拉取一堆依赖项时,速度极快,已经是我正常开发和运行过程中不可缺少的工具了,现在推荐给大家。
用一个工具替代 pip,pip-tools,pipx,poetry,pyenv,twine,virtualenv 等等
️ 比标准pip快10~100倍。
可用于安装和管理 Python 版本。
️ 管理pypi发布的 Python 包。
可以通过curl或者pip安装,而无需安装 Rust 或 Python。
️ 跨平台,支持 macOS、Linux 和 Windows。详细信息可参见官方文档 