schema icon indicating copy to clipboard operation
schema copied to clipboard

MapSchema string field undefined set error

Open NoahHahm opened this issue 1 year ago • 3 comments

An error occurs when inserting undefined into a string field in MapSchema. There was no problem until 1.0.38, but after the latest update, a refId error occurred in the client; If you simply insert undefined into the string field in the Schema Object when using MapSchema, an error occurs.

items: MapSchema<Expedition>;

class Expedition {
  pet1: string;
}

expedition.pet1 =  undefined;

items.set('test', expedition);

NoahHahm avatar Nov 02 '22 10:11 NoahHahm

Error: a 'string' was expected, but 'undefined' was provided in current#pet1
    at new EncodeSchemaError (/usr/src/app/node_modules/@colyseus/schema/build/cjs/index.js:2232:42)
    at assertType (/usr/src/app/node_modules/@colyseus/schema/build/cjs/index.js:2266:15)
    at encodePrimitiveType (/usr/src/app/node_modules/@colyseus/schema/build/cjs/index.js:2275:5)
    at current.Schema.encode (/usr/src/app/node_modules/@colyseus/schema/build/cjs/index.js:2686:21)
    at SchemaSerializer.applyPatches (/usr/src/app/lib/applications/colyseus/Serializer.js:41:40)

@endel are there any recent undefined issues? there were no problems until version 1.0.38.

NoahHahm avatar Nov 07 '22 13:11 NoahHahm

this seems related to our issue. undefined gets changed to {} This test fails

    test('set schema and change', () => {
        class OptionalSubScheam extends Schema {
            constructor() {
                super();
                this.index = 200;
                this.my_string = 'a good string';
            }
            @type('number')  index: number;
            @type('string')  my_string: string;
        }

        class Test extends Schema {
            constructor() {
                super();
                this.size = 0;
            }
            @type('number') size: number; // total number of storage slots in this container.
                    
            @type('boolean') transient?: boolean;
          
            @type(OptionalSubScheam) sub?: OptionalSubScheam;
        }
        
        const testobj = new Test();
        const encoded = testobj.encodeAll(false);
        const handshake = Reflection.encode(testobj);


        // serializer from client libs
        const serializer = new SchemaSerializer<Test>();
        serializer.handshake(handshake);
        serializer.setState(encoded);

        const clientobj = serializer.getState();
        expect(clientobj.sub).toBeUndefined();
    });

hunkydoryrepair avatar Jul 12 '23 18:07 hunkydoryrepair

Hi @hunkydoryrepair, your scenario is an intended behavior, in order to simplify listening on direct references on the root structure, such as: room.state.sub.listen("index", () => {}).

When structures are "reflected" in the client, every non-primitive structure in the root level is auto-initialized, see: https://github.com/colyseus/schema/blob/185e332c37fc9f2dc717c04d6ec025b948f79c57/src/Reflection.ts#L143-L155

I agree that at the data level, it would be more intuitive if it starts as undefined - the problem is we don't have an API for listening to direct schema "additions" like this. I'm open to a proposal here.


@NoahHahm your case seems different but the reproduction scenario seems incomplete. I'm not sure how to reproduce it. Could you provide a full test scenario like @hunkydoryrepair did? Cheers!

endel avatar Jul 13 '23 00:07 endel