Skip to content

AccessViolationException when serializing two worlds, but deserializing in different order #154

Open
@solonrice

Description

@solonrice

Thank you for implementing the serialization! I recently had a chance to try it out and found an issue when using multiple worlds. The TLDR is basically this:

If you create two worlds and add an entity to each (with a component), serialize both worlds, then deserialize them later in a different order and in a different process, an AccessViolationException is thrown in the MessagePack formatters.

It looks like the exception is thrown almost every time in the ChunkFormatter implementation when the chunk array is setup for the first component type.

Here is some code to help reproduce it:

    [Fact]
    public void TestSerialized()
    {
        // Prove compression works
        Assert.Equal("Compression is working", Encoding.UTF8.GetString(Decompress(Compress(Encoding.UTF8.GetBytes("Compression is working")))));
        
        // Set up binary serializer with no extra formatters
        var binarySerializer = new ArchBinarySerializer();
        
        // Make two worlds, adding entities with different components to each (doesnt seem to matter which two types, just that they are different).
        var worldA = World.Create();
        worldA.Create(12);
        var worldB = World.Create();
        worldB.Create(13f);
        
        // Serialize both worlds in order (A, B).
        var serializedA = Convert.ToBase64String(Compress(binarySerializer.Serialize(worldA)));
        var serializedB = Convert.ToBase64String(Compress(binarySerializer.Serialize(worldB)));

        // Output the compressed strings for copy/paste into deserialize method.
        _testOutputHelper.WriteLine(serializedA);
        _testOutputHelper.WriteLine("-=-=-=-=-");
        _testOutputHelper.WriteLine(serializedB);
    }

    [Fact]
    public void TestDeserialized()
    {
        // Set up a binary serializer again
        var binarySerializer = new ArchBinarySerializer();
        
        // Paste in the two compressed serialized world strings
        var serializedA = "7dlBaoNAFIDhF4jQRQ7hAURCi5gsssqqtItASvdqH0ViFMax4DH0YFlkV/AuVm16hS7i//z5cEAYmJ1M0z3IomcYhmEYZm7TdktpRdrhjYiIiIiIZtD4C8A5EBERERHNpouILJrJ789jXVo9+8+5fXr03NvqYNKvyKq/L4y+prHnvqsp0yLfhf56fDx3X2W2MrrLtbImyjz3UMVZmrxo/VacdPgw0WQTfISxRmG43ei427LtZdr1DyfonOA6LAQAAAAAAAAAAAAAAADuj/+8i/q9dlpx7AAAAAAAAAAAAAAAAPfHDw==";
        var serializedB = "7dlBasJAGIbh32Kgx8gBQnATYhcuiktdBBT3SfrTDo0JjIngMYy3cuHCXSF3STOlvUIX5v18eXBAGMgueO6eZdIzxhhjbGxru6m0Iu3wjYiIiIiIRpB7BeA5EBERERGNppuITM7Op6+PzelQ6z7cmPK90MD/PSbWHNNaw2VldW2ywN+pPZiqXMThzH0Cf9kUdWN1UWpT27QI/KTJCpOv9LStPnX4Ya75PHqLM03j+GWu7rrppe/l5/Y/vKjzovtwEAAAAAAAAAAAAAAAAHg8/vXfKC+6viYiV3cxAAAAAAAAAAAAAAAAPBTf";

        // Deserialize in different order (B, A). Will throw on first one.
        var worldB = binarySerializer.Deserialize(Decompress(Convert.FromBase64String(serializedB)));
        Assert.True(worldB.Size > 0);
        
        var worldA = binarySerializer.Deserialize(Decompress(Convert.FromBase64String(serializedA)));
        Assert.True(worldA.Size > 0);
    }
    
    private static byte[] Compress(byte[] data)
    {
        using var output = new MemoryStream();
        using (var dstream = new DeflateStream(output, CompressionLevel.SmallestSize)) {
            dstream.Write(data, 0, data.Length);
        }
        return output.ToArray();
    }
    
    private static byte[] Decompress(byte[] data)
    {
        using var input = new MemoryStream(data);
        using var output = new MemoryStream();
        using (var dstream = new DeflateStream(input, CompressionMode.Decompress)) {
            dstream.CopyTo(output);
        }
        return output.ToArray();
    }

You can simply run the TestDeserialized() method, which has the encoded world already there. But of course, prove it out completely by running TestSerialized(), copying the output of each world (A and B) to the TestDeserialized() method, then running that method to show the AccessViolationException.

Thanks again for an awesome ECS!

EDIT: This also happens with the ArchJsonSerializer, for what it's worth.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Arch.ExtendedThis feature targets Arch.ExtendedbugSomething isn't working

    Projects

    Status

    Todo

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions