GitHub Documentation

Best way to represent inheritance and optionals together in Colyseus



  • Hi all,

    First, some code to explain what I want:

    class Character extends Schema {
        @type([ Item ])
        items: new ArraySchema<Item>();;
    }
    

    Imagine we have this character class. I want a character to be able to hold 0+ Items. An item would ideally be a superclass, with some subclasses, like this:

    class Item extends Schema {
    }
    
    class Bucket extends Item {
    }
    
    class Hose extends Item {
    }
    

    What is the recommended way to do this? The problem with this approach is I would need some kind of client side check to determine which kind of item I'm holding. I'm also not sure this is possible at all, since the docs say "You can't mix types inside arrays." (here). If this was Rust for example, I'd probably have a bunch of items that impl an Item trait.

    Another thing I would reach for in another language is enums. In Rust (again) for example you can have enums that have different values for each enum variant. This would be good for this case, as well as for representing optionals.

    How have you managed to do this kind of thing? Would you just have n properties, one for each type of item?

    As a related point, imagine also I want it to be possible for it to just be 0 or 1. I see there are no optionals, how would I do this? Do you just use an ArraySchema for this?

    Thanks!


  • administrator

    Hi @banool, sorry for the delayed response!

    Inheritance is supported on ArraySchema and MapSchema. So the approach of having an Item as base class works! (I'm using a MapSchema for this, to be able to add/remove directly by key rather than a moving index, see mazmorra.io sources)

    In order to be able to differentiate the instances on the client-side - if you are using JavaScript/TypeScript, you can share the actual Schema implementation with the client, and provide its concrete implementation as a third parameter for the matchmaking method:

    // this is the same RoomState as you have in the server
    class RoomState extends Schema {}
    
    const room = await client.joinOrCreate("my_room", {}, RoomState);
    

    This way, the actual instances of Bucket, Hose, and whatnot are going to be created during decoding time.

    Hope this helps, let me know if you have any questions!

    Cheers,
    Endel



  • Hi @endel,

    Thanks that's great to hear! My client side is in lua (Defold) but I assume there is something I can check at runtime to differentiate them, I'll just take a look at the messages I receive.

    Thanks!


 

© 2021 Lucid Sight, Inc