状态数据同步 » Schema¶
还没使用 TypeScript?
强烈建议您使用 TypeScript 以便更好地定义 Schema 结构并提高整体开发体验. TypeScript 支持的 "实验性修饰器" 会在本手册内大量使用.
如何定义可同步结构¶
Schema
结构由服务器定义, 用于房间状态服务端客户端数据同步.- 只有以
@type()
修饰的字段才会被用于同步. - (可同步 Schema 结构仅应被用于需要服务器客户端同步的数据.)
定义 Schema
结构¶
// MyState.ts
import { Schema, type } from "@colyseus/schema";
export class MyState extends Schema {
@type("string") currentTurn: string;
}
// MyState.ts
const schema = require('@colyseus/schema');
const Schema = schema.Schema;
class MyState extends Schema {
}
schema.defineTypes(MyState, {
currentTurn: "string"
});
"这个 @type()
关键字是什么? 我之前从未见过!"
您看见的在本页大量使用的 @type()
是一个即将推出的 JavaScript 功能, 还没有被 TC39 正式认可. type
其实只是一个从 @colyseus/schema
模块导入的函数. 在属性层级调用带有 @
前缀的 type
, 意味着我们将其作为一个 属性修饰器 进行调用. 在这里查看修饰器方案.
在您的 Room
内使用状态¶
// MyRoom.ts
import { Room } from "colyseus";
import { MyState } from "./MyState";
export class MyRoom extends Room<MyState> {
onCreate() {
this.setState(new MyState());
}
}
使用 Schema¶
- 只有服务器端有权修改 Schema 数据
- 客户端要包含用
schema-codegen
生成的, 与服务器端相一致的Schema
定义. (如果使用 JavaScript SDK 则此条为可选项) - 为了从服务器获得更新, 需要 在客户端把回调附加在 schema 实例上.
- 客户端永远不应主动修改 schema - 因为在收到来自服务器的下一帧更新时就会把它覆盖掉.
基本类型¶
基本类型为数字, 字符串和布尔型.
类型 | 描述 | 范围 |
---|---|---|
"string" |
utf8 字符串 | 最大 4294967295 字节 |
"number" |
又称为 "正整数". 自动检测数字类型. (编码时可能会多用1个字节) | 取值范围 0 到 18446744073709551615 |
"boolean" |
true 或 false |
取值为 0 或 1 |
特定数值类型:
类型 | 描述 | 范围 |
---|---|---|
"int8" |
有符号 8-bit 整数 | -128 到 127 |
"uint8" |
无符号 8-bit 整数 | 0 到 255 |
"int16" |
有符号 16-bit 整数 | -32768 到 32767 |
"uint16" |
无符号 16-bit 整数 | 0 到 65535 |
"int32" |
有符号 32-bit 整数 | -2147483648 到 2147483647 |
"uint32" |
无符号 32-bit 整数 | 0 到 4294967295 |
"int64" |
有符号 64-bit 整数 | -9223372036854775808 到 9223372036854775807 |
"uint64" |
无符号 64-bit 整数 | 0 到 18446744073709551615 |
"float32" |
单精度浮点数 | -3.40282347e+38 到 3.40282347e+38 |
"float64" |
双精度浮点数 | -1.7976931348623157e+308 到 1.7976931348623157e+308 |
复杂类型¶
复杂类型由 Schema
嵌套而成. 它们也可以包含 集合类型 (array, map 等).
import { Schema, type } from "@colyseus/schema";
class World extends Schema {
@type("number") width: number;
@type("number") height: number;
@type("number") items: number = 10;
}
class MyState extends Schema {
@type(World) world: World = new World();
}
const schema = require('@colyseus/schema');
const Schema = schema.Schema;
class World extends Schema {
}
schema.defineTypes(World, {
width: "number",
height: "number",
items: "number"
});
class MyState extends Schema {
constructor () {
super();
this.world = new World();
}
}
schema.defineTypes(MyState, {
world: World
});
集合类型¶
ArraySchema¶
ArraySchema
是 JavaScript 内置 Array 类型的一种可同步版本.
示例: 自定义 Schema
类型 数组
import { Schema, ArraySchema, type } from "@colyseus/schema";
class Block extends Schema {
@type("number") x: number;
@type("number") y: number;
}
class MyState extends Schema {
@type([ Block ]) blocks = new ArraySchema<Block>();
}
const schema = require('@colyseus/schema');
const Schema = schema.Schema;
const ArraySchema = schema.ArraySchema;
class Block extends Schema {
}
schema.defineTypes(Block, {
x: "number",
y: "number"
});
class MyState extends Schema {
constructor () {
super();
this.blocks = new ArraySchema();
}
}
schema.defineTypes(MyState, {
blocks: [ Block ],
});
示例: 基本类型 数组
数组元素必须是同一数据类型.
import { Schema, ArraySchema, type } from "@colyseus/schema";
class MyState extends Schema {
@type([ "string" ]) animals = new ArraySchema<string>();
}
const schema = require('@colyseus/schema');
const Schema = schema.Schema;
const ArraySchema = schema.ArraySchema;
class MyState extends Schema {
constructor () {
super();
this.animals = new ArraySchema();
}
}
schema.defineTypes(MyState, {
animals: [ "string" ],
});
array.push()
¶
在一个数组后面添加一个或多个元素, 并返回该数组更新后的长度.
const animals = new ArraySchema<string>();
animals.push("pigs", "goats");
animals.push("sheeps");
animals.push("cows");
// 输出: 4
array.pop()
¶
移除一个数组的最后一个元素并返回该元素. 该方法会改变数组的长度.
animals.pop();
// 输出: "cows"
animals.length
// 输出: 3
array.shift()
¶
移除一个数组的第一个元素并返回该元素. 该方法会改变数组的长度.
animals.shift();
// 输出: "pigs"
animals.length
// 输出: 2
array.unshift()
¶
在一个数组的开头添加一个或多个元素, 并返回该数组更新后的长度.
animals.unshift("pigeon");
// 输出: 3
array.indexOf()
¶
返回数组中找到给定元素的第一个索引, 如果不存在则返回 -1
const itemIndex = animals.indexOf("sheeps");
array.splice()
¶
移除替换现有元素或 在指定位置 添加新元素来更改一个数组的内容.
// 找到需要移除元素的索引
const itemIndex = animals.findIndex((animal) => animal === "sheeps");
// 移除元素!
animals.splice(itemIndex, 1);
array.forEach()
¶
迭代数组的每个元素.
this.state.array1 = new ArraySchema<string>('a', 'b', 'c');
this.state.array1.forEach(element => {
console.log(element);
});
// 输出: "a"
// 输出: "b"
// 输出: "c"
State.array1.ForEach((value) => {
Debug.Log(value);
})
state.array1:each(function(value, index)
print(index, "=>")
pprint(value)
end)
for (index => value in state.array1) {
trace(index + " => " + value);
}
Array 还有更多函数可用
详见 MDN 文档.
MapSchema¶
MapSchema
是基于 JavaScript 内置 Map 的一种可同步版本.
推荐使用 Maps 里的 id 来追踪游戏实体, 比如玩家, 敌人等.
当前仅支持字符串类型的 id
目前, MapSchema
允许您自定义值的类型, 但是键的类型必须为为 string
.
import { Schema, MapSchema, type } from "@colyseus/schema";
class Player extends Schema {
@type("number") x: number;
@type("number") y: number;
}
class MyState extends Schema {
@type({ map: Player }) players = new MapSchema<Player>();
}
const schema = require('@colyseus/schema');
const Schema = schema.Schema;
const MapSchema = schema.MapSchema;
class Player extends Schema {
}
schema.defineTypes(Player, {
x: "number",
y: "number"
});
class MyState extends Schema {
constructor () {
super();
this.players = new MapSchema();
}
}
schema.defineTypes(MyState, {
players: { map: Player }
});
map.get()
¶
通过键得到 map 的值:
const map = new MapSchema<string>();
const item = map.get("key");
或者
//
// 不建议使用这种方法
//
// 保留这种方法只是为了 @colyseus/schema 的版本向下兼容
// 未来会舍弃这种方法.
//
const item = map["key"];
map.set()
¶
通过键来设置 map 的值:
const map = new MapSchema<string>();
map.set("key", "value");
或者
//
// 不建议使用这种方法
//
// 保留这种方法只是为了 @colyseus/schema 的版本向下兼容
// 未来会舍弃这种方法.
//
map["key"] = "value";
map.delete()
¶
通过键移除 map 的值:
map.delete("key");
或者
//
// 不建议使用这种方法
//
// 保留这种方法只是为了 @colyseus/schema 的版本向下兼容
// 未来会舍弃这种方法.
//
delete map["key"];
map.size
¶
返回 MapSchema
对象中元素的数量.
const map = new MapSchema<number>();
map.set("one", 1);
map.set("two", 2);
console.log(map.size);
// 输出: 2
map.forEach()
¶
以元素插入顺序迭代 map 中的键值对.
this.state.players.forEach((value, key) => {
console.log("key =>", key)
console.log("value =>", value)
});
State.players.ForEach((key, value) => {
Debug.Log(key);
Debug.Log(value);
})
state.players:each(function(value, key)
print(key, "=>")
pprint(value)
end)
for (key => value in state.players) {
trace(index + " => " + value);
}
Map 还有更多函数可用
详见 MDN 文档.
SetSchema¶
SetSchema
仅支持 JavaScript
目前 SetSchema
只能在 JavaScript 中使用. 尚不支持 Haxe, C#, LUA 和 C++ 客户端.
SetSchema
是一个基于 JavaScript 内置 Set 的可同步版本.
SetSchema
的用法和 [CollectionSchema
] 十分类似, 最大区别在于 Set 的值具有唯一性. Set 没有直接获取值的方法. (比如像 collection.at())
import { Schema, SetSchema, type } from "@colyseus/schema";
class Effect extends Schema {
@type("number") radius: number;
}
class Player extends Schema {
@type({ set: Effect }) effects = new SetSchema<Effect>();
}
const schema = require('@colyseus/schema');
const Schema = schema.Schema;
const SetSchema = schema.SetSchema;
class Effect extends Schema {
}
schema.defineTypes(Effect, {
radius: "number",
});
class Player extends Schema {
constructor () {
super();
this.effects = new SetSchema();
}
}
schema.defineTypes(Player, {
effects: { set: Effect }
});
set.add()
¶
为 SetSchema
添加元素.
const set = new SetSchema<number>();
set.add(1);
set.add(2);
set.add(3);
set.delete()
¶
按值删除元素.
set.delete("three");
set.has()
¶
检查集合中是否有该值.
if (set.has("two")) {
console.log("Exists!");
} else {
console.log("Does not exist!");
}
set.size
¶
返回 SetSchema
里元素的长度.
const set = new SetSchema<number>();
set.add(10);
set.add(20);
set.add(30);
console.log(set.size);
// 输出: 3
Set 还有更多函数可用
详见 MDN 文档.
CollectionSchema¶
CollectionSchema
仅支持 JavaScript
目前 CollectionSchema
只能在 JavaScript 中使用. 尚不支持 Haxe, C#, LUA 和 C++ 客户端.
CollectionSchema
的用法与 ArraySchema
类似, 需要注意的是, 它不具备某些数组可用的函数.
import { Schema, CollectionSchema, type } from "@colyseus/schema";
class Item extends Schema {
@type("number") damage: number;
}
class Player extends Schema {
@type({ collection: Item }) items = new CollectionSchema<Item>();
}
const schema = require('@colyseus/schema');
const Schema = schema.Schema;
const CollectionSchema = schema.CollectionSchema;
class Item extends Schema {
}
schema.defineTypes(Item, {
damage: "number",
});
class Player extends Schema {
constructor () {
super();
this.items = new CollectionSchema();
}
}
schema.defineTypes(Player, {
items: { collection: Item }
});
collection.add()
¶
为 CollectionSchema
添加元素.
const collection = new CollectionSchema<number>();
collection.add(1);
collection.add(2);
collection.add(3);
collection.at()
¶
获取 index
处的值.
const collection = new CollectionSchema<string>();
collection.add("one");
collection.add("two");
collection.add("three");
collection.at(1);
// 输出: "two"
collection.delete()
¶
按值删除元素.
collection.delete("three");
collection.has()
¶
检查集合中是否有该值.
if (collection.has("two")) {
console.log("Exists!");
} else {
console.log("Does not exist!");
}
collection.size
¶
返回 CollectionSchema
里元素的长度.
const collection = new CollectionSchema<number>();
collection.add(10);
collection.add(20);
collection.add(30);
console.log(collection.size);
// 输出: 3
collection.forEach()
¶
迭代 CollectionSchema
中的键值对, 以元素插入顺序.
collection.forEach((value, at) => {
console.log("at =>", at)
console.log("value =>", value)
});
每个客户端过滤数据¶
此功能为实验性质
@filter()
/ @filterChildren()
为实验性质, 可能不适合快节奏游戏.
过滤用来为指定客户端隐藏部分状态数据, 防止作弊, 防止玩家获取全部数据.
数据过滤器回调, 可以针对 每个客户端 的 每个字段 进行触发 (如果使用了 @filterChildren
, 还可针对每个子结构触发). 如果过滤器回调返回 true
, 则该字段数据将会发送给那个指定的客户端, 否则不发送.
请注意, 只有被过滤字段 (或其子字段) 数据更新时, 过滤器回调才能被触发. 要想手动触发请参考 此问题 里描述的方法.
@filter()
属性修饰器¶
@filter()
属性修饰器可作用于整个 Schema 字段.
下面展示了 @filter()
的函数签名:
class State extends Schema {
@filter(function(client, value, root) {
// client 参数是:
//
// 当前将要接受数据的客户端. 可以通过其
// client.sessionId, 及其他信息判定是否
// 要把数据同步给这个客户端.
// value 参数是:
// 被 @filter() 标记过滤的字段值
// root 参数是:
// 房间 Schema 实例引用. 方便在是否过滤的
// 决策过程中
// 访问房间状态.
})
@type("string") field: string;
}
const schema = require('@colyseus/schema');
class State extends schema.Schema {}
schema.defineTypes(State, {
field: "string"
});
schema.filter(function(client, value, root) {
// client 参数是:
//
// 当前将要接受数据的客户端. 可以通过其
// client.sessionId, 及其他信息判定是否
// 要把数据同步给这个客户端.
// value 参数是:
// 被 @filter() 标记过滤的字段值
// root 参数是:
// 房间 Schema 实例引用. 方便在是否过滤的
// 决策过程中
// 访问房间状态.
return true;
})(State.prototype, "field");
@filterChildren()
属性修饰器¶
@filterChildren()
属性修饰器可用于过滤掉 array, map, set等集合类型的内容. 它的签名与 @filter()
基本相同, 但是在 value
之前添加了 key
参数 - 表示 ArraySchema, MapSchema, CollectionSchema 等内容的索引.
class State extends Schema {
@filterChildren(function(client, key, value, root) {
// client 参数是:
//
// 当前将要接受数据的客户端. 可以通过其
// client.sessionId, 及其他信息判定是否
// 要把数据同步给这个客户端.
// key 参数是:
// 集合内容的当前索引
// value 参数是:
// 集合内容当前索引处的值
// root 参数是:
// 房间 Schema 实例引用. 方便在是否过滤的
// 决策过程中
// 访问房间状态.
})
@type([Cards]) cards = new ArraySchema<Card>();
}
const schema = require('@colyseus/schema');
class State extends schema.Schema {}
schema.defineTypes(State, {
cards: [Card]
});
schema.filterChildren(function(client, key, value, root) {
// client 参数是:
//
// 当前将要接受数据的客户端. 可以通过其
// client.sessionId, 及其他信息判定是否
// 要把数据同步给这个客户端.
// key 参数是:
// 集合内容的当前索引
// value 参数是:
// 集合内容当前索引处的值
// root 参数是:
// 房间 Schema 实例引用. 方便在是否过滤的
// 决策过程中
// 访问房间状态.
return true;
})(State.prototype, "cards");
示例: 在卡牌游戏中, 应该只有卡牌的持有者知道每个卡片的数据, 或者在特定条件下才能知道这些数据 (例如摊牌)
参考 @filter()
回调签名:
import { Client } from "colyseus";
class Card extends Schema {
@type("string") owner: string; // 用来保存卡牌持有者的 sessionId
@type("boolean") discarded: boolean = false;
/**
* 不要在 `@filter` 函数里使用箭头函数
* (会造成 `this` 指针丢失)
*/
@filter(function(
this: Card, // 定义 `@filter` 的类 (这里 this 就是 `Card` 的实例)
client: Client, // 要被过滤的客户端 `client` 实例
value: Card['number'], // 要被过滤的字段值. (这里是 `number` 字段的值)
root: Schema // 房间状态 Schema 实例
) {
return this.discarded || this.owner === client.sessionId;
})
@type("uint8") number: number;
}
const schema = require('@colyseus/schema');
class Card extends schema.Schema {}
schema.defineTypes(Card, {
owner: "string",
discarded: "boolean",
number: "uint8"
});
/**
* 不要在 `@filter` 函数里使用箭头函数
* (会造成 `this` 指针丢失)
*/
schema.filter(function(client, value, root) {
return this.discarded || this.owner === client.sessionId;
})(Card.prototype, "number");
客户端¶
C#, C++, Haxe
在使用强类型语言时, 需要基于 Typescript schema 定义手动生成客户端 schema 文件. 生成客户端 schema 的方法.
回调¶
服务器状态数据更新应用到客户端时, 会根据变更的类型自动触发本地实例上的回调.
回调通过房间状态实例触发. 使用前要确保该实例上已实现回调函数.
- onAdd (instance, key)
- onRemove (instance, key)
- onChange (changes) (触发于
Schema
实例) - onChange (instance, key) (触发于集合实例:
MapSchema
,ArraySchema
等等.) - listen()
onAdd (instance, key)
¶
只有集合 (MapSchema
, ArraySchema
等) 可以使用 onAdd
回调. 集合更新后触发 onAdd
回调, 外加已更新内容的键作为参数.
room.state.players.onAdd = (player, key) => {
console.log(player, "has been added at", key);
// 在游戏中加入player!
// 要想跟踪地图上物体的移动, 通常要这么做:
player.onChange = function(changes) {
changes.forEach(change => {
console.log(change.field);
console.log(change.value);
console.log(change.previousValue);
})
};
// 手动强制触发 "onChange"
player.triggerAll();
};
room.state.players['on_add'] = function (player, key)
print("player has been added at", key);
-- 在游戏中加入player!
-- 要想跟踪地图上物体的移动, 通常要这么做:
player['on_change'] = function(changes)
for i, change in ipairs(changes) do
print(change.field)
print(change.value)
print(change.previousValue)
end
end
-- 手动强制触发 "onChange"
player.trigger_all()
end
room.State.players.OnAdd += (Player player, string key) =>
{
Debug.Log("player has been added at " + key);
// 在游戏中加入player!
// 要想跟踪地图上物体的移动, 通常要这么做:
player.OnChange += (changes) =>
{
changes.ForEach((obj) =>
{
Debug.Log(obj.Field);
Debug.Log(obj.Value);
Debug.Log(obj.PreviousValue);
});
};
// 手动强制触发 "onChange"
e.Value.TriggerAll();
};
onRemove (instance, key)
¶
只有映射 (MapSchema
) 和数组 (ArraySchema
) 可以使用 onRemove
回调. 集合更新后触发 onRemove
回调, 外加已移除内容的键作为参数.
room.state.players.onRemove = (player, key) => {
console.log(player, "has been removed at", key);
// 从游戏中移除player!
};
room.state.players['on_remove'] = function (player, key)
print("player has been removed at " .. key);
-- 从游戏中移除player!
end
room.State.players.OnRemove += (Player player, string key) =>
{
Debug.Log("player has been removed at " + key);
// 从游戏中移除player!
};
onChange (changes:DataChange[])
¶
Schema
上的onChange
和集合上的不一样. 对于 集合结构(数组, 映射等)的onChange
请参考这里.
可以注册 onChange
以跟踪 Schema
实例属性的变更. onChange
的参数数组包含已变更的属性以及变更前的值.
room.state.onChange = (changes) => {
changes.forEach(change => {
console.log(change.field);
console.log(change.value);
console.log(change.previousValue);
});
};
room.state['on_change'] = function (changes)
for i, change in ipairs(changes) do
print(change.field)
print(change.value)
print(change.previousValue)
end
end
room.State.OnChange += (changes) =>
{
changes.ForEach((obj) =>
{
Debug.Log(obj.Field);
Debug.Log(obj.Value);
Debug.Log(obj.PreviousValue);
});
};
没与客户端同步过的状态上不能注册 onChange
回调.
onChange (instance, key)
¶
Schema
上的onChange
和集合上的不一样. 对于Schema
的onChange
请参考这里.
当集合里的 基本 类型 (string
, number
, boolean
等) 值更新时, 将触发此回调.
room.state.players.onChange = (player, key) => {
console.log(player, "have changes at", key);
};
room.state.players['on_change'] = function (player, key)
print("player have changes at " .. key);
end
room.State.players.OnChange += (Player player, string key) =>
{
Debug.Log("player have changes at " + key);
};
对于 非基本 类型 (各种 Schema
集合), 请先注册 onAdd
再注册 onChange
.
onChange
, onAdd
和 onRemove
是 互斥的
onChange
回调在 onAdd
或 onRemove
期间不会被触发.
如果想要跟踪的更新包括 onAdd
和 onRemove
, 请注册这两个回调.
.listen(prop, callback)
¶
侦听单个属性更新.
.listen()
目前仅可用于 JavaScript/TypeScript.
参数:
property
: 想要侦听更新的属性名称.callback
: 当property
更新时触发的回调.
state.listen("currentTurn", (currentValue, previousValue) => {
console.log(`currentTurn is now ${currentValue}`);
console.log(`previous value was: ${previousValue}`);
});
.listen()
返回的句柄可用于移除侦听器
const removeListener = state.listen("currentTurn", (currentValue, previousValue) => {
// ...
});
// 之后, 如果不需要侦听器了, 可以调用 `removeListener()` 来移除对 `"currentTurn"` 的侦听.
removeListener();
listen
和 onChange
有什么区别?
.listen()
方法是专为监听单个属性的 onChange
的简化版本. 下面是把 .listen()
写成 onChange
的样子:
state.onChange = function(changes) {
changes.forEach((change) => {
if (change.field === "currentTurn") {
console.log(`currentTurn is now ${change.value}`);
console.log(`previous value was: ${change.previousValue}`);
}
})
}
生成客户端 schema 的方法¶
schema-codegen
是一个转译工具, 用于把服务器端的 schema 定义文件转换为客户端可以使用的版本:
要在客户端正确解码 state, 客户端的 schema 定义文件必须与服务器端相兼容.
在使用 JavaScript SDK 时不必使用此工具
只有在客户端使用强类型语言, 如 C#, Haxe 等时, 才必须使用 schema-codegen
.
使用方法
要在终端查看使用方法, 先 cd
进入服务器目录, 然后运行以下命令:
npx schema-codegen --help
输出:
schema-codegen [path/to/Schema.ts]
Usage (C#/Unity)
schema-codegen src/Schema.ts --output client-side/ --csharp --namespace MyGame.Schema
Valid options:
--output: fhe output directory for generated client-side schema files
--csharp: generate for C#/Unity
--cpp: generate for C++
--haxe: generate for Haxe
--ts: generate for TypeScript
--js: generate for JavaScript
--java: generate for Java
Optional:
--namespace: generate namespace on output code
举例: Unity / C¶
下面是用 Unity 演示项目 生成 C# schema 文件的实例.
npx schema-codegen src/rooms/schema/* --csharp --output ../Assets/Scripts/States/"
generated: Player.cs
generated: State.cs
使用 npm
脚本:
方便起见, 推荐您把 schema-codegen
的参数保存在 package.json
文件中的 npm
脚本里:
"scripts": {
"schema-codegen": "schema-codegen src/rooms/schema/* --csharp --output ../Assets/Scripts/States/"
}
这样, 运行 npm run schema-codegen
, 就可以代替完整的命令:
npm run schema-codegen
generated: Player.cs
generated: State.cs
版本及向下/向上兼容¶
通过在现有结构末尾声明新字段, 可以实现向下/向上兼容, 不应删除先前的声明, 而是应该根据需要将其标记为 @deprecated()
. 下面是一个例子.
import { Schema, type, deprecated } from "@colyseus/schema";
class MyState extends Schema {
@type("string") myField: string;
}
import { Schema, type, deprecated } from "@colyseus/schema";
class MyState extends Schema {
// 标记此字段作废.
@deprecated() @type("string") myField: string;
// 保证不同客户端版本的服务器兼容.
@type("string") newField: string;
}
import { Schema, type, deprecated } from "@colyseus/schema";
class MyState extends Schema {
// 标记此字段作废.
@deprecated() @type("string") myField: string;
// 再次标记此字段作废.
@deprecated() @type("string") newField: string;
// 最新的字段总是保持在最下边
@type("string") anotherNewField: string;
}
这对于本地编译类语言很有用, 如 C#, C++, Haxe 等 - 即使这些客户端编译时没有最新的 schema 定义.
限制和最佳实践¶
- 每个
Schema
结构最多可以保存64
个字段. 如果需要更多字段, 可以嵌套使用Schema
结构. NaN
或null
数字被编码为0
null
字符串被编码为""
Infinity
数字被编码为Number.MAX_SAFE_INTEGER
- 不支持多维数组. 查看如何将一维数组作为多维数组使用
@colyseus/schema
编码顺序按照字段定义顺序.- 编码器 (服务器) 和解码器 (客户端) 都必须拥有相同的 schema 定义.
- 字段的顺序也要相同.
集合¶
集合类型 (ArraySchema
, MapSchema
等) 里的元素类型必须相同, 或者基类相同.
支持以下写法:
class Item extends Schema {/* 基类 Item */}
class Weapon extends Item {/* 武器类 */}
class Shield extends Item {/* 盾牌类 */}
class Inventory extends Schema {
@type({ map: Item }) items = new MapSchema<Item>();
}
const inventory = new Inventory();
inventory.set("left", new Weapon());
inventory.set("right", new Shield());