Contents of FixedList is not properly serialized/deserialized

Given the following structure, the contents of LeafsByPlayer will not be properly serialized or deserialized.
On the client, it will have the correct number of items, however these items will all be initialized to defaults.

In the example case, the PlayerId will be 0 and the Leafs list will be empty.

    [GhostComponent(PrefabType = GhostPrefabType.All)]
    public struct OuterList : IComponentData
    {
        [GhostField]
        public FixedList512Bytes<InnerList> LeafsByPlayer;
    }
    public struct InnerList
    {
        public ulong PlayerId;
        public FixedList32Bytes<Leaf> Leafs;
    }
    public struct Leaf
    {
        ...
    }
  
    var outer = SystemAPI.GetSingletonRW<OuterList>();
    outer.ValueRW.LeafsByPlayer = new InnerList
    {
        PlayerId = 42,
        Leafs = new FixedList32Bytes<Leaf>
        {
            new Leaf()
        }
    }

FixedList are not supported yet and does not serialise correctly. It is known issue.

That is unfortunate. I also found it missing from [this list](http://Supported Types Inside the package, we have default templates for how to generate serializers (called the “default serializers”) for a limited set of types:) in the mean time. Any plans to support this and if so, any ETA?
As an alternative I suppose an RPC with a custom serializer could be the answer.

See similar thread with custom serializer: RPC FixedList512Bytes<int> 0 on receive

1 Like

Using an RPC with a custom serializer worked for our use case, and even though the documentation is actually quite good for this, I’ll add our example for reference:

[BurstCompile]
public struct LevelUpRewardOfferRequest : IComponentData, IRpcCommandSerializer<LevelUpRewardOfferRequest>
{
    public FixedList32Bytes<LevelUpReward> RewardsOnOffer;
    private static readonly PortableFunctionPointer<RpcExecutor.ExecuteDelegate> Executor = new(InvokeExecute);
    public static readonly ComponentTypeSet SendRequestComponentSet = new(typeof(LevelUpRewardOfferRequest), typeof(SendRpcCommandRequest));
    public void Serialize(ref DataStreamWriter writer, in RpcSerializerState state, in LevelUpRewardOfferRequest data)
    {
        // length|[Type|Id]*
        var offer = data.RewardsOnOffer;
        writer.WriteByte((byte)offer.Length);
        foreach (var reward in offer)
        {
            writer.WriteByte(reward.Id.Value);
            writer.WriteByte((byte)reward.Type);
        }
    }
    public void Deserialize(ref DataStreamReader reader, in RpcDeserializerState state, ref LevelUpRewardOfferRequest data)
    {
        var rewardCount = reader.ReadByte();
        for (var i = 0; i < rewardCount; i++)
        {
            var reward = new LevelUpReward
            {
                Id = ByteId.From(reader.ReadByte()),
                Type = (LevelUpRewardType)reader.ReadByte()
            };
            data.RewardsOnOffer.Add(reward);
        }
    }
    public PortableFunctionPointer<RpcExecutor.ExecuteDelegate> CompileExecute() => Executor;
    [BurstCompile(DisableDirectCall = true)]
    private static void InvokeExecute(ref RpcExecutor.Parameters parameters)
    {
        RpcExecutor.ExecuteCreateRequestComponent<LevelUpRewardOfferRequest, LevelUpRewardOfferRequest>(ref parameters);
    }
}
1 Like