会话¶
┌──────┐ │ ┌───────┐ ┌───────┐ ┌──────┐
│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
:
自定义会话对象¶
什么时候需要自定义会话对象?例如,考虑一个角色创建系统:你可能决定将其保留在角色外级别。这意味着你在某种菜单选择结束时创建角色。实际的角色创建 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 模块。