Filter 是什么意思?
Filter 这个词在检索和匹配类的程序中经常见到.
比如下面这个网站.
Type to filter... 这里我们填写关键词,
匹配程序精准地帮我们找到了需要的内容.
Colyseus 里面有两处与 Filter 有关的地方. 一个是 Matchmaking 用的 filterBy(); 一个是 Schema 用的 @filter 装饰器.
RegisteredHandler.filterBy()
这次我们用 Colyseus Server + Cocos Creator Client 来做展示.
首先建立服务器环境.
不知道这个菜单哪里来的的话请参考这里.
新建 Cocos 项目, 安装 Colyseus 客户端 SDK.
如果发现导入时报错,
要在 tsconfig.json 文件中开启 allowSyntheticDefaultImports
参数.
好了, 准备工作结束, 让我们来连接房间看看.
joinOrCreate
函数的第一个参数是房间名, 第二个参数是自定义的房间参数.
这里我们自定义一个叫做 mode
的参数, 值为 duo
, 表示是双人游戏房间.
this.room = await this.client.joinOrCreate("my_room", { mode: "duo" });
在服务器端, 我们定义了房间名 my_room, 然后后面跟着一个 filterBy, 里面有两个过滤参数, "mode" 和 "level".
gameServer.define('my_room', MyRoom).filterBy(["mode", "level"]);
它的意思是, 在进行房间匹配的时候, 把所有房间根据 "mode" 和 "level" 两个参数过滤, 只有这两个参数 完全匹配
的玩家才可以匹配进入相应房间.
还记得客户端连接时的需求吗? 玩家要进入 "mode" 为 "duo" 的房间, "level" 没有指定.
匹配器会根据 server 上已有的没有锁定的房间, 过滤出 "mode" 为 "duo" 的, "level" 为 null 的房间, 如果存在, 优先进入; 如果不存在, 新建一个这样的房间再进入.
如果客户端没有要求参数, 如何匹配房间呢? 匹配器会寻找 "mode" 为 "null" 而且 "level" 为 "null" 的房间优先匹配进入; 对于 "mode" 为 "duo" 的房间, 它是 不考虑在内
的.
除了 filterBy, 常用的还有一个 sortBy, 两个联合使用又是什么意思呢?
gameServer.define('my_room', MyRoom).filterBy(["mode"]).sortBy({level:-1});
sortBy 的意思是排序. 根据参数值升序 (+) 或者降序 (-) 排序, 然后进行优先匹配.
上面那条语句的意思是, 寻找 mode 一致的, 按 level 从大到小排列, 选最大的, 进行匹配.
可以看出, filterBy 的作用就是告诉匹配器, 如何寻找, 找什么样的房间, 来给客户端进行分配.
Schema 的 @filter 装饰器
众所周知(?), Schema 是会自动同步到房间的每一个客户端中的, 是 "服务器权威" 模式的基础.
但是有时候, 我们需要某个客户端只能知道 Schema 的一部分而非全部.
典型的需求就是牌类游戏, 每个客户端只能了解自己的手牌而不能知道其他人的牌.
让我们开发一个简单的猜骰子点数大小的游戏. 游戏分三个步骤:
- 玩家进入服务器房间, 服务器开始摇骰子出一个点数, 此时玩家虽然知道已摇出点数, 但是不能知道点数是多少;
- 玩家猜测点数大小并发送到服务器;
- 服务器开点数, 并把结果发回给客户端.
点数 1~3 是 "小", 4~6 是 "大".
游戏的 Schema 设定是关键所在:
import {Schema, Context, type, filter} from "@colyseus/schema";
import {Client} from "colyseus";
export class Dice extends Schema {
@type("string") status: string = "rolling";
@type("string") result: string = "";
@filter(function (this: Dice, client: Client, value: Dice['number'], root: Schema) {
return this.status == "opened";
})
@type("uint8") number: number;
}
首先导入 filter 装饰器, 才能使用. 这个游戏我们要过滤的是骰子点数, 也就是最后的 number. 过滤方法是看当前状态是否已经到了开点数的 status.
status 分为 "要骰子", "猜点数", "开点数" 三个阶段.
"猜点数" 阶段虽然客户端知道有 "number" 这个数据, 但是值是 undefined.
当服务器在任何地方执行了 this.state.status = "opened";
这条语句, 过滤自动失效, 此后客户端就能知道具体点数了.
代码和手册参考:
https://github.com/CocosGames/ColyseusFilters
https://docs.colyseus.io/colyseus/state/schema/#filtering-data-per-client