会话

┌──────┐ │   ┌───────┐    ┌───────┐   ┌──────┐
│Client├─┼──►│Session├───►│Account├──►│Object│
└──────┘ │   └───────┘    └───────┘   └──────┘
                 ^

Evennia 中的 会话 表示与服务器建立的单个连接。根据 Evennia 会话的不同,一个人可以多次连接,例如在多个窗口中使用不同的客户端。每个这样的连接都由一个会话对象表示。

会话对象有自己的 cmdset,通常是“未登录”cmdset。这用于显示登录屏幕和处理创建新账户(或在 Evennia 术语中为 账户)的命令,读取初始帮助并使用现有账户登录游戏。会话对象可以是“已登录”或未登录。已登录意味着用户已通过身份验证。当这种情况发生时,会话与一个账户对象关联(这是持有账户相关内容的地方)。然后,账户可以控制任意数量的对象/角色。

会话不是 持久的 ——它不是一个 Typeclass,也没有与数据库的连接。当用户断开连接时,会话将消失,如果服务器重载,你将丢失会话上的任何自定义数据。会话上的 .db 处理程序是为了提供统一的 API(因此即使你不知道接收到的是对象还是会话,你也可以假设 .db 存在),但这只是 .ndb 的别名。因此,不要在会话上存储任何你无法承受在重载中丢失的数据。

使用会话

会话上的属性

以下是(服务器)会话上可用的一些重要属性:

  • sessid - 唯一的会话 ID。这是一个从 1 开始的整数。

  • address - 连接客户端的地址。不同协议在此处提供不同的信息。

  • logged_in - 如果用户已通过此会话进行身份验证,则为 True

  • account - 此会话附加到的 账户。如果尚未登录,则为 None

  • puppet - 由此账户/会话组合当前控制的 角色/对象。如果未登录或处于 OOC 模式,则为 None

  • ndb - 非持久属性 处理程序。

  • db - 如上所述,会话没有常规属性。这是 ndb 的别名。

  • cmdset - 会话的 CmdSetHandler

会话统计信息主要由 Evennia 内部使用。

  • conn_time - 此会话已连接的时间。

  • cmd_last - 最后活跃的时间戳。这将通过发送 idle 保活重置。

  • cmd_last_visible - 最后活跃的时间戳。这忽略 idle 保活,表示此会话上次真正可见的活跃时间。

  • cmd_total - 通过此会话的命令总数。

向会话返回数据

当你使用 msg() 向用户返回数据时,你调用 msg() 的对象很重要。MULTISESSION_MODE 也很重要,尤其是在大于 1 的情况下。

例如,如果你使用 account.msg("hello"),Evennia 无法知道应该将问候发送到哪个会话。在这种情况下,它将发送到所有会话。如果你想要特定会话,你需要在 msg 调用中提供其会话(account.msg("hello", session=mysession))。

另一方面,如果你在一个被控制的对象上调用 msg() 消息,例如 character.msg("hello"),角色已经知道控制它的会话——它会聪明地为你自动添加这个(如果你特别想将内容发送到另一个会话,你可以指定不同的会话)。

最后,所有命令类上都有一个 msg() 的包装器:command.msg()。这将透明地检测触发命令的会话(如果有)并重定向到该会话(这通常是你想要的)。如果你在重定向到给定会话时遇到问题,command.msg() 通常是最安全的选择。

你可以通过两种主要方式获取 session

  • 账户对象(包括角色)有一个 sessions 属性。这是一个 处理程序,跟踪所有附加到或控制它们的会话。例如使用 accounts.sessions.get() 获取附加到该实体的会话列表。

  • 命令实例有一个 session 属性,总是指向触发它的会话(总是一个)。如果没有会话参与,例如当一个怪物或脚本触发命令时,它将为 None

自定义会话对象

什么时候需要自定义会话对象?例如,考虑一个角色创建系统:你可能决定将其保留在角色外级别。这意味着你在某种菜单选择结束时创建角色。实际的角色创建 cmdset 通常会放在账户上。这在 MULTISESSION_MODE 低于 2 时工作得很好。对于较高的模式,替换账户 cmdset 会影响 所有 连接的会话,包括那些不参与角色创建的会话。在这种情况下,你希望将角色创建 cmdset 放在会话级别——这样即使你在其中一个会话中创建新角色,所有其他会话仍将正常工作。

默认情况下,用户首次连接时,会话对象获得 commands.default_cmdsets.UnloggedinCmdSet。一旦会话通过身份验证,它就没有默认集。要向会话添加“已登录”cmdset,请使用 settings.CMDSET_SESSION 提供 cmdset 类的路径。然后,一旦账户登录,该集将始终存在。

要进一步自定义,你可以完全使用自己的子类覆盖会话。要替换默认的会话类,请将 settings.SERVER_SESSION_CLASS 更改为指向你的自定义类。这是一种危险的做法,错误可能会使你的游戏无法玩。请务必注意 原始 并仔细进行更改。

门户和服务器会话

注意:这是一个高级主题。第一次阅读时不需要了解这一点。

Evennia 分为两个部分,门户和服务器。每一侧都跟踪自己的会话,并相互同步。

我们通常所说的“会话”实际上是 ServerSession。其在门户侧的对应物是 PortalSession。服务器会话处理游戏状态,而门户会话处理连接协议本身的细节。两者还充当关键数据的备份,例如当服务器重启时。

新账户连接由门户使用其理解的 协议 监听和处理(例如 telnet、ssh、webclient 等)。当建立新连接时,会在门户侧创建一个 PortalSession。此会话对象根据使用的协议不同而看起来不同,但仍然具有适用于所有会话的最小属性集。

这些通用属性从门户通过 AMP 连接传输到服务器,服务器现在被告知已建立新连接。在服务器端,创建一个 ServerSession 对象来表示这一点。无论账户如何连接,只有一种类型的 ServerSession;它看起来都是一样的。

从现在开始,AMP 连接的一侧的 ServerSession 和另一侧的 PortalSession 之间是一对一的匹配。到达门户会话的数据被发送到其镜像服务器会话,反之亦然。

在某些情况下,门户和服务器侧会话会相互“同步”:

  • 玩家关闭其客户端,终止门户会话。门户与服务器同步,以确保相应的服务器会话也被删除。

  • 玩家从游戏内部退出,终止服务器会话。然后,服务器与门户同步,以确保干净地关闭门户连接。

  • 服务器重启/重置/关闭 - 服务器会话被复制到门户侧(“保存”)。当服务器重新启动时,门户将返回此数据,以便两者再次同步。这样,账户的登录状态和其他连接关键内容可以在服务器重启后继续存在(假设门户没有同时停止,显然)。

会话处理程序

门户和服务器各自都有一个 会话处理程序 来管理连接。这些处理程序是全局实体,包含所有用于跨 AMP 桥接传输数据的方法。所有类型的会话都持有对其各自会话处理程序的引用(属性称为 sessionhandler),以便它们可以传输数据。有关构建新协议的更多信息,请参阅 协议

要获取游戏中的所有会话(即所有当前连接的客户端),你可以访问服务器端会话处理程序,你可以通过以下方式获取:

from evennia.server.sessionhandler import SESSION_HANDLER

注意:SESSION_HANDLER 单例有一个较旧的别名 SESSIONS,在各个地方也常常看到。

有关 ServerSessionHandler 功能的详细信息,请参阅 sessionhandler.py 模块。