Esempio n. 1
0
            public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex, DynamicComponentTypeHandle *ghostChunkComponentTypesPtr, int ghostChunkComponentTypesLength)
            {
                bool  predicted          = chunk.Has(predictedGhostComponentType);
                uint  targetTick         = predicted ? predictedTargetTick : interpolatedTargetTick;
                float targetTickFraction = predicted ? 1.0f : interpolatedTargetTickFraction;

                var deserializerState = new GhostDeserializerState
                {
                    GhostMap = GhostMap
                };
                var ghostComponents              = chunk.GetNativeArray(ghostType);
                int ghostTypeId                  = ghostComponents[0].ghostType;
                var typeData                     = GhostTypeCollection[ghostTypeId];
                var ghostSnapshotDataArray       = chunk.GetNativeArray(ghostSnapshotDataType);
                var ghostSnapshotDataBufferArray = chunk.GetBufferAccessor(ghostSnapshotDataBufferType);

                int changeMaskUints        = GhostCollectionSystem.ChangeMaskArraySizeInUInts(typeData.ChangeMaskBits);
                int snapshotOffset         = GhostCollectionSystem.SnapshotSizeAligned(4 + changeMaskUints * 4);
                int snapshotDataAtTickSize = UnsafeUtility.SizeOf <SnapshotData.DataAtTick>();

#if UNITY_EDITOR || DEVELOPMENT_BUILD
                var minMaxOffset = ThreadIndex * (JobsUtility.CacheLineSize / 4);
#endif
                var  dataAtTick  = new NativeArray <SnapshotData.DataAtTick>(ghostComponents.Length, Allocator.Temp);
                var  entityRange = new NativeList <int2>(ghostComponents.Length, Allocator.Temp);
                int2 nextRange   = default;
                var  predictedGhostComponentArray = chunk.GetNativeArray(predictedGhostComponentType);
                bool canBeStatic = typeData.StaticOptimization;
                // Find the ranges of entities which have data to apply, store the data to apply in an array while doing so
                for (int ent = 0; ent < ghostComponents.Length; ++ent)
                {
                    var  snapshotDataBuffer = ghostSnapshotDataBufferArray[ent];
                    var  ghostSnapshotData  = ghostSnapshotDataArray[ent];
                    var  latestTick         = ghostSnapshotData.GetLatestTick(snapshotDataBuffer);
                    bool isStatic           = canBeStatic && ghostSnapshotData.WasLatestTickZeroChange(snapshotDataBuffer, changeMaskUints);
#if UNITY_EDITOR || DEVELOPMENT_BUILD
                    if (latestTick != 0 && !isStatic)
                    {
                        if (minMaxSnapshotTick[minMaxOffset] == 0 || SequenceHelpers.IsNewer(minMaxSnapshotTick[minMaxOffset], latestTick))
                        {
                            minMaxSnapshotTick[minMaxOffset] = latestTick;
                        }
                        if (minMaxSnapshotTick[minMaxOffset + 1] == 0 || SequenceHelpers.IsNewer(latestTick, minMaxSnapshotTick[minMaxOffset + 1]))
                        {
                            minMaxSnapshotTick[minMaxOffset + 1] = latestTick;
                        }
                    }
#endif
                    if (ghostSnapshotData.GetDataAtTick(targetTick, targetTickFraction, snapshotDataBuffer, out var data))
                    {
                        if (predicted)
                        {
                            // TODO: is this the right way to handle this?
                            data.InterpolationFactor = 0;
                            var snapshotTick  = *(uint *)data.SnapshotBefore;
                            var predictedData = predictedGhostComponentArray[ent];
                            // We want to contiue prediction from the last full tick we predicted last time
                            var predictionStartTick = predictionStateBackupTick;
                            // If there is no history, try to use the tick where we left off last time, will only be a valid tick if we ended with a full prediction tick as opposed to a fractional one
                            if (predictionStartTick == 0)
                            {
                                predictionStartTick = lastPredictedTick;
                            }
                            // If we do not have a backup or we got more data since last time we run from the tick we have snapshot data for
                            if (predictionStartTick == 0 || predictedData.AppliedTick != snapshotTick)
                            {
                                predictionStartTick = snapshotTick;
                            }
                            // If we have newer or equally new data in the
                            else if (!SequenceHelpers.IsNewer(predictionStartTick, snapshotTick))
                            {
                                predictionStartTick = snapshotTick;
                            }

                            // If we want to continue prediction, and this is not the currently applied prediction state we must restore the state from the backup
                            if (predictionStartTick != snapshotTick && predictionStartTick != lastPredictedTick)
                            {
                                // If we cannot restore the backup and continue prediction we roll back and resimulate
                                if (!RestorePredictionBackup(chunk, ent, typeData, ghostChunkComponentTypesPtr, ghostChunkComponentTypesLength))
                                {
                                    predictionStartTick = snapshotTick;
                                }
                            }


                            if (minPredictedTick[ThreadIndex] == 0 || SequenceHelpers.IsNewer(minPredictedTick[ThreadIndex], predictionStartTick))
                            {
                                minPredictedTick[ThreadIndex] = predictionStartTick;
                            }

                            if (predictionStartTick != snapshotTick)
                            {
                                if (nextRange.y != 0)
                                {
                                    entityRange.Add(nextRange);
                                }
                                nextRange = default;
                            }
                            else
                            {
                                predictedData.AppliedTick = snapshotTick;
                                if (nextRange.y == 0)
                                {
                                    nextRange.x = ent;
                                }
                                nextRange.y = ent + 1;
                            }
                            predictedData.PredictionStartTick = predictionStartTick;
                            predictedGhostComponentArray[ent] = predictedData;
                        }
                        else
                        {
                            // If this snapshot is static, and the data for the latest tick was applied during last interpolation update, we can just skip copying data
                            if (isStatic && !SequenceHelpers.IsNewer(latestTick, lastInterpolatedTick))
                            {
                                if (nextRange.y != 0)
                                {
                                    entityRange.Add(nextRange);
                                }
                                nextRange = default;
                            }
                            else
                            {
                                if (nextRange.y == 0)
                                {
                                    nextRange.x = ent;
                                }
                                nextRange.y = ent + 1;
                            }
                        }
                        dataAtTick[ent] = data;
                    }
                    else if (nextRange.y != 0)
                    {
                        entityRange.Add(nextRange);
                        nextRange = default;
                        if (predicted)
                        {
                            var predictionStartTick = predictionStateBackupTick;
                            if (predictionStateBackupTick != lastPredictedTick)
                            {
                                // Try to back up the thing
                                if (!RestorePredictionBackup(chunk, ent, typeData, ghostChunkComponentTypesPtr, ghostChunkComponentTypesLength))
                                {
                                    predictionStartTick = 0;
                                }
                            }
#if ENABLE_UNITY_COLLECTIONS_CHECKS
                            if (predictionStartTick == 0)
                            {
                                throw new System.InvalidOperationException("Trying to predict a ghost without having a state to roll back to");
                            }
#endif
                        }
                    }
                }
                if (nextRange.y != 0)
                {
                    entityRange.Add(nextRange);
                }

                var requiredSendMask  = predicted ? GhostComponentSerializer.SendMask.Predicted : GhostComponentSerializer.SendMask.Interpolated;
                int numBaseComponents = typeData.NumComponents - typeData.NumChildComponents;
                for (int comp = 0; comp < numBaseComponents; ++comp)
                {
                    int compIdx = GhostComponentIndex[typeData.FirstComponent + comp].ComponentIndex;
#if ENABLE_UNITY_COLLECTIONS_CHECKS
                    if (compIdx >= ghostChunkComponentTypesLength)
                    {
                        throw new System.InvalidOperationException("Component index out of range");
                    }
#endif
                    if (chunk.Has(ghostChunkComponentTypesPtr[compIdx]) &&
                        (GhostComponentCollection[compIdx].SendMask & requiredSendMask) != 0)
                    {
                        var compSize = GhostComponentCollection[compIdx].ComponentSize;
                        var compData = (byte *)chunk.GetDynamicComponentDataArrayReinterpret <byte>(ghostChunkComponentTypesPtr[compIdx], compSize).GetUnsafePtr();
                        for (var rangeIdx = 0; rangeIdx < entityRange.Length; ++rangeIdx)
                        {
                            var range        = entityRange[rangeIdx];
                            var snapshotData = (byte *)dataAtTick.GetUnsafeReadOnlyPtr();
                            snapshotData += snapshotDataAtTickSize * range.x;
                            GhostComponentCollection[compIdx].CopyFromSnapshot.Ptr.Invoke((System.IntPtr)UnsafeUtility.AddressOf(ref deserializerState), (System.IntPtr)snapshotData, snapshotOffset, snapshotDataAtTickSize, (System.IntPtr)(compData + range.x * compSize), compSize, range.y - range.x);
                        }
                    }
                    snapshotOffset += GhostCollectionSystem.SnapshotSizeAligned(GhostComponentCollection[compIdx].SnapshotSize);
                }
                if (typeData.NumChildComponents > 0)
                {
                    var linkedEntityGroupAccessor = chunk.GetBufferAccessor(linkedEntityGroupType);
                    for (int comp = numBaseComponents; comp < typeData.NumComponents; ++comp)
                    {
                        int compIdx = GhostComponentIndex[typeData.FirstComponent + comp].ComponentIndex;
#if ENABLE_UNITY_COLLECTIONS_CHECKS
                        if (compIdx >= ghostChunkComponentTypesLength)
                        {
                            throw new System.InvalidOperationException("Component index out of range");
                        }
#endif
                        if ((GhostComponentCollection[compIdx].SendMask & requiredSendMask) != 0)
                        {
                            var compSize = GhostComponentCollection[compIdx].ComponentSize;
                            for (var rangeIdx = 0; rangeIdx < entityRange.Length; ++rangeIdx)
                            {
                                var range = entityRange[rangeIdx];
                                for (int ent = range.x; ent < range.y; ++ent)
                                {
                                    var linkedEntityGroup = linkedEntityGroupAccessor[ent];
                                    if (!childEntityLookup.TryGetValue(linkedEntityGroup[GhostComponentIndex[typeData.FirstComponent + comp].EntityIndex].Value, out var childChunk))
                                    {
                                        continue;
                                    }
                                    if (!childChunk.chunk.Has(ghostChunkComponentTypesPtr[compIdx]))
                                    {
                                        continue;
                                    }
                                    var compData     = (byte *)childChunk.chunk.GetDynamicComponentDataArrayReinterpret <byte>(ghostChunkComponentTypesPtr[compIdx], compSize).GetUnsafePtr();
                                    var snapshotData = (byte *)dataAtTick.GetUnsafeReadOnlyPtr();
                                    snapshotData += snapshotDataAtTickSize * ent;
                                    GhostComponentCollection[compIdx].CopyFromSnapshot.Ptr.Invoke((System.IntPtr)UnsafeUtility.AddressOf(ref deserializerState), (System.IntPtr)snapshotData, snapshotOffset, snapshotDataAtTickSize, (System.IntPtr)(compData + childChunk.index * compSize), compSize, 1);
                                }
                            }
                        }
                        snapshotOffset += GhostCollectionSystem.SnapshotSizeAligned(GhostComponentCollection[compIdx].SnapshotSize);
                    }
                }
            }