public void Execute(DynamicComponentTypeHandle *ghostChunkComponentTypesPtr,
                                int ghostChunkComponentTypesLength)
            {
                for (int i = 0; i < GhostChunks.Length; i++)
                {
                    ArchetypeChunk chunk    = GhostChunks[i];
                    var            entities = chunk.GetNativeArray(EntityTypeHandle);
                    var            buffers  = chunk.GetBufferAccessor(SnapshotBufferTypeHandle);
                    var            datas    = chunk.GetNativeArray(SnapshotComponentTypeHandle);
                    var            ghosts   = chunk.GetNativeArray(GhostComponentTypeHandle);

                    for (int ent = 0; ent < entities.Length; ent++)
                    {
                        GhostComponent ghostComponent = ghosts[ent];
                        var            typeState      = GhostTypeCollection[ghostComponent.GhostType];

                        SnapshotData snapshotData = datas[ent];
                        DynamicBuffer <SnapshotDataBuffer> snapshotDataBuffer = buffers[ent];

                        if (snapshotData.SnapshotSize == 0)
                        {
                            continue;
                        }

                        bool isGet = snapshotData.GetDataAtTick(snapshotDataBuffer, InterpolatedTargetTick,
                                                                InterpolatedTargetTickFraction,
                                                                out var dataAtTick);

                        int baseOffset        = typeState.FirstComponent;
                        int numBaseComponents = typeState.NumComponents;

                        // 跳过Tick
                        int offset = GlobalConstants.TickSize;

                        for (int j = 0; j < numBaseComponents; j++)
                        {
                            int compIdx = ComponentIndex[baseOffset + j].ComponentIndex;
                            GhostComponentSerializer   serializer  = ComponentSerializers[compIdx];
                            DynamicComponentTypeHandle dynamicType =
                                ghostChunkComponentTypesPtr[compIdx];

                            if (chunk.Has(dynamicType))
                            {
                                IntPtr compPtr = (IntPtr)chunk
                                                 .GetDynamicComponentDataArrayReinterpret <byte>(dynamicType,
                                                                                                 serializer.ComponentSize).GetUnsafePtr();

                                if (isGet)
                                {
                                    serializer.CopyFromSnapshot.Ptr.Invoke(
                                        compPtr + serializer.ComponentSize * ent,
                                        (IntPtr)(&dataAtTick), offset);
                                }
                            }

                            offset += serializer.DataSize;
                        }
                    }
                }
            }
        protected override unsafe void OnUpdate()
        {
            var session  = GetSingletonEntity <CommandTargetComponent>();
            var inBuffer = EntityManager.GetBuffer <IncomingSnapshotDataStreamBufferComponent>(session);

            var reader = inBuffer.AsDataStreamReader();

            if (reader.Length == 0)
            {
                return;
            }

#if UNITY_EDITOR || DEVELOPMENT_BUILD
            NetDebug.SnapMS     = reader.Length;
            NetDebug.DownCount += reader.Length;
#endif

            uint serverTick = reader.ReadUInt();

            var ack = EntityManager.GetComponentData <NetworkSnapshotAckComponent>(session);
            if (ack.IsOldWithLastReceivedSnapshotByLocal(serverTick))
            {
                return;
            }
            ack.UpdateLocalValues(serverTick);
            PostUpdateCommands.SetComponent(session, ack);

            uint destroyLen = reader.ReadUInt();
            uint len        = reader.ReadUInt();

            for (int i = 0; i < destroyLen; i++)
            {
                int ghostId = reader.ReadPackedInt(_networkCompressionModel);
                if (!_ghostEntityMap.TryGetValue(ghostId, out Entity ent))
                {
                    continue;
                }

                var s = new GhostDespawnSystem.DelayedDespawnGhost
                {
                    Ghost = new SpawnedGhost
                    {
                        GhostId   = ghostId,
                        SpawnTick = EntityManager.GetComponentData <GhostComponent>(ent).SpawnTick
                    },
                    Tick = serverTick
                };

                _ghostEntityMap.Remove(ghostId);
                if (EntityManager.HasComponent <GhostPredictionComponent>(ent))
                {
                    _ghostDespawnSystem.AddToPredicted(s);
                }
                else
                {
                    _ghostDespawnSystem.AddToInterpolated(s);
                }
            }

            var ghostSerializerCollectionSystem = World.GetExistingSystem <GhostCollectionSystem>();

            var ghostSpawnEntity      = GetSingletonEntity <GhostSpawnQueueComponent>();
            var bufferFromEntity      = GetBufferFromEntity <SnapshotDataBuffer>();
            var spawnBufferFromEntity = GetBufferFromEntity <GhostSpawnBuffer>();
            var compFromEntity        = GetComponentDataFromEntity <SnapshotData>();

            for (int i = 0; i < len; i++)
            {
                int ghostType = reader.ReadPackedInt(_networkCompressionModel);
                int ghostId   = reader.ReadPackedInt(_networkCompressionModel);

                if (ghostType < 0 || ghostType >= ghostSerializerCollectionSystem.GhostTypeCollection.Length)
                {
                    throw new Exception($"GhostRecvSystem:GhostType={ghostType}, GhostId={ghostId}");
                }

                // 序列化组件信息
                var typeState         = ghostSerializerCollectionSystem.GhostTypeCollection[ghostType];
                var baseOffset        = typeState.FirstComponent;
                var numBaseComponents = typeState.NumComponents;

                byte *snapshotData;
                DynamicBuffer <SnapshotDataBuffer> snapshotDataBuffer;
                SnapshotData snapshotDataComponent;

                bool existingGhost = _ghostEntityMap.TryGetValue(ghostId, out Entity gent);

                if (existingGhost && bufferFromEntity.HasComponent(gent) &&
                    compFromEntity.HasComponent(gent))
                {
                    snapshotDataBuffer                = bufferFromEntity[gent];
                    snapshotData                      = (byte *)snapshotDataBuffer.GetUnsafePtr();
                    snapshotDataComponent             = compFromEntity[gent];
                    snapshotDataComponent.LatestIndex =
                        (snapshotDataComponent.LatestIndex + 1) % GlobalConstants.SnapshotHistorySize;
                    compFromEntity[gent] = snapshotDataComponent;
                }
                else
                {
                    var ghostSpawnBuffer = spawnBufferFromEntity[ghostSpawnEntity];
                    snapshotDataBuffer = bufferFromEntity[ghostSpawnEntity];
                    var snapshotDataBufferOffset = snapshotDataBuffer.Length;
                    ghostSpawnBuffer.Add(new GhostSpawnBuffer
                    {
                        GhostType       = ghostType,
                        GhostId         = ghostId,
                        ClientSpawnTick = serverTick,
                        ServerSpawnTick = serverTick,
                        DataOffset      = snapshotDataBufferOffset
                    });

                    snapshotDataBuffer.ResizeUninitialized(snapshotDataBufferOffset + typeState.SnapshotSize);
                    snapshotData = (byte *)snapshotDataBuffer.GetUnsafePtr() + snapshotDataBufferOffset;
                    UnsafeUtility.MemClear(snapshotData, typeState.SnapshotSize);
                    snapshotDataComponent = new SnapshotData
                    {
                        SnapshotSize = typeState.SnapshotSize,
                        LatestIndex  = 0
                    };
                }

                // 把快照放到对应内存中
                snapshotData           += typeState.SnapshotSize * snapshotDataComponent.LatestIndex;
                *((uint *)snapshotData) = serverTick;
                snapshotData           += GlobalConstants.TickSize;

                // 放到快照对应内存位置
                for (int j = 0; j < numBaseComponents; j++)
                {
                    var compIdx    = ghostSerializerCollectionSystem.IndexCollection[baseOffset + j].ComponentIndex;
                    var serializer =
                        ghostSerializerCollectionSystem.Serializers[compIdx];

                    serializer.Deserialize.Ptr.Invoke((IntPtr)snapshotData, ref reader, ref _networkCompressionModel);
                    snapshotData += serializer.DataSize;
                }
            }


#if UNITY_EDITOR || DEVELOPMENT_BUILD
            NetDebug.Set(nameof(ack.LastReceivedSnapshotByLocal), ack.LastReceivedSnapshotByLocal);
            NetDebug.Set(nameof(ack.EstimatedRTT), ack.EstimatedRTT);
            NetDebug.Set(nameof(ack.DeviationRTT), ack.DeviationRTT);
            NetDebug.RTT    = (uint)ack.EstimatedRTT;
            NetDebug.Jitter = (uint)ack.DeviationRTT;
#endif

            inBuffer.Clear();
        }
            public void Execute(DynamicComponentTypeHandle *ghostChunkComponentTypesPtr,
                                int ghostChunkComponentTypesLength)
            {
                for (int i = 0; i < GhostChunks.Length; i++)
                {
                    ArchetypeChunk chunk    = GhostChunks[i];
                    var            entities = chunk.GetNativeArray(EntityTypeHandle);
                    var            buffers  = chunk.GetBufferAccessor(SnapshotBufferTypeHandle);
                    var            datas    = chunk.GetNativeArray(SnapshotComponentTypeHandle);
                    var            ghosts   = chunk.GetNativeArray(GhostComponentTypeHandle);

                    for (int ent = 0; ent < entities.Length; ent++)
                    {
                        GhostComponent ghostComponent = ghosts[ent];

                        var typeState = GhostTypeCollection[ghostComponent.GhostType];

                        SnapshotData snapshotData = datas[ent];
                        DynamicBuffer <SnapshotDataBuffer> snapshotDataBuffer = buffers[ent];

                        if (snapshotData.SnapshotSize == 0)
                        {
                            continue;
                        }

                        IntPtr latestSnapshot = snapshotData.GetLatest(snapshotDataBuffer);

                        int baseOffset        = typeState.FirstComponent;
                        int numBaseComponents = typeState.NumComponents;

                        // 跳过Tick
                        int offset = GlobalConstants.TickSize;
                        for (int j = 0; j < numBaseComponents; j++)
                        {
                            int cmpIdx     = ComponentIndex[baseOffset + j].ComponentIndex;
                            var serializer = ComponentSerializers[cmpIdx];


                            DynamicComponentTypeHandle dynamicType =
                                ghostChunkComponentTypesPtr[cmpIdx];
                            if (chunk.Has(dynamicType))
                            {
                                IntPtr compPtr = (IntPtr)chunk
                                                 .GetDynamicComponentDataArrayReinterpret <byte>(dynamicType,
                                                                                                 serializer.ComponentSize).GetUnsafePtr();

                                // 不回滚只更新值
                                if (serializer.IsUpdateValue)
                                {
                                    var dataAtTick = new SnapshotData.DataAtTick
                                    {
                                        Tick = snapshotData.GetLatestTick(snapshotDataBuffer),
                                        InterpolationFactor = 1,
                                        SnapshotAfter       = latestSnapshot,
                                        SnapshotBefore      = latestSnapshot
                                    };

                                    serializer.CopyFromSnapshot.Ptr.Invoke(
                                        compPtr + serializer.ComponentSize * ent,
                                        (IntPtr)(&dataAtTick), offset);
                                }
                                else
                                {
                                    serializer.RestoreFromBackup.Ptr.Invoke(
                                        compPtr + serializer.ComponentSize * ent,
                                        latestSnapshot + offset);
                                }
                            }

                            offset += serializer.DataSize;
                        }
                    }
                }
            }