diff --git a/package.json b/package.json index 3eebe1e0..2485c286 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "generate-test-10": "bin/schema-codegen test-external/Callbacks.ts --namespace SchemaTest.Callbacks --output ../colyseus-unity-sdk/Assets/Colyseus/Tests/Editor/ColyseusTests/Schema/Callbacks", "generate-test-11": "bin/schema-codegen test-external/MapSchemaMoveNullifyType.ts --namespace SchemaTest.MapSchemaMoveNullifyType --output ../colyseus-unity-sdk/Assets/Colyseus/Tests/Editor/ColyseusTests/Schema/MapSchemaMoveNullifyType", "generate-test-12": "bin/schema-codegen test-external/ArraySchemaClear --namespace SchemaTest.ArraySchemaClear --output ../colyseus-unity-sdk/Assets/Colyseus/Tests/Editor/ColyseusTests/Schema/ArraySchemaClear", - "prepublishOnly": "npm run build" + "prepare": "npm run build" }, "files": [ "src", diff --git a/src/encoder/ChangeTree.ts b/src/encoder/ChangeTree.ts index 268e3705..7330957a 100644 --- a/src/encoder/ChangeTree.ts +++ b/src/encoder/ChangeTree.ts @@ -568,7 +568,7 @@ export class ChangeTree { || fieldHasViewTag; // - // "isFiltered" may not be imedialely available during `change()` due to the instance not being attached to the root yet. + // "isFiltered" may not be immediately available during `change()` due to the instance not being attached to the root yet. // when it's available, we need to enqueue the "changes" changeset into the "filteredChanges" changeset. // if (this.isFiltered) { @@ -576,8 +576,7 @@ export class ChangeTree { this.isVisibilitySharedWithParent = ( parentChangeTree.isFiltered && typeof (refType) !== "string" && - !fieldHasViewTag && - parentIsCollection + !fieldHasViewTag ); if (!this.filteredChanges) { diff --git a/test/Schema.ts b/test/Schema.ts index 1d12c31f..e65a9c3e 100644 --- a/test/Schema.ts +++ b/test/Schema.ts @@ -1,6 +1,6 @@ import * as assert from "assert"; -import { Schema, type, ArraySchema, MapSchema, Reflection, Iterator, StateView } from "../src"; +import { Schema, type, ArraySchema, MapSchema, Reflection, Iterator, StateView, view } from "../src"; import { Decoder } from "../src/decoder/Decoder"; import { Encoder } from "../src/encoder/Encoder"; import { CallbackProxy, getDecoderStateCallbacks, SchemaCallbackProxy } from "../src/decoder/strategy/getDecoderStateCallbacks"; @@ -249,6 +249,16 @@ export class Position extends Schema { } } +export class InheritanceParent extends Schema { + @type(Position) standardChild: Position | undefined = undefined; + @view() @type(Position) viewChild: Position | undefined = undefined; + @type([Position]) arrayChild: ArraySchema = new ArraySchema(); +} + +export class InheritanceRoot extends Schema { + @view() @type(InheritanceParent) parent = new InheritanceParent(); +} + export class Another extends Schema { @type(Position) position: Position = new Position(0, 0, 0); } diff --git a/test/StateView.test.ts b/test/StateView.test.ts index b6ba79f5..c123e05d 100644 --- a/test/StateView.test.ts +++ b/test/StateView.test.ts @@ -1,7 +1,7 @@ import * as assert from "assert"; import * as util from "util"; import { Schema, type, view, ArraySchema, MapSchema, StateView, Encoder, ChangeTree, $changes, OPERATION, SetSchema, CollectionSchema } from "../src"; -import { createClientWithView, encodeMultiple, assertEncodeAllMultiple, getDecoder, getEncoder, createInstanceFromReflection, encodeAllForView, encodeAllMultiple, assertRefIdCounts } from "./Schema"; +import { createClientWithView, encodeMultiple, assertEncodeAllMultiple, getDecoder, getEncoder, createInstanceFromReflection, encodeAllForView, encodeAllMultiple, assertRefIdCounts, InheritanceRoot, Position } from "./Schema"; import { nanoid } from "nanoid"; describe("StateView", () => { @@ -2595,4 +2595,85 @@ describe("StateView", () => { }); }); + it("should encode nested Schema", () => { + const state = new InheritanceRoot(); + const encoder = getEncoder(state); + + const client = createClientWithView(state); + client.view.add(state.parent); + + // Initial encode: child property is undefined + encodeMultiple(encoder, state, [client]); + + // Assign a child Schema instance. + state.parent.standardChild = new Position(1, 2, 3); + + /** + * Encode an assignment of a child field: + * Its fields should be visible to the client, because it inherits visibility from its parent. + */ + encodeMultiple(encoder, state, [client]); + + assert.notStrictEqual(client.state.parent, undefined); + assert.notStrictEqual(client.state.parent.standardChild, undefined); + assert.strictEqual(client.state.parent.standardChild.x, 1); + assert.strictEqual(client.state.parent.standardChild.y, 2); + assert.strictEqual(client.state.parent.standardChild.z, 3); + }); + + it("should not encode nested Schema with @view", () => { + const state = new InheritanceRoot(); + const encoder = getEncoder(state); + + const client = createClientWithView(state); + client.view.add(state.parent); + + // Initial encode: child property is undefined + encodeMultiple(encoder, state, [client]); + + // Assign a child Schema that uses @view + state.parent.viewChild = new Position(1, 2, 3); + + /** + * Encode an assignment of a child field: + * the child "viewChild" field (Position) is marked with @view, + * so it does not share visibility with its parent, and + * its fields are not encoded for the client. + */ + encodeMultiple(encoder, state, [client]); + + assert.notStrictEqual(client.state.parent, undefined); + assert.notStrictEqual(client.state.parent.viewChild, undefined); + assert.strictEqual(client.state.parent.viewChild.x, undefined); + assert.strictEqual(client.state.parent.viewChild.y, undefined); + assert.strictEqual(client.state.parent.viewChild.z, undefined); + }); + + it("should encode nested Schema wrapped in ArraySchema", () => { + const state = new InheritanceRoot(); + const encoder = getEncoder(state); + + const client = createClientWithView(state); + client.view.add(state.parent); + + // Initial encode: child property is undefined + encodeMultiple(encoder, state, [client]); + + // Assign a child Schema wrapped in an ArraySchema, to demonstrate this workaround. + state.parent.arrayChild.push(new Position(1, 2, 3)); + + /** + * Encode an assignment of a child field wrapped in an ArraySchema + * the child "arrayChild" field (Position) shares visibility with its parent, + * and its fields are encoded for the client. + */ + encodeMultiple(encoder, state, [client]); + + assert.notStrictEqual(client.state.parent, undefined); + assert.strictEqual(client.state.parent.arrayChild.length, 1); + assert.strictEqual(client.state.parent.arrayChild[0].x, 1); + assert.strictEqual(client.state.parent.arrayChild[0].y, 2); + assert.strictEqual(client.state.parent.arrayChild[0].z, 3); + }); + });