Search code examples
c#msgpack

Incorrect reflection after inheritance-involved serialization with MessagePack


I am serializing an Entity on a server:

    public class Entity
    {
        private List<Component> _components = new List<Component>();
        [Key(0)]
        public List<Component> Components {
            get {
                return _components;
            }
            set
            {
                _components = value;
            }
        }

        public T GetComponent<T>() where T : Component
        {
            foreach (Component component in _components)
            {
                var componentType = component.GetType();
                Debug.WriteLine(componentType);
                if (componentType.Equals(typeof(T)))
                {
                    return (T)component;
                }
            }
            return null;
        }
    }

    [MessagePackObject]
    public class Component
    {
        [IgnoreMember]
        public Entity entity;
    }

    [MessagePackObject]
    public class Tile : Component
    {
        public enum TileType
        {
            Soil,
            Mud,
        }

        [Key(0)]
        public TileType Type { get; set; }

        public Tile(TileType type)
        {
            Type = type;
        }
    }

Every Entity has a Tile (which inherits from Component). Before serializing I check the type of the Tile and it returns World2D.ECS.Components.Tile.

Then I send the serialized Entity to a client.

On the client I check reflection type of the component:

public T GetComponent<T>() where T : Component
        {
            foreach (Component component in _components)
            {
                var componentType = component.GetType();
                Debug.WriteLine(componentType); // returns World2D.ECS.Components.Component instead of World2D.ECS.Components.Tile as on the server
                if (componentType.Equals(typeof(T)))
                {
                    return (T)component;
                }
            }
            return null;
        }

After deserealizing Entity on the client, reflection returns World2D.ECS.Components.Component instead of World2D.ECS.Components.Tile.

Update 1: Confirmed that this doesn't occur due to sending the serialized Entity over the network because the bug occurs even if serializing and deserializing on the same machine.


Solution

  • https://github.com/neuecc/MessagePack-CSharp/issues/1405

    By default, type information is not included in the messagepack stream. The best way to add type information is to apply the [Union] attribute as described in the readme. You might also find the Typeless resolver can solve your problem, but that comes with security implications and a larger data size.