Skip to content

Commit 8f0bdfa

Browse files
committed
StateView: using view.add() before instance is set to state throws error
1 parent ceb2ad9 commit 8f0bdfa

File tree

3 files changed

+51
-4
lines changed

3 files changed

+51
-4
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@colyseus/schema",
3-
"version": "3.0.23",
3+
"version": "3.0.24",
44
"description": "Binary state serializer with delta encoding for games",
55
"bin": {
66
"schema-codegen": "./bin/schema-codegen",

src/encoder/StateView.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,30 @@ export class StateView {
4242

4343
// TODO: allow to set multiple tags at once
4444
add(obj: Ref, tag: number = DEFAULT_VIEW_TAG, checkIncludeParent: boolean = true) {
45-
if (!obj?.[$changes]) {
45+
const changeTree: ChangeTree = obj?.[$changes];
46+
47+
if (!changeTree) {
4648
console.warn("StateView#add(), invalid object:", obj);
4749
return this;
50+
51+
} else if (
52+
!changeTree.parent &&
53+
changeTree.refId !== 0 // allow root object
54+
) {
55+
/**
56+
* TODO: can we avoid this?
57+
*
58+
* When the "parent" structure has the @view() tag, it is currently
59+
* not possible to identify it has to be added to the view as well
60+
* (this.addParentOf() is not called).
61+
*/
62+
throw new Error(
63+
`Cannot add a detached instance to the StateView. Make sure to assign the "${changeTree.ref.constructor.name}" instance to the state before calling view.add()`
64+
);
4865
}
4966

5067
// FIXME: ArraySchema/MapSchema do not have metadata
5168
const metadata: Metadata = obj.constructor[Symbol.metadata];
52-
const changeTree: ChangeTree = obj[$changes];
5369
this.visible.add(changeTree);
5470

5571
// add to iterable list (only the explicitly added items)

test/StateView.test.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,38 @@ describe("StateView", () => {
3030
assertEncodeAllMultiple(encoder, state, [client1, client2])
3131
});
3232

33-
it("should allow adding detached instances to the view (not immediately attached)", () => {
33+
it("should not be required to add parent structure", () => {
34+
/**
35+
* TODO/FIXME: this test is asserting to throw an error, but it should
36+
* actually work instead.
37+
*/
38+
39+
class Item extends Schema {
40+
@type("number") amount: number;
41+
}
42+
43+
class State extends Schema {
44+
@type("string") prop1 = "Hello world";
45+
@view() @type([Item]) items = new ArraySchema<Item>();
46+
}
47+
48+
const state = new State();
49+
const encoder = getEncoder(state);
50+
51+
const client1 = createClientWithView(state);
52+
53+
const item = new Item().assign({ amount: 0 });
54+
assert.throws(() => {
55+
client1.view.add(item);
56+
}, /Make sure to assign the "Item" instance to the state before calling view.add/);
57+
58+
// state.items.push(item);
59+
// encodeMultiple(encoder, state, [client1]);
60+
// assert.strictEqual(client1.state.items.length, 1);
61+
// assertEncodeAllMultiple(encoder, state, [client1])
62+
});
63+
64+
xit("should allow adding detached instances to the view (not immediately attached)", () => {
3465
class Item extends Schema {
3566
@type("number") amount: number;
3667
}

0 commit comments

Comments
 (0)