特征
- 普通对象, 同步: 分配财产,
push到数组,或set在地图上。 改变跟踪,编码和解码都是发生在你的身上. - Delta 编码:只有更改的属性才以紧凑的二进制格式发送.
- 解码时触发回调数字 : 带上你自己的 调用系统在解码时,或使用内置系统.
- 案件查询:共享全州同一案例的参考文献.
- 国家意见:每个客户的能见度. 决定每个客户获得哪些属性和实例 .
- 反思: Encode/Decode schema定义.
- Schema 生成: 生成严格输入语言的客户端-侧图文件.
- 类型安全:严格地打入了计划定义.
- 多语言支持:多种语言可用的解码器(C# (韩语), (中文(简体) ), 卢亚, (中文(简体) ), 哈克丝) (中文(简体) ).
Schema 定义
定义可同步结构 schema() 财务报告和审定财务报表 t.* 字段构建器 :
import { schema, t, type SchemaType } from '@colyseus/schema';
export const Player = schema({
name: t.string(),
x: t.number(),
y: t.number(),
}, "Player");
export type Player = SchemaType<typeof Player>;
export const MyState = schema({
fieldString: t.string(),
fieldNumber: t.number(),
player: Player,
arrayOfPlayers: t.array(Player),
mapOfPlayers: t.map(Player),
}, "MyState");
export type MyState = SchemaType<typeof MyState>;
schema() 返回一个真实的类( c)instanceof works)并运行在平版JavaScript和TypeScript相同,没有编译器配置. 这个 type 化名是TypeScript-只用糖-在普通JS中省略.
装饰者(仍在支持)
经典 @type() 装饰器样式仍然完全支持,两种样式都产生相同的线条格式:
import { Schema, type, ArraySchema, MapSchema } from '@colyseus/schema';
export class Player extends Schema {
@type("string") name: string;
@type("number") x: number;
@type("number") y: number;
}
由于生态系统兼容性问题,我们正在远离装饰者:它们依赖于遗产 experimentalDecorators 执行进展情况 useDefineForClassFields: false,它与现代工具链默认(sbuilding, SWC, Vite)相冲突,与TC39的装饰器规格相去甚远,在平版JavaScript中不可用. 见 设计参考 用于设置和完整的装饰文件.
类型脚本支持
与类型脚本兼容 5.x 电话, (中文(简体) ), 6.x (单位:千美元) 财务报告和审定财务报表 7.x 电话。 。 。 .
这个 @type() 装饰者使用后遗症的装饰器——在您 tsconfig.json数字 :
{
"compilerOptions": {
"experimentalDecorators": true,
"useDefineForClassFields": false
}
}
说明:
schema-codegen需要安装在您的脚本 5.x 或 6.x 中 项目 — TypeScript 7 的本地编译器不再载运 JS编译器 API 那个密码用于解析你的计划文件。 运行时间和你的建设是 不受此影响.
支持类型
初级类型
| 类型 | 说明 | 限制 |
|---|---|---|
| 字符串 | utf8 字符串 | 最大字节大小 4294967295 |
| 编号 | 自动检测 int 或者说 float 类型。 (输出时的字节外) | 0 改为 18446744073709551615 |
| 布尔 | true 或者说 false | 0 或者说 1 |
| 单位8 | 签名 8- 位整数 | -128 改为 127 |
| 金图8 | 未签名的 8- 位整数 | 0 改为 255 |
| 单位16 | 签名 16 位整数 | -32768 改为 32767 |
| 金特16号 | 无符号 16- 位整数 | 0 改为 65535 |
| 单位32 | 签名 32 位整数 | -2147483648 改为 2147483647 |
| 金特32号 | 未签名的32位整数 | 0 改为 4294967295 |
| 单位64 | 签名64位整数 | -9223372036854775808 改为 9223372036854775807 |
| 金特64号 | 未签名的64位整数 | 0 改为 18446744073709551615 |
| 浮点32 | 单精度浮点数 | -3.40282347e+38 改为 3.40282347e+38 |
| 浮点64 | 双精度浮点数 | -1.7976931348623157e+308 改为 1.7976931348623157e+308 |
声明:
每个原始类型都通过它的 t.* 工厂t.string(), (中文(简体) ), t.uint8()(中文(简体) ). 收藏元素采用类型 名称,不是建筑师: t.array("string")永远 t.array(t.string())。 。 。 .
初级类型(string, (中文(简体) ), number, (中文(简体) ), boolean页:1
name: t.string(),
health: t.int32(),
儿童 Schema 结构
player: Player, // shorthand for t.ref(Player)
矩阵 Schema 结构
arrayOfPlayers: t.array(Player),
原始类型的阵列
不能在阵列内混合类型.
arrayOfNumbers: t.array("number"),
arrayOfStrings: t.array("string"),
地图 Schema 结构
mapOfPlayers: t.map(Player),
原始类型的地图
地图内部不能混合原始类型.
mapOfNumbers: t.map("number"),
mapOfStrings: t.map("string"),
反思
Schema 定义可以通过 Reflection你可以拥有
在服务器侧定义执行, 只要发送编码
向客户端反思,例如:
import { schema, t, Encoder, Reflection } from "@colyseus/schema";
const MyState = schema({
currentTurn: t.string(),
// ... more definitions
}, "MyState");
// server-side: encode the schema definition itself
const encoder = new Encoder(new MyState());
const encodedStateSchema = Reflection.encode(encoder);
// ... send `encodedStateSchema` across the network
// client-side: rebuild the state without having its definition
const decoder = Reflection.decode(encodedStateSchema);
const myState = decoder.state;
StateView 编号 : .view()
你可以用这个 .view() 字段修改器以过滤只应发送到的属性 StateView即能入会.
import { schema, t } from "@colyseus/schema";
const Player = schema({
secret: t.string().view(),
notSecret: t.string(),
}, "Player");
const MyState = schema({
players: t.map(Player),
}, "MyState");
使用 StateView
const view = new StateView();
view.add(player);
编码器
共有3个主要特征: Encoder 类 :
- 编码完整的状态
- 编码状态变化
- 有过滤器的编码状态( 标记为属性)
.view()(中文(简体) )
import { Encoder } from "@colyseus/schema";
const state = new MyState();
const encoder = new Encoder(state);
新客户在第一次连接时必须获得完整状态:
const fullEncode = encoder.encodeAll();
// ... send "fullEncode" to client and decode it
必须发出进一步的状态变化,以便:
const changesBuffer = encoder.encode();
// ... send "changesBuffer" to client and decode it
用视图编码
使用时 .view() 财务报告和审定财务报表 StateView'',必须使用单一"完整编码"来进行多视图. 每个观点还必须加上自己的变化.
// shared buffer iterator
const it = { offset: 0 };
// shared full encode
encoder.encodeAll(it);
const sharedOffset = it.offset;
// view 1
const fullEncode1 = encoder.encodeAllView(view1, sharedOffset, it);
// ... send "fullEncode1" to client1 and decode it
// view 2
const fullEncode2 = encoder.encodeAllView(view2, sharedOffset, it);
// ... send "fullEncode" to client2 and decode it
每个视图编码更改 :
// shared buffer iterator
const it = { offset: 0 };
// shared changes encode
encoder.encode(it);
const sharedOffset = it.offset;
// view 1
const view1Encoded = this.encoder.encodeView(view1, sharedOffset, it);
// ... send "view1Encoded" to client1 and decode it
// view 2
const view2Encoded = this.encoder.encodeView(view2, sharedOffset, it);
// ... send "view2Encoded" to client2 and decode it
// discard all changes after encoding is done.
encoder.discardChanges();
解码器
这个 Decoder 类用于解码从服务器收到的二进制数据.
import { Decoder } from "@colyseus/schema";
const state = new MyState();
const decoder = new Decoder(state);
decoder.decode(encodedBytes);
后向/ 前向兼容性
后向/ 前向兼容性可以通过在
终止现有结构,并提前宣布不删除,但
标记 .deprecated() 需要的时候.
这对本地编译的目标特别有用,如 C#, C++, Haxe等 - 客户端不可能拥有最多 最新版本的"计划"定义.
限制和最佳做法
- 每个
Schema结构可以维持64字段。 如果需要更多田地,使用巢状结构. - 标记为
.view(), (中文(简体) ),.unreliable(),或.fullStateOnly()字段索引≥ 32使用更慢的 per- mutation 分类路径( 线性扫描覆盖已标记的字段列表, 而不是单个位图 op) 。 对于超过32个字段的计程器,请提前声明频繁被标记的字段,这样它们就会掉入位图快道. - 带有
≤ 8字段以两个数字(没有分配每个实例)存储每个实地操作字节。 带有> 8字段分配小块Uint8Array用于操作存储。 这种差别只有在分配每滴数以千计的情况时才具有重大意义——在这一制度中更倾向于较窄的巢状结构. NaN或者说null数字编码为0null字符串编码为""Infinity数字编码为Number.MAX_SAFE_INTEGER- 多维数组不支持.
- 阵列和地图中的项目必须全部为同类型实例 .
@colyseus/schema编码仅以指定的顺序编码字段值.- 编码器(server)和解码器(client)都必须有相同的计划定义.
- 田地之序须同.
生成客户端计划文件( 用于严格输入的语言)
如果使用JavaScript或LUA,就没有必要去烦了. 被解释的编程语言可以通过使用
Reflection。 。 。 .
您可以根据您的服务器侧图定义自动生成客户端侧图文件 schema() 和装饰样式得到支持.
schema-codegen需要在您的工程中安装 typeScript 5. x 或 6. x (TypeScript 7+不再将JS编译器API用于解析).
# C#/Unity
schema-codegen ./schemas/State.ts --output ./unity-project/ --csharp
# C/C++
schema-codegen ./schemas/State.ts --output ./cpp-project/ --cpp
# Haxe
schema-codegen ./schemas/State.ts --output ./haxe-project/ --haxe
代码生成选项
| 选项 | 说明 |
|---|---|
--output | 生成的客户端计划文件输出目录( 需要) |
--bundle | 将所有生成的文件装入单个文件 |
--namespace | 生成输出代码上的命名空间/ 软件包 |
--decorator | 自定义名称 @type 要扫描的装饰器 |
--tsconfig | tsconfig.json 来解析导入路径的别名( 默认: 最近的) tsconfig.json编号 :jsconfig.json 在每个源文件上方) |
遵循进口发现相关计划,包括光谱仪
地图由 compilerOptions.paths编号 :baseUrl 和再导出的桶文件
他们。 未跟踪已安装软件包的导入 .
套装模式
默认情况下,代码生成器每个计划类创建一个文件. 使用 --bundle 选项,将所有生成的类合并为一个文件:
# Generate a single bundled file
schema-codegen ./schemas/State.ts --output ./unity-project/ --csharp --bundle
# With namespace
schema-codegen ./schemas/State.ts --output ./unity-project/ --csharp --bundle --namespace MyGame.Schema
Bundle 模式输出文件名 :
- 类型脚本数字 :
schema.ts(或{namespace}.ts(中文(简体) ) - Java 脚本数字 :
schema.js(或{namespace}.js(中文(简体) ) - C# (韩语)数字 :
Schema.cs(或{namespace}.cs(中文(简体) ) - C++ 组合数字 :
schema.hpp(或{namespace}.hpp(中文(简体) ) - 哈克丝数字 :
Schema.hx(或{namespace}.hx(中文(简体) ) - 爪哇语数字 :
Schema.java - 卢亚数字 :
schema.lua(或{namespace}.lua(中文(简体) ) - C级数字 :
schema.h(或{namespace}.h(中文(简体) )
基准:
| 假设 | @colyseus/schema | msgpack + 键 fossil-delta |
|---|---|---|
| 初始状态规模( 100个实体) | 2671号 | 3283 (英语) |
| 初始状态后更新一个实体的 x/y | 第9条 | 第 26 条 |
| 在初始状态后更新50个实体的 x/y | 342个 | 684个 |
| 初始状态后更新100个实体的 x/y | 668个 | 1529 (英语) |
其他语言的解码器执行
每个Colyseus SDK都有自己的解码器执行 @colyseus/schema 协议 :
为什么
初步想法/设想:
- 检测状态变化的瓶颈小到没有.
- 在服务器和客户端上都有一个计划定义
- 在静态类型语言(C#, C++)方面更好的经验
- 变相应该很便宜.
实际的Colyseus问题应该解决:
- 避免解码未补齐的大对象
- 允许为每个客户端发送不同的补丁
- 改善静态型语言的开发者经验
灵感:
许可证
麻省理工学院