消息路径

> look

草地

这是一片美丽的草地,满是鲜花。

你看到:一朵花
出口:北,东

当你在 Evennia 中发送一个像 look 这样的命令时,实际上发生了什么?这个 look 字符串是如何被 CmdLook 类处理的?当我们使用例如 caller.msg() 发送消息返回时,会发生什么?

理解这种数据流——消息路径对于理解 Evennia 的工作原理是很重要的。

传入消息路径

            Internet│
            ┌─────┐ │                                   ┌──────────┐
┌──────┐    │文本 │ │  ┌────────────┐    ┌─────────┐    │命令      │
│客户端├────┤JSON ├─┼──►命令元组    ├────►输入函数 ├────►数据库查询│
└──────┘    │等   │ │  └────────────┘    └─────────┘    │等        │
            └─────┘ │                                   └──────────┘
                    │Evennia

传入命令元组

来自客户端的传入数据(以原始字符串或序列化的 JSON 形式)被 Evennia 转换为一个 命令元组。无论使用什么客户端或连接,这些都是相同的。一个 命令元组 是一个简单的三元素元组:

(commandname, (args), {kwargs})

对于 look 命令(以及玩家输入的其他内容),生成的 text 命令元组为:

("text", ("look",), {})

输入函数

在 Evennia 服务器端,注册了一系列 输入函数。你可以通过扩展 settings.INPUT_FUNC_MODULES 来添加自己的输入函数。

inputfunc_commandname(session, *args, **kwargs)

这里的 session 代表了唯一的客户端连接,即标识是谁发送了这个输入。

有一个这样的输入函数名为 text。对于发送 look,它将被调用为:

text(session, *("look",), **{})  

一个 输入函数 的作用取决于它的实现。对于一个 带外 指令,它可能会获取玩家的健康状态或减少某个计数器。

对于 text 输入函数,Evennia 的 命令处理器 被调用,参数会被进一步解析以确定意图的命令。

look 的例子中,CmdLook 命令类将被调用。它将检索当前位置的描述。

传出消息路径

            Internet│
            ┌─────┐ │
┌──────┐    │文本 │ │  ┌──────────┐    ┌────────────┐   ┌─────┐
│客户端◄────┤JSON ├─┼──┤输出函数◄──────┤命令元组◄───────┤msg()│
└──────┘    │等   │ │  └──────────┘    └────────────┘   └─────┘
            └─────┘ │
                    │Evennia

msg 到传出命令元组

输入函数 完成其应做的事情后,服务器可能决定返回一个结果(有些类型的 输入命令 可能不期望或不需要响应)。服务器也经常在没有任何先前匹配的传入数据的情况下发送传出消息。

每当需要将数据从 Evennia “发送出” 时,我们必须将其概括为一个(现在是传出)命令元组 (commandname, (args), {kwargs})。我们通过 msg() 方法来实现这一点。为了方便起见,这个方法在每个主要实体上都可用,例如 Object.msg()Account.msg()。它们都链接回 Session.msg()

msg(text=None, from_obj=None, session=None, options=None, **kwargs)

text 是如此常见,以至于它被作为默认值:

msg("一片草地\n\n这是一片美丽的草地...")

这被转换为一个 命令元组,如下所示:

("text", ("一片草地\n\n这是一片美丽的草地...",) {})

msg() 方法允许你直接定义 命令元组,用于你想要找到的任何传出指令:

msg(current_status=(("健康", "充能"), {"hp": 12, "mp": 20}))

这将被转换为一个 命令元组,如下所示:

("current_status", ("健康", "充能"), {"hp": 12, "mp": 20})

输出函数

由于 msg() 知道要发送到哪个 会话,传出的 命令元组 总是指向正确的客户端。

每个支持的 Evennia 协议(Telnet、SSH、Webclient 等)都有自己的 输出函数,它将通用的 命令元组 转换为该协议理解的形式,如 Telnet 指令或 JSON。

对于 Telnet(无 SSL),look 将以纯文本形式返回:

一片草地\n\n这是一片美丽的草地...

当发送到 Web 客户端时,命令元组 被转换为序列化的 JSON,如下所示:

'["look", ["一片草地\\n\\n这是一片美丽的草地..."], {}]'

然后将其通过网络发送给客户端。由客户端解释和处理数据。

路径上的组件

传入

                ┌──────┐                ┌─────────────────────────┐
                │客户端│                │                         │
                └──┬───┘                │  ┌────────────────────┐ │
                   │             ┌──────┼─►│服务器会话处理器    │ │
┌──────────────────┼──────┐      │      │  └───┬────────────────┘ │
│门户              │      │      │      │      │                  │
│        ┌─────────▼───┐  │    ┌─┴─┐    │  ┌───▼─────────┐        │
│        │门户会话     │  │    │AMP│    │  │服务器会话   │        │
│        └─────────┬───┘  │    └─┬─┘    │  └───┬─────────┘        │
│                  │      │      │      │      │                  │
│ ┌────────────────▼───┐  │      │      │  ┌───▼─────┐            │
│ │门户会话处理器      ├──┼──────┘      │  │输入函数 │            │
│ └────────────────────┘  │             │  └─────────┘            │
│                         │             │                  服务器 │
└─────────────────────────┘             └─────────────────────────┘
  1. 客户端 - 通过网络发送握手或命令。这由 Evennia 门户接收。

  2. 门户会话 代表一个客户端连接。它理解使用的通信协议。它将协议特定的输入转换为通用的 命令元组 结构 (cmdname, (args), {kwargs})

  3. 门户会话处理器 处理所有连接。它将 命令元组 与会话 ID 一起序列化。

  4. 序列化的数据通过 AMP(异步消息协议)连接发送到 Evennia 的 服务器部分。

  5. 服务器会话处理器 反序列化 命令元组 并将会话 ID 匹配到一个匹配的 服务器会话

  6. 服务器会话 代表服务器端的会话连接。它在其注册的 输入函数 中寻找匹配项。

  7. 调用适当的 输入函数,包括在 命令元组 中的 args/kwargs。根据 输入函数 的不同,这可能会有不同的效果。对于 text 输入函数,它触发 命令处理器

传出

                ┌──────┐                ┌─────────────────────────┐
                │客户端│                │                         │
                └──▲───┘                │  ┌────────────────────┐ │
                   │             ┌──────┼──┤服务器会话处理器    │ │
┌──────────────────┼──────┐      │      │  └───▲────────────────┘ │
│门户              │      │      │      │      │                  │
│        ┌─────────┴───┐  │    ┌─┴─┐    │  ┌───┴─────────┐        │
│        │门户会话     │  │    │AMP│    │  │服务器会话   │        │
│        └─────────▲───┘  │    └─┬─┘    │  └───▲─────────┘        │
│                  │      │      │      │      │                  │
│ ┌────────────────┴───┐  │      │      │  ┌───┴──────┐           │
│ │门户会话处理器   ◄──┼─────────┘      │  │msg() 调用│           │
│ └────────────────────┘  │             │  └──────────┘           │
│                         │             │                  服务器 │
└─────────────────────────┘             └─────────────────────────┘
  1. 调用 msg() 方法。

  2. 服务器会话,特别是 服务器会话.msg() 是所有 msg() 调用的中心点,用于将数据发送到那个 会话

  3. 服务器会话处理器msg 输入转换为一个适当的 命令元组 结构 (cmdname, (args), {kwargs})。它将 命令元组 与会话 ID 一起序列化。

  4. 序列化的数据通过 AMP(异步消息协议)连接发送到 Evennia 的 门户部分。

  5. 门户会话处理器 反序列化 命令元组 并将其会话 ID 匹配到一个匹配的 门户会话

  6. 门户会话 现在负责将通用的 命令元组 转换为该特定连接使用的通信协议。

  7. 客户端接收数据并可以对其进行操作。