Same Schema object in multiple ArraySchema not possible?

Hi all/Endel,

I'm experimenting with Colyseus to recreate a boardgame to play with my mother who's in isolation now (to learn and do something useful at the same time).

For convenience, I'm tracking stacks of Card objects (subclasses of Schema) in 3 seperate ArraySchema<Card>s representing card stacks within my state (which I use to track the order of the cards, and to be able to shuffle them), and I have the same Card objects (all of them) in a MapSchema<Card>. Note: This is all for convenience to represent the real world version of the boardgame and not optimized for performance or minimal overhead etc.

When I move the top Card from one ArraySchema to another using
ArraySchema_1.push(ArraySchema_2.pop()), the client gets an OnRemove for ArraySchema_2 with a proper reference to the Card I removed. However the OnAdd for ArraySchema_1, while also triggered in the same update, seems to have an 'empty' Card reference, i.e. as if I executed ArraySchema_1.push(new Card()).

When I manipulate the Card my MapSchema<Card> does not trigger an OnChange in the client.

In pseudo-code (not a working example):

/**
* Handles an incoming cardClick event, where I pass the name of the card clicked from the received Message.
*/
public handleCardClick(name: string) : void {
const card: Card = allCards_MapSchema[name]; //Gets the card that the user clicked on - this should be the top card from a stack, and it is when I check on the server.

const topCard = Stack_1_ArraySchema.pop(); // onRemove triggered as expected Client side
// The next test returns true in my case, I've tested this on the server: the MapSchema and Arrayschema thus contain a reference to the same object
// or at least equal-enough to be considered '==='. 
if (card === topCard) 
{
    card.name = 'new Name'; // Should trigger onChange on the MapSchema, but does not
    Stack_2_ArraySchema.push(topCard); // should trigger onAdd with the Card on the ArraySchema, but effect is more as if Stack_2_ArraySchema.push(new Card()); was executed, but actually (topCard !== new Card()).
}
}

In short: On the server I do see that the Card is moved as expected from one Array to the other, but it's not being sent to the clients.

Does this sound as known/expected behavior? I can try to make an example after work today.

Cheers!

@jelle any chance your mapschema are using numbers as indexes?

Could it be this? https://github.com/colyseus/colyseus/issues/320

@krazyjakee I saw that and no, it's currently an uuid (string) which includes dashes and letters.

However, a uuid often starts with a number 🤔 have you tested "your" bug too for the case there's a string that starts with a number? I see you work around by prepending a letter - wonder if it also works if you append a letter.

@jelle I added a test to prepend numbers and can confirm that it works as expected and does not fail.

@krazyjakee In the meantime I've changed my code (and ran into a different potential bug with the Unity client: https://github.com/colyseus/colyseus-unity3d/issues/114) and I can confirm all works fine when I use a single MapSchema to track all my Card objects (using UUID's as the 'name' of the card which I also use in the MapSchema).

I've added properties to the Card that track 1) in which 'stack' the card is and 2) what the position in the stack is. This is of course exactly what Arrays do wel so would prefer to be able to use them for coding convenience :) I'll probably write a small class that behaves array-like exposing pop and push methods, but and that also sets these properties on the Card objects.

Hi @jelle, thanks for reporting, you mind creating an issue on the @colyseus/schema issue tracker? (https://github.com/colyseus/schema/issues)

This sounds like a bug on ArraySchema indeed. Probably the internal card.$changes is not being updated after array.push(). Or maybe you have multiple references of the same Card on multiple arrays - which will indeed cause synchronization issues.

You can have only one reference of a Schema object in the state, if you need more copies to the same object, you'd need to call schema.clone().

If you happen to have a reproducible scenario it would be really helpful. Cheers!

@endel said in Same Schema object in multiple ArraySchema not possible?:

Or maybe you have multiple references of the same Card on multiple arrays - which will indeed cause synchronization issues

Thansk for replying Endel. That is likely to be part of the problem then - it's perhaps more of a documentation 'issue' in that case (I don't think this is mentioned in the Schema documentation, or I've missed it https://docs.colyseus.io/state/schema/).

Indeed I had all Card objects in the MapSchema, and a second reference to that same Card object in an ArraySchema too.

I've not tested the ArraySchema_1.push(ArraySchema_2.pop()) 'in isolation' (without the MapSchema), if I find some time I'll try and test that.