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;
                        }
                    }
                }
            }
Beispiel #2
0
            public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex)
            {
                var dstPtr = (void *)((IntPtr)dst + sizeOfValue * firstEntityIndex);
                var src    = chunk.GetDynamicComponentDataArrayReinterpret <byte>(dynamicType, sizeOfValue);

                if (src.Length == 0)
                {
                    UnsafeUtility.MemCpyReplicate(dstPtr, defaultValue, sizeOfValue, chunk.Count);
                }
                else
                {
                    var srcPtr = src.GetUnsafeReadOnlyPtr();
                    UnsafeUtility.MemCpy(dstPtr, srcPtr, src.Length);
                }
            }
        public void Distribute(DynamicComponentTypeHandle typeHandle, ArchetypeChunk chunk, Dictionary <uint, byte[]> objects)
        {
            NativeArray <PhysicsColliderBlob> components  = chunk.GetDynamicComponentDataArrayReinterpret <PhysicsColliderBlob>(typeHandle, PhysicsColliderBlob.TYPE_SIZE);
            NativeArray <SimAssetId>          simAssetIds = chunk.Has(_simAssetIdsHandle) ? chunk.GetNativeArray(_simAssetIdsHandle) : default;

            for (int i = 0; i < components.Length; i++)
            {
                uint id = ushort.MaxValue + 1;

                if (simAssetIds.IsCreated)
                {
                    id = simAssetIds[i].Value;
                }

                if (objects.TryGetValue(id, out byte[] data))
        public void Collect(DynamicComponentTypeHandle typeHandle, ArchetypeChunk chunk, Dictionary <uint, byte[]> outResult)
        {
            NativeArray <PhysicsColliderBlob> components  = chunk.GetDynamicComponentDataArrayReinterpret <PhysicsColliderBlob>(typeHandle, PhysicsColliderBlob.TYPE_SIZE);
            NativeArray <SimAssetId>          simAssetIds = chunk.Has(_simAssetIdsHandle) ? chunk.GetNativeArray(_simAssetIdsHandle) : default;

            for (int i = 0; i < components.Length; i++)
            {
                if (components[i].Collider.IsCreated)
                {
                    byte[] data;
                    IntPtr ptr;

                    unsafe
                    {
                        ptr = new IntPtr(components[i].Collider.GetUnsafePtr());
                    }

                    if (!_cachedColliders.TryGetValue(ptr, out data))
                    {
                        using (var writer = new MemoryBinaryWriter())
                        {
                            writer.Write(components[i].Collider);
                            data = writer.GetDataArray();
                        }

                        _cachedColliders[ptr] = data;
                    }

                    if (simAssetIds.IsCreated)
                    {
                        outResult[simAssetIds[i].Value] = data;
                    }
                    else
                    {
#if SAFETY && UNITY_EDITOR
                        if (!chunk.Has(_tileColliderTagHandle))
                        {
                            Log.Error($"Error in collider collection: An entity ({string.Join(", ", chunk.Archetype.GetComponentTypes().Select(c => c.ToString()))}) " +
                                      $"has a collider but no SimAssetId");
                            continue;
                        }
#endif
                        outResult[ushort.MaxValue + 1] = data;
                    }
                }
            }
        }
Beispiel #5
0
            public unsafe void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex, DynamicComponentTypeHandle *ghostChunkComponentTypesPtr, int ghostChunkComponentTypesLength)
            {
                var prefabList             = ghostPrefabBufferFromEntity[prefabEntity];
                var entityList             = chunk.GetNativeArray(entityType);
                var snapshotDataList       = chunk.GetNativeArray(snapshotDataType);
                var snapshotDataBufferList = chunk.GetBufferAccessor(snapshotDataBufferType);

                var ghostTypeComponent = ghostTypeFromEntity[entityList[0]];
                int ghostType;

                for (ghostType = 0; ghostType < prefabList.Length; ++ghostType)
                {
                    if (ghostTypeFromEntity[prefabList[ghostType].Value] == ghostTypeComponent)
                    {
                        break;
                    }
                }
                if (ghostType >= prefabList.Length)
                {
                    throw new System.InvalidOperationException("Could not find ghost type in the collection");
                }

                var spawnList          = spawnListFromEntity[spawnListEntity];
                var typeData           = GhostTypeCollection[ghostType];
                var snapshotSize       = typeData.SnapshotSize;
                int changeMaskUints    = GhostCollectionSystem.ChangeMaskArraySizeInUInts(typeData.ChangeMaskBits);
                var snapshotBaseOffset = GhostCollectionSystem.SnapshotSizeAligned(4 + changeMaskUints * 4);
                var serializerState    = new GhostSerializerState
                {
                    GhostFromEntity = ghostFromEntity
                };

                for (int i = 0; i < entityList.Length; ++i)
                {
                    var entity = entityList[i];

                    var ghostComponent = ghostFromEntity[entity];
                    ghostComponent.ghostType = ghostType;
                    ghostFromEntity[entity]  = ghostComponent;
                    // Set initial snapshot data
                    // Get the buffers, fill in snapshot size etc
                    snapshotDataList[i] = new SnapshotData {
                        SnapshotSize = snapshotSize, LatestIndex = 0
                    };
                    var snapshotDataBuffer = snapshotDataBufferList[i];
                    snapshotDataBuffer.ResizeUninitialized(snapshotSize * GhostSystemConstants.SnapshotHistorySize);
                    var snapshotPtr = (byte *)snapshotDataBuffer.GetUnsafePtr();
                    UnsafeUtility.MemClear(snapshotPtr, snapshotSize * GhostSystemConstants.SnapshotHistorySize);
                    *(uint *)snapshotPtr = spawnTick;
                    var snapshotOffset = snapshotBaseOffset;

                    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 InvalidOperationException("Component index out of range");
                        }
#endif
                        if (chunk.Has(ghostChunkComponentTypesPtr[compIdx]))
                        {
                            var compSize = GhostComponentCollection[compIdx].ComponentSize;
                            var compData = (byte *)chunk.GetDynamicComponentDataArrayReinterpret <byte>(ghostChunkComponentTypesPtr[compIdx], compSize).GetUnsafeReadOnlyPtr();
                            compData += i * compSize;
                            GhostComponentCollection[compIdx].CopyToSnapshot.Ptr.Invoke((IntPtr)UnsafeUtility.AddressOf(ref serializerState), (IntPtr)snapshotPtr, snapshotOffset, snapshotSize, (IntPtr)compData, compSize, 1);
                        }
                        else
                        {
                            var componentSnapshotSize = GhostCollectionSystem.SnapshotSizeAligned(GhostComponentCollection[compIdx].SnapshotSize);
                            UnsafeUtility.MemClear(snapshotPtr + snapshotOffset, componentSnapshotSize);
                        }
                        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 InvalidOperationException("Component index out of range");
                            }
#endif
                            var compSize = GhostComponentCollection[compIdx].ComponentSize;

                            var linkedEntityGroup = linkedEntityGroupAccessor[i];
                            var childEnt          = linkedEntityGroup[GhostComponentIndex[typeData.FirstComponent + comp].EntityIndex].Value;
                            //We can skip here, becase the memory buffer offset is computed using the start-end entity indices
                            if (!childEntityLookup.TryGetValue(childEnt, out var childChunk) || !childChunk.chunk.Has(ghostChunkComponentTypesPtr[compIdx]))
                            {
                                var componentSnapshotSize = GhostCollectionSystem.SnapshotSizeAligned(GhostComponentCollection[compIdx].SnapshotSize);
                                UnsafeUtility.MemClear(snapshotPtr + snapshotOffset, componentSnapshotSize);
                            }
                            else
                            {
                                var compData = (byte *)childChunk.chunk.GetDynamicComponentDataArrayReinterpret <byte>(ghostChunkComponentTypesPtr[compIdx], compSize).GetUnsafeReadOnlyPtr();
                                compData += childChunk.index * compSize;
                                GhostComponentCollection[compIdx].CopyToSnapshot.Ptr.Invoke((IntPtr)UnsafeUtility.AddressOf(ref serializerState), (IntPtr)snapshotPtr, snapshotOffset, snapshotSize, (IntPtr)compData, compSize, 1);
                            }

                            snapshotOffset += GhostCollectionSystem.SnapshotSizeAligned(GhostComponentCollection[compIdx].SnapshotSize);
                        }
                    }


                    // Remove request component
                    commandBuffer.RemoveComponent <PredictedGhostSpawnRequestComponent>(entity);
                    // Add to list of predictive spawn component - maybe use a singleton for this so spawn systems can just access it too
                    spawnList.Add(new PredictedGhostSpawn {
                        entity = entity, ghostType = ghostType, spawnTick = spawnTick
                    });
                }
            }
            public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex, DynamicComponentTypeHandle *ghostChunkComponentTypesPtr, int ghostChunkComponentTypesLength)
            {
                var ghostComponents = chunk.GetNativeArray(ghostType);
                int ghostTypeId     = ghostComponents[0].ghostType;
                var typeData        = GhostTypeCollection[ghostTypeId];

                var headerSize = PredictionBackupState.GetHeaderSize();
                var entitySize = PredictionBackupState.GetEntitiesSize(chunk.Capacity, out var singleEntitySize);
                int baseOffset = typeData.FirstComponent;

                if (!predictionState.TryGetValue(chunk, out var state) ||
                    (*(PredictionBackupState *)state).ghostType != ghostTypeId ||
                    (*(PredictionBackupState *)state).entityCapacity != chunk.Capacity)
                {
                    int dataSize = 0;
                    // Sum up the size of all components rounded up
                    for (int comp = 0; comp < typeData.NumComponents; ++comp)
                    {
                        int compIdx = GhostComponentIndex[baseOffset + 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)
                        {
                            continue;
                        }
                        dataSize += PredictionBackupState.GetDataSize(GhostComponentCollection[compIdx].ComponentSize, chunk.Capacity);
                    }
                    // Chunk does not exist in the history, or has changed ghost type in which case we need to create a new one
                    state = (System.IntPtr)UnsafeUtility.Malloc(headerSize + entitySize + dataSize, 16, Allocator.Persistent);
                    newPredictionState.Enqueue(new PredictionStateEntry {
                        chunk = chunk, data = state
                    });
                    (*(PredictionBackupState *)state).ghostType      = ghostTypeId;
                    (*(PredictionBackupState *)state).entityCapacity = chunk.Capacity;
                }
                else
                {
                    stillUsedPredictionState.TryAdd(chunk, 1);
                }
                Entity *entities    = PredictionBackupState.GetEntities(state, headerSize);
                var     srcEntities = chunk.GetNativeArray(entityType).GetUnsafeReadOnlyPtr();
                UnsafeUtility.MemCpy(entities, srcEntities, chunk.Count * singleEntitySize);
                if (chunk.Count < chunk.Capacity)
                {
                    UnsafeUtility.MemClear(entities + chunk.Count, (chunk.Capacity - chunk.Count) * singleEntitySize);
                }

                byte *dataPtr           = PredictionBackupState.GetData(state, headerSize, entitySize);
                int   numBaseComponents = typeData.NumComponents - typeData.NumChildComponents;
                for (int comp = 0; comp < numBaseComponents; ++comp)
                {
                    int compIdx = GhostComponentIndex[baseOffset + 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)
                    {
                        continue;
                    }
                    var compSize = GhostComponentCollection[compIdx].ComponentSize;
                    if (chunk.Has(ghostChunkComponentTypesPtr[compIdx]))
                    {
                        var compData = (byte *)chunk.GetDynamicComponentDataArrayReinterpret <byte>(ghostChunkComponentTypesPtr[compIdx], compSize).GetUnsafeReadOnlyPtr();
                        UnsafeUtility.MemCpy(dataPtr, compData, chunk.Count * compSize);
                    }
                    else
                    {
                        UnsafeUtility.MemClear(dataPtr, chunk.Count * compSize);
                    }

                    dataPtr = PredictionBackupState.GetNextData(dataPtr, compSize, chunk.Capacity);
                }
                if (typeData.NumChildComponents > 0)
                {
                    var linkedEntityGroupAccessor = chunk.GetBufferAccessor(linkedEntityGroupType);
                    for (int comp = numBaseComponents; comp < typeData.NumComponents; ++comp)
                    {
                        int compIdx = GhostComponentIndex[baseOffset + 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)
                        {
                            continue;
                        }
                        var compSize = GhostComponentCollection[compIdx].ComponentSize;

                        for (int ent = 0; ent < chunk.Count; ++ent)
                        {
                            var linkedEntityGroup = linkedEntityGroupAccessor[ent];
                            if (childEntityLookup.TryGetValue(linkedEntityGroup[GhostComponentIndex[baseOffset + comp].EntityIndex].Value, out var childChunk) &&
                                childChunk.chunk.Has(ghostChunkComponentTypesPtr[compIdx]))
                            {
                                var compData = (byte *)childChunk.chunk.GetDynamicComponentDataArrayReinterpret <byte>(ghostChunkComponentTypesPtr[compIdx], compSize).GetUnsafePtr();
                                UnsafeUtility.MemCpy(dataPtr + ent * compSize, compData + childChunk.index * compSize, compSize);
                            }
                            else
                            {
                                UnsafeUtility.MemClear(dataPtr + ent * compSize, compSize);
                            }
                        }

                        dataPtr = PredictionBackupState.GetNextData(dataPtr, compSize, chunk.Capacity);
                    }
                }
            }
            public unsafe void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex, DynamicComponentTypeHandle *ghostChunkComponentTypesPtr, int ghostChunkComponentTypesLength)
            {
                var entityList                    = chunk.GetNativeArray(entityType);
                var snapshotDataList              = chunk.GetNativeArray(snapshotDataType);
                var snapshotDataBufferList        = chunk.GetBufferAccessor(snapshotDataBufferType);
                var snapshotDynamicDataBufferList = chunk.GetBufferAccessor(snapshotDynamicDataBufferType);

                var GhostCollection     = GhostCollectionFromEntity[GhostCollectionSingleton];
                var GhostTypeCollection = GhostTypeCollectionFromEntity[GhostCollectionSingleton];
                var ghostTypeComponent  = ghostTypeFromEntity[entityList[0]];
                int ghostType;

                for (ghostType = 0; ghostType < GhostCollection.Length; ++ghostType)
                {
                    if (GhostCollection[ghostType].GhostType == ghostTypeComponent)
                    {
                        break;
                    }
                }
                if (ghostType >= GhostCollection.Length)
                {
                    throw new InvalidOperationException("Could not find ghost type in the collection");
                }
                if (ghostType >= GhostTypeCollection.Length)
                {
                    return; // serialization data has not been loaded yet
                }
                var GhostComponentCollection = GhostComponentCollectionFromEntity[GhostCollectionSingleton];
                var GhostComponentIndex      = GhostComponentIndexFromEntity[GhostCollectionSingleton];

                var spawnList          = spawnListFromEntity[spawnListEntity];
                var typeData           = GhostTypeCollection[ghostType];
                var snapshotSize       = typeData.SnapshotSize;
                int changeMaskUints    = GhostCollectionSystem.ChangeMaskArraySizeInUInts(typeData.ChangeMaskBits);
                var snapshotBaseOffset = GhostCollectionSystem.SnapshotSizeAligned(4 + changeMaskUints * 4);
                var serializerState    = new GhostSerializerState
                {
                    GhostFromEntity = ghostFromEntity
                };
                var buffersToSerialize = new NativeList <BufferDataEntry>(Allocator.Temp);

                for (int i = 0; i < entityList.Length; ++i)
                {
                    var entity = entityList[i];

                    var ghostComponent = ghostFromEntity[entity];
                    ghostComponent.ghostType = ghostType;
                    ghostFromEntity[entity]  = ghostComponent;
                    // Set initial snapshot data
                    // Get the buffers, fill in snapshot size etc
                    snapshotDataList[i] = new SnapshotData {
                        SnapshotSize = snapshotSize, LatestIndex = 0
                    };
                    var snapshotDataBuffer = snapshotDataBufferList[i];
                    snapshotDataBuffer.ResizeUninitialized(snapshotSize * GhostSystemConstants.SnapshotHistorySize);
                    var snapshotPtr = (byte *)snapshotDataBuffer.GetUnsafePtr();
                    UnsafeUtility.MemClear(snapshotPtr, snapshotSize * GhostSystemConstants.SnapshotHistorySize);
                    *(uint *)snapshotPtr = spawnTick;
                    var snapshotOffset            = snapshotBaseOffset;
                    var dynamicSnapshotDataOffset = 0;

                    //Loop through all the serializable components and copy their data into the snapshot.
                    //For buffers, we collect what we need to serialize and then we copy the content in a second
                    //step. This is necessary because we need to resize the dynamic snapshot buffer
                    int numBaseComponents = typeData.NumComponents - typeData.NumChildComponents;
                    for (int comp = 0; comp < numBaseComponents; ++comp)
                    {
                        int compIdx       = GhostComponentIndex[typeData.FirstComponent + comp].ComponentIndex;
                        int serializerIdx = GhostComponentIndex[typeData.FirstComponent + comp].SerializerIndex;
#if ENABLE_UNITY_COLLECTIONS_CHECKS
                        if (compIdx >= ghostChunkComponentTypesLength)
                        {
                            throw new InvalidOperationException("Component index out of range");
                        }
#endif
                        var componentSnapshotSize = GhostComponentCollection[serializerIdx].ComponentType.IsBuffer
                            ? GhostCollectionSystem.SnapshotSizeAligned(GhostSystemConstants.DynamicBufferComponentSnapshotSize)
                            : GhostCollectionSystem.SnapshotSizeAligned(GhostComponentCollection[serializerIdx].SnapshotSize);
                        var compSize = GhostComponentCollection[serializerIdx].ComponentSize;

                        if (!chunk.Has(ghostChunkComponentTypesPtr[compIdx]))
                        {
                            UnsafeUtility.MemClear(snapshotPtr + snapshotOffset, componentSnapshotSize);
                        }
                        else if (!GhostComponentCollection[serializerIdx].ComponentType.IsBuffer)
                        {
                            var compData = (byte *)chunk.GetDynamicComponentDataArrayReinterpret <byte>(ghostChunkComponentTypesPtr[compIdx], compSize).GetUnsafeReadOnlyPtr();
                            GhostComponentCollection[serializerIdx].CopyToSnapshot.Ptr.Invoke((IntPtr)UnsafeUtility.AddressOf(ref serializerState), (IntPtr)snapshotPtr, snapshotOffset, snapshotSize, (IntPtr)(compData + i * compSize), compSize, 1);
                        }

                        else
                        {
                            // Collect the buffer data to serialize by storing pointers, offset and size.
                            var bufData       = chunk.GetUntypedBufferAccessor(ref ghostChunkComponentTypesPtr[compIdx]);
                            var bufferPointer = (IntPtr)bufData.GetUnsafeReadOnlyPtrAndLength(i, out var bufferLen);
                            var snapshotData  = (uint *)(snapshotPtr + snapshotOffset);
                            snapshotData[0] = (uint)bufferLen;
                            snapshotData[1] = (uint)dynamicSnapshotDataOffset;
                            buffersToSerialize.Add(new BufferDataEntry
                            {
                                offset        = dynamicSnapshotDataOffset,
                                len           = bufferLen,
                                serializerIdx = serializerIdx,
                                bufferData    = bufferPointer
                            });
                            var maskSize = SnapshotDynamicBuffersHelper.GetDynamicDataChangeMaskSize(GhostComponentCollection[serializerIdx].ChangeMaskBits, bufferLen);
                            dynamicSnapshotDataOffset += GhostCollectionSystem.SnapshotSizeAligned(maskSize + GhostComponentCollection[serializerIdx].ComponentSize * bufferLen);
                        }
                        snapshotOffset += componentSnapshotSize;
                    }
                    if (typeData.NumChildComponents > 0)
                    {
                        var linkedEntityGroupAccessor = chunk.GetBufferAccessor(linkedEntityGroupType);
                        for (int comp = numBaseComponents; comp < typeData.NumComponents; ++comp)
                        {
                            int compIdx       = GhostComponentIndex[typeData.FirstComponent + comp].ComponentIndex;
                            int serializerIdx = GhostComponentIndex[typeData.FirstComponent + comp].SerializerIndex;
#if ENABLE_UNITY_COLLECTIONS_CHECKS
                            if (compIdx >= ghostChunkComponentTypesLength)
                            {
                                throw new System.InvalidOperationException("Component index out of range");
                            }
#endif
                            var compSize = GhostComponentCollection[serializerIdx].ComponentSize;

                            var linkedEntityGroup = linkedEntityGroupAccessor[i];
                            var childEnt          = linkedEntityGroup[GhostComponentIndex[typeData.FirstComponent + comp].EntityIndex].Value;

                            var componentSnapshotSize = GhostComponentCollection[serializerIdx].ComponentType.IsBuffer
                                ? GhostCollectionSystem.SnapshotSizeAligned(GhostSystemConstants.DynamicBufferComponentSnapshotSize)
                                : GhostCollectionSystem.SnapshotSizeAligned(GhostComponentCollection[serializerIdx].SnapshotSize);

                            //We can skip here, becase the memory buffer offset is computed using the start-end entity indices
                            if (!childEntityLookup.TryGetValue(childEnt, out var childChunk) || !childChunk.chunk.Has(ghostChunkComponentTypesPtr[compIdx]))
                            {
                                UnsafeUtility.MemClear(snapshotPtr + snapshotOffset, componentSnapshotSize);
                            }
                            else if (!GhostComponentCollection[serializerIdx].ComponentType.IsBuffer)
                            {
                                var compData = (byte *)childChunk.chunk.GetDynamicComponentDataArrayReinterpret <byte>(ghostChunkComponentTypesPtr[compIdx], compSize).GetUnsafeReadOnlyPtr();
                                compData += childChunk.index * compSize;
                                GhostComponentCollection[serializerIdx].CopyToSnapshot.Ptr.Invoke((System.IntPtr)UnsafeUtility.AddressOf(ref serializerState),
                                                                                                  (System.IntPtr)snapshotPtr, snapshotOffset, snapshotSize, (System.IntPtr)compData, compSize, 1);
                            }
                            else
                            {
                                // Collect the buffer data to serialize by storing pointers, offset and size.
                                var bufData       = childChunk.chunk.GetUntypedBufferAccessor(ref ghostChunkComponentTypesPtr[compIdx]);
                                var bufferPointer = (IntPtr)bufData.GetUnsafeReadOnlyPtrAndLength(childChunk.index, out var bufferLen);
                                var snapshotData  = (uint *)(snapshotPtr + snapshotOffset);
                                snapshotData[0] = (uint)bufferLen;
                                snapshotData[1] = (uint)dynamicSnapshotDataOffset;
                                buffersToSerialize.Add(new BufferDataEntry
                                {
                                    offset        = dynamicSnapshotDataOffset,
                                    len           = bufferLen,
                                    serializerIdx = serializerIdx,
                                    bufferData    = bufferPointer
                                });
                                var maskSize = SnapshotDynamicBuffersHelper.GetDynamicDataChangeMaskSize(GhostComponentCollection[serializerIdx].ChangeMaskBits, bufferLen);
                                dynamicSnapshotDataOffset += GhostCollectionSystem.SnapshotSizeAligned(maskSize + GhostComponentCollection[serializerIdx].ComponentSize * bufferLen);
                            }
                            snapshotOffset += componentSnapshotSize;
                        }
                    }

                    //Second step (necessary only for buffers): resize the dynamicdata snapshot buffer and copy the buffer contents
                    if (GhostTypeCollection[ghostType].NumBuffers > 0 && buffersToSerialize.Length > 0)
                    {
                        var dynamicDataCapacity       = SnapshotDynamicBuffersHelper.CalculateBufferCapacity((uint)dynamicSnapshotDataOffset, out _);
                        var headerSize                = SnapshotDynamicBuffersHelper.GetHeaderSize();
                        var snapshotDynamicDataBuffer = snapshotDynamicDataBufferList[i];
                        snapshotDynamicDataBuffer.ResizeUninitialized((int)dynamicDataCapacity);
                        var snapshotDynamicDataBufferPtr = (byte *)snapshotDynamicDataBuffer.GetUnsafePtr() + headerSize;

                        ((uint *)snapshotDynamicDataBuffer.GetUnsafePtr())[0] = (uint)dynamicSnapshotDataOffset;
                        for (int buf = 0; buf < buffersToSerialize.Length; ++buf)
                        {
                            var entry           = buffersToSerialize[buf];
                            var dynamicDataSize = GhostComponentCollection[entry.serializerIdx].SnapshotSize;
                            var compSize        = GhostComponentCollection[entry.serializerIdx].ComponentSize;
                            var maskSize        = SnapshotDynamicBuffersHelper.GetDynamicDataChangeMaskSize(GhostComponentCollection[entry.serializerIdx].ChangeMaskBits, entry.len);
#if ENABLE_UNITY_COLLECTIONS_CHECKS
                            if ((maskSize + entry.offset + dynamicDataSize * entry.len) > dynamicDataCapacity)
                            {
                                throw new System.InvalidOperationException("Overflow writing data to dynamic snapshot memory buffer");
                            }
#endif
                            GhostComponentCollection[entry.serializerIdx].CopyToSnapshot.Ptr.Invoke((System.IntPtr)UnsafeUtility.AddressOf(ref serializerState),
                                                                                                    (System.IntPtr)snapshotDynamicDataBufferPtr + maskSize, entry.offset, dynamicDataSize,
                                                                                                    entry.bufferData, compSize, entry.len);
                        }
                    }

                    // Remove request component
                    commandBuffer.RemoveComponent <PredictedGhostSpawnRequestComponent>(entity);
                    // Add to list of predictive spawn component - maybe use a singleton for this so spawn systems can just access it too
                    spawnList.Add(new PredictedGhostSpawn {
                        entity = entity, ghostType = ghostType, spawnTick = spawnTick
                    });
                }
            }
Beispiel #8
0
            public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex, DynamicComponentTypeHandle *ghostChunkComponentTypesPtr, int ghostChunkComponentTypesLength)
            {
                if (!predictionState.TryGetValue(chunk, out var state) ||
                    (*(PredictionBackupState *)state).entityCapacity != chunk.Capacity)
                {
                    return;
                }

                var ghostComponents = chunk.GetNativeArray(ghostType);
                int ghostTypeId     = ghostComponents[0].ghostType;
                var typeData        = GhostTypeCollection[ghostTypeId];

                var headerSize = PredictionBackupState.GetHeaderSize();
                var entitySize = PredictionBackupState.GetEntitiesSize(chunk.Capacity, out var singleEntitySize);
                int baseOffset = typeData.FirstComponent;

                Entity *backupEntities = PredictionBackupState.GetEntities(state, headerSize);
                var     entities       = chunk.GetNativeArray(entityType);

                var predictedGhostComponents = chunk.GetNativeArray(predictedGhostType);

                byte *dataPtr           = PredictionBackupState.GetData(state, headerSize, entitySize);
                int   numBaseComponents = typeData.NumComponents - typeData.NumChildComponents;

                for (int comp = 0; comp < numBaseComponents; ++comp)
                {
                    int compIdx = GhostComponentIndex[baseOffset + 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)
                    {
                        continue;
                    }
                    var compSize = GhostComponentCollection[compIdx].ComponentSize;
                    if (chunk.Has(ghostChunkComponentTypesPtr[compIdx]))
                    {
                        var compData = (byte *)chunk.GetDynamicComponentDataArrayReinterpret <byte>(ghostChunkComponentTypesPtr[compIdx], compSize).GetUnsafeReadOnlyPtr();
                        for (int ent = 0; ent < entities.Length; ++ent)
                        {
                            // If this entity did not predict anything there was no rollback and no need to debug it
                            if (!GhostPredictionSystemGroup.ShouldPredict(tick, predictedGhostComponents[ent]))
                            {
                                continue;
                            }
                            if (entities[ent] == backupEntities[ent])
                            {
                                SmoothComponent(compData + compSize * ent, dataPtr + compSize * ent, GhostComponentCollection[compIdx].ComponentType);
                            }
                        }
                    }

                    dataPtr = PredictionBackupState.GetNextData(dataPtr, compSize, chunk.Capacity);
                }
                if (typeData.NumChildComponents > 0)
                {
                    var linkedEntityGroupAccessor = chunk.GetBufferAccessor(linkedEntityGroupType);
                    for (int comp = numBaseComponents; comp < typeData.NumComponents; ++comp)
                    {
                        int compIdx = GhostComponentIndex[baseOffset + 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)
                        {
                            continue;
                        }
                        var compSize  = GhostComponentCollection[compIdx].ComponentSize;
                        var entityIdx = GhostComponentIndex[baseOffset + comp].EntityIndex;

                        for (int ent = 0; ent < chunk.Count; ++ent)
                        {
                            // If this entity did not predict anything there was no rollback and no need to debug it
                            if (!GhostPredictionSystemGroup.ShouldPredict(tick, predictedGhostComponents[ent]))
                            {
                                continue;
                            }
                            if (entities[ent] != backupEntities[ent])
                            {
                                continue;
                            }
                            var linkedEntityGroup = linkedEntityGroupAccessor[ent];
                            if (childEntityLookup.TryGetValue(linkedEntityGroup[entityIdx].Value, out var childChunk) &&
                                childChunk.chunk.Has(ghostChunkComponentTypesPtr[compIdx]))
                            {
                                var compData = (byte *)childChunk.chunk.GetDynamicComponentDataArrayReinterpret <byte>(ghostChunkComponentTypesPtr[compIdx], compSize).GetUnsafePtr();
                                SmoothComponent(compData + compSize * childChunk.index, dataPtr + compSize * ent, GhostComponentCollection[compIdx].ComponentType);
                            }
                        }

                        dataPtr = PredictionBackupState.GetNextData(dataPtr, compSize, chunk.Capacity);
                    }
                }
            }
Beispiel #9
0
            public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex, DynamicComponentTypeHandle *ghostChunkComponentTypesPtr, int ghostChunkComponentTypesLength,
                                DynamicComponentTypeHandle *userTypes, int userTypesLength)
            {
                if (!predictionState.TryGetValue(chunk, out var state) ||
                    (*(PredictionBackupState *)state).entityCapacity != chunk.Capacity)
                {
                    return;
                }

                var GhostTypeCollection      = GhostTypeCollectionFromEntity[GhostCollectionSingleton];
                var GhostComponentIndex      = GhostComponentIndexFromEntity[GhostCollectionSingleton];
                var GhostComponentCollection = GhostComponentCollectionFromEntity[GhostCollectionSingleton];

                var ghostComponents = chunk.GetNativeArray(ghostType);

                int ghostTypeId = ghostComponents.GetFirstGhostTypeId();

                if (ghostTypeId < 0)
                {
                    return;
                }

                var typeData = GhostTypeCollection[ghostTypeId];

                var headerSize = PredictionBackupState.GetHeaderSize();
                var entitySize = PredictionBackupState.GetEntitiesSize(chunk.Capacity, out var singleEntitySize);

                Entity *backupEntities = PredictionBackupState.GetEntities(state);
                var     entities       = chunk.GetNativeArray(entityType);

                var predictedGhostComponents = chunk.GetNativeArray(predictedGhostType);

                int numBaseComponents = typeData.NumComponents - typeData.NumChildComponents;
                int baseOffset        = typeData.FirstComponent;

                var actions      = new NativeList <SmoothingActionState>(Allocator.Temp);
                var childActions = new NativeList <SmoothingActionState>(Allocator.Temp);

                int backupOffset = headerSize + entitySize;

                // todo: this loop could be cached on chunk.capacity, because now we are re-calculating it everytime.
                for (int comp = 0; comp < typeData.NumComponents; ++comp)
                {
                    int index         = baseOffset + comp;
                    int compIdx       = GhostComponentIndex[index].ComponentIndex;
                    int serializerIdx = GhostComponentIndex[index].SerializerIndex;
#if ENABLE_UNITY_COLLECTIONS_CHECKS
                    if (compIdx >= ghostChunkComponentTypesLength)
                    {
                        throw new System.InvalidOperationException("Component index out of range");
                    }
#endif
                    if ((GhostComponentIndex[index].SendMask & requiredSendMask) == 0)
                    {
                        continue;
                    }

                    if (GhostComponentCollection[serializerIdx].ComponentType.IsBuffer)
                    {
                        backupOffset += PredictionBackupState.GetDataSize(GhostSystemConstants.DynamicBufferComponentSnapshotSize, chunk.Capacity);
                        continue;
                    }
                    var compSize = GhostComponentCollection[serializerIdx].ComponentSize;

                    if (smoothingActions.TryGetValue(GhostComponentCollection[serializerIdx].ComponentType,
                                                     out var action))
                    {
                        action.compIndex        = compIdx;
                        action.compSize         = compSize;
                        action.serializerIndex  = serializerIdx;
                        action.entityIndex      = GhostComponentIndex[index].EntityIndex;
                        action.compBackupOffset = backupOffset;

                        if (comp < numBaseComponents)
                        {
                            actions.Add(action);
                        }
                        else
                        {
                            childActions.Add(action);
                        }
                    }
                    backupOffset += PredictionBackupState.GetDataSize(compSize, chunk.Capacity);
                }

                foreach (var action in actions)
                {
                    if (chunk.Has(ghostChunkComponentTypesPtr[action.compIndex]))
                    {
                        for (int ent = 0; ent < entities.Length; ++ent)
                        {
                            // If this entity did not predict anything there was no rollback and no need to debug it
                            if (!GhostPredictionSystemGroup.ShouldPredict(tick, predictedGhostComponents[ent]))
                            {
                                continue;
                            }

                            if (entities[ent] != backupEntities[ent])
                            {
                                continue;
                            }

                            var compData = (byte *)chunk.GetDynamicComponentDataArrayReinterpret <byte>(ghostChunkComponentTypesPtr[action.compIndex], action.compSize).GetUnsafePtr();

                            void *usrDataPtr = null;
                            if (action.userTypeId >= 0 && chunk.Has(userTypes[action.userTypeId]))
                            {
                                var usrData = (byte *)chunk.GetDynamicComponentDataArrayReinterpret <byte>(userTypes[action.userTypeId], action.userTypeSize).GetUnsafeReadOnlyPtr();
                                usrDataPtr = usrData + action.userTypeSize * ent;
                            }

                            byte *dataPtr = ((byte *)state) + action.compBackupOffset;
                            action.action.Ptr.Invoke(compData + action.compSize * ent, dataPtr + action.compSize * ent,
                                                     usrDataPtr);
                        }
                    }
                }

                var linkedEntityGroupAccessor = chunk.GetBufferAccessor(linkedEntityGroupType);
                foreach (var action in childActions)
                {
                    for (int ent = 0; ent < chunk.Count; ++ent)
                    {
                        // If this entity did not predict anything there was no rollback and no need to debug it
                        if (!GhostPredictionSystemGroup.ShouldPredict(tick, predictedGhostComponents[ent]))
                        {
                            continue;
                        }
                        if (entities[ent] != backupEntities[ent])
                        {
                            continue;
                        }
                        var linkedEntityGroup = linkedEntityGroupAccessor[ent];
                        if (childEntityLookup.TryGetValue(linkedEntityGroup[action.entityIndex].Value, out var childChunk) &&
                            childChunk.chunk.Has(ghostChunkComponentTypesPtr[action.compIndex]))
                        {
                            var compData = (byte *)childChunk.chunk.GetDynamicComponentDataArrayReinterpret <byte>(ghostChunkComponentTypesPtr[action.compIndex], action.compSize).GetUnsafePtr();

                            void *usrDataPtr = null;
                            if (action.userTypeId >= 0 && chunk.Has(userTypes[action.userTypeId]))
                            {
                                var usrData = (byte *)chunk.GetDynamicComponentDataArrayReinterpret <byte>(userTypes[action.userTypeId], action.userTypeSize).GetUnsafeReadOnlyPtr();
                                usrDataPtr = usrData + action.userTypeSize * ent;
                            }

                            byte *dataPtr = ((byte *)state) + action.compBackupOffset;
                            action.action.Ptr.Invoke(compData + action.compSize * childChunk.index, dataPtr + action.compSize * ent, usrDataPtr);
                        }
                    }
                }
            }
            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;
                        }
                    }
                }
            }
Beispiel #11
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);
                    }
                }
            }
Beispiel #12
0
            public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex, DynamicComponentTypeHandle *ghostChunkComponentTypesPtr, int ghostChunkComponentTypesLength)
            {
                var GhostTypeCollection      = GhostTypeCollectionFromEntity[GhostCollectionSingleton];
                var GhostComponentIndex      = GhostComponentIndexFromEntity[GhostCollectionSingleton];
                var GhostComponentCollection = GhostComponentCollectionFromEntity[GhostCollectionSingleton];

                var ghostComponents = chunk.GetNativeArray(ghostType);

                int ghostTypeId = ghostComponents.GetFirstGhostTypeId();

                if (ghostTypeId < 0)
                {
                    return;
                }
                var typeData = GhostTypeCollection[ghostTypeId];

                var singleEntitySize = UnsafeUtility.SizeOf <Entity>();
                int baseOffset       = typeData.FirstComponent;

                if (!predictionState.TryGetValue(chunk, out var state) ||
                    (*(PredictionBackupState *)state).ghostType != ghostTypeId ||
                    (*(PredictionBackupState *)state).entityCapacity != chunk.Capacity)
                {
                    int dataSize = 0;
                    // Sum up the size of all components rounded up
                    for (int comp = 0; comp < typeData.NumComponents; ++comp)
                    {
                        int compIdx       = GhostComponentIndex[baseOffset + comp].ComponentIndex;
                        int serializerIdx = GhostComponentIndex[baseOffset + comp].SerializerIndex;
#if ENABLE_UNITY_COLLECTIONS_CHECKS
                        if (compIdx >= ghostChunkComponentTypesLength)
                        {
                            throw new System.InvalidOperationException("Component index out of range");
                        }
#endif
                        if ((GhostComponentIndex[baseOffset + comp].SendMask & requiredSendMask) == 0)
                        {
                            continue;
                        }

                        //for buffers we store a a pair of uint:
                        // uint length: the num of elements
                        // uint backupDataOffset: the start position in the backup buffer
                        if (!GhostComponentCollection[serializerIdx].ComponentType.IsBuffer)
                        {
                            dataSize += PredictionBackupState.GetDataSize(
                                GhostComponentCollection[serializerIdx].ComponentSize, chunk.Capacity);
                        }
                        else
                        {
                            dataSize += PredictionBackupState.GetDataSize(GhostSystemConstants.DynamicBufferComponentSnapshotSize, chunk.Capacity);
                        }
                    }

                    //compute the space necessary to store the dynamic buffers data for the chunk
                    int buffersDataCapacity = 0;
                    if (typeData.NumBuffers > 0)
                    {
                        buffersDataCapacity = GetChunkBuffersDataSize(typeData, chunk, ghostChunkComponentTypesPtr, ghostChunkComponentTypesLength, GhostComponentIndex, GhostComponentCollection);
                    }

                    // Chunk does not exist in the history, or has changed ghost type in which case we need to create a new one
                    state = PredictionBackupState.AllocNew(ghostTypeId, dataSize, chunk.Capacity, buffersDataCapacity);
                    newPredictionState.Enqueue(new PredictionStateEntry {
                        chunk = chunk, data = state
                    });
                }
                else
                {
                    stillUsedPredictionState.TryAdd(chunk, 1);
                    if (typeData.NumBuffers > 0)
                    {
                        //resize the backup state to fit the dynamic buffers contents
                        var buffersDataCapacity      = GetChunkBuffersDataSize(typeData, chunk, ghostChunkComponentTypesPtr, ghostChunkComponentTypesLength, GhostComponentIndex, GhostComponentCollection);
                        int bufferBackupDataCapacity = PredictionBackupState.GetBufferDataCapacity(state);
                        if (bufferBackupDataCapacity < buffersDataCapacity)
                        {
                            var dataSize = ((PredictionBackupState *)state)->dataSize;
                            var newState = PredictionBackupState.AllocNew(ghostTypeId, dataSize, chunk.Capacity, buffersDataCapacity);
                            UnsafeUtility.Free((void *)state, Allocator.Persistent);
                            state = newState;
                            updatedPredictionState.Enqueue(new PredictionStateEntry {
                                chunk = chunk, data = newState
                            });
                        }
                    }
                }
                Entity *entities    = PredictionBackupState.GetEntities(state);
                var     srcEntities = chunk.GetNativeArray(entityType).GetUnsafeReadOnlyPtr();
                UnsafeUtility.MemCpy(entities, srcEntities, chunk.Count * singleEntitySize);
                if (chunk.Count < chunk.Capacity)
                {
                    UnsafeUtility.MemClear(entities + chunk.Count, (chunk.Capacity - chunk.Count) * singleEntitySize);
                }

                byte *dataPtr             = PredictionBackupState.GetData(state);
                byte *bufferBackupDataPtr = PredictionBackupState.GetBufferDataPtr(state);

                int numBaseComponents      = typeData.NumComponents - typeData.NumChildComponents;
                int bufferBackupDataOffset = 0;
                for (int comp = 0; comp < numBaseComponents; ++comp)
                {
                    int compIdx       = GhostComponentIndex[baseOffset + comp].ComponentIndex;
                    int serializerIdx = GhostComponentIndex[baseOffset + comp].SerializerIndex;
#if ENABLE_UNITY_COLLECTIONS_CHECKS
                    if (compIdx >= ghostChunkComponentTypesLength)
                    {
                        throw new System.InvalidOperationException("Component index out of range");
                    }
#endif
                    if ((GhostComponentIndex[baseOffset + comp].SendMask & requiredSendMask) == 0)
                    {
                        continue;
                    }

                    var compSize = GhostComponentCollection[serializerIdx].ComponentType.IsBuffer
                        ? GhostSystemConstants.DynamicBufferComponentSnapshotSize
                        : GhostComponentCollection[serializerIdx].ComponentSize;

                    if (!chunk.Has(ghostChunkComponentTypesPtr[compIdx]))
                    {
                        UnsafeUtility.MemClear(dataPtr, chunk.Count * compSize);
                    }
                    else if (!GhostComponentCollection[serializerIdx].ComponentType.IsBuffer)
                    {
                        var compData = (byte *)chunk.GetDynamicComponentDataArrayReinterpret <byte>(ghostChunkComponentTypesPtr[compIdx], compSize).GetUnsafeReadOnlyPtr();
                        UnsafeUtility.MemCpy(dataPtr, compData, chunk.Count * compSize);
                    }
                    else
                    {
                        var bufferData  = chunk.GetUntypedBufferAccessor(ref ghostChunkComponentTypesPtr[compIdx]);
                        var bufElemSize = GhostComponentCollection[serializerIdx].ComponentSize;
                        //Use local variable to iterate and set the buffer offset and length. The dataptr must be
                        //advanced "per chunk" to the next correct position
                        var tempDataPtr = dataPtr;
                        for (int i = 0; i < bufferData.Length; ++i)
                        {
                            //Retrieve an copy each buffer data. Set size and offset in the backup buffer in the component backup
                            var bufferPtr = bufferData.GetUnsafeReadOnlyPtrAndLength(i, out var size);
                            ((int *)tempDataPtr)[0] = size;
                            ((int *)tempDataPtr)[1] = bufferBackupDataOffset;
                            if (size > 0)
                            {
                                UnsafeUtility.MemCpy(bufferBackupDataPtr + bufferBackupDataOffset, (byte *)bufferPtr, size * bufElemSize);
                            }
                            bufferBackupDataOffset += size * bufElemSize;
                            tempDataPtr            += compSize;
                        }
                        bufferBackupDataOffset = GhostCollectionSystem.SnapshotSizeAligned(bufferBackupDataOffset);
                    }
                    dataPtr = PredictionBackupState.GetNextData(dataPtr, compSize, chunk.Capacity);
                }
                if (typeData.NumChildComponents > 0)
                {
                    var linkedEntityGroupAccessor = chunk.GetBufferAccessor(linkedEntityGroupType);
                    for (int comp = numBaseComponents; comp < typeData.NumComponents; ++comp)
                    {
                        int compIdx       = GhostComponentIndex[baseOffset + comp].ComponentIndex;
                        int serializerIdx = GhostComponentIndex[baseOffset + comp].SerializerIndex;
#if ENABLE_UNITY_COLLECTIONS_CHECKS
                        if (compIdx >= ghostChunkComponentTypesLength)
                        {
                            throw new System.InvalidOperationException("Component index out of range");
                        }
#endif
                        if ((GhostComponentIndex[baseOffset + comp].SendMask & requiredSendMask) == 0)
                        {
                            continue;
                        }

                        var isBuffer = GhostComponentCollection[serializerIdx].ComponentType.IsBuffer;
                        var compSize = isBuffer ? GhostSystemConstants.DynamicBufferComponentSnapshotSize : GhostComponentCollection[serializerIdx].ComponentSize;
                        if (!GhostComponentCollection[serializerIdx].ComponentType.IsBuffer)
                        {
                            //use a temporary for the iteration here. Otherwise when the dataptr is offset for the chunk, we
                            //end up in the wrong position
                            var tempDataPtr = dataPtr;
                            for (int ent = 0; ent < chunk.Count; ++ent)
                            {
                                var linkedEntityGroup = linkedEntityGroupAccessor[ent];
                                if (childEntityLookup.TryGetValue(linkedEntityGroup[GhostComponentIndex[baseOffset + comp].EntityIndex].Value, out var childChunk) &&
                                    childChunk.chunk.Has(ghostChunkComponentTypesPtr[compIdx]))
                                {
                                    var compData = (byte *)childChunk.chunk.GetDynamicComponentDataArrayReinterpret <byte>(ghostChunkComponentTypesPtr[compIdx], compSize).GetUnsafeReadOnlyPtr();
                                    UnsafeUtility.MemCpy(tempDataPtr, compData + childChunk.index * compSize, compSize);
                                }
                                else
                                {
                                    UnsafeUtility.MemClear(tempDataPtr, compSize);
                                }
                                tempDataPtr += compSize;
                            }
                        }
                        else
                        {
                            var bufElemSize = GhostComponentCollection[serializerIdx].ComponentSize;
                            var tempDataPtr = dataPtr;
                            for (int ent = 0; ent < chunk.Count; ++ent)
                            {
                                var linkedEntityGroup = linkedEntityGroupAccessor[ent];
                                if (childEntityLookup.TryGetValue(linkedEntityGroup[GhostComponentIndex[baseOffset + comp].EntityIndex].Value, out var childChunk) &&
                                    childChunk.chunk.Has(ghostChunkComponentTypesPtr[compIdx]))
                                {
                                    var bufferData = childChunk.chunk.GetUntypedBufferAccessor(ref ghostChunkComponentTypesPtr[compIdx]);
                                    //Retrieve an copy each buffer data. Set size and offset in the backup buffer in the component backup
                                    var bufferPtr = bufferData.GetUnsafeReadOnlyPtrAndLength(childChunk.index, out var size);
                                    ((int *)tempDataPtr)[0] = size;
                                    ((int *)tempDataPtr)[1] = bufferBackupDataOffset;
                                    if (size > 0)
                                    {
                                        UnsafeUtility.MemCpy(bufferBackupDataPtr + bufferBackupDataOffset, (byte *)bufferPtr, size * bufElemSize);
                                    }
                                    bufferBackupDataOffset += size * bufElemSize;
                                }
                                else
                                {
                                    //reset the entry to 0. Don't use memcpy in this case (is faster this way)
                                    ((long *)tempDataPtr)[0] = 0;
                                }
                                tempDataPtr += compSize;
                            }
                            bufferBackupDataOffset = GhostCollectionSystem.SnapshotSizeAligned(bufferBackupDataOffset);
                        }
                        dataPtr = PredictionBackupState.GetNextData(dataPtr, compSize, chunk.Capacity);
                    }
                }
            }
Beispiel #13
0
            public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex, DynamicComponentTypeHandle *ghostChunkComponentTypesPtr, int ghostChunkComponentTypesLength)
            {
                if (!predictionState.TryGetValue(chunk, out var state) ||
                    (*(PredictionBackupState *)state).entityCapacity != chunk.Capacity)
                {
                    return;
                }

                var GhostTypeCollection      = GhostTypeCollectionFromEntity[GhostCollectionSingleton];
                var GhostComponentIndex      = GhostComponentIndexFromEntity[GhostCollectionSingleton];
                var GhostComponentCollection = GhostComponentCollectionFromEntity[GhostCollectionSingleton];

                var ghostComponents = chunk.GetNativeArray(ghostType);
                int ghostTypeId     = ghostComponents.GetFirstGhostTypeId();

                if (ghostTypeId < 0)
                {
                    return;
                }
                var typeData   = GhostTypeCollection[ghostTypeId];
                int baseOffset = typeData.FirstComponent;

                Entity *backupEntities = PredictionBackupState.GetEntities(state);
                var     entities       = chunk.GetNativeArray(entityType);

                var predictedGhostComponents = chunk.GetNativeArray(predictedGhostType);

                byte *dataPtr           = PredictionBackupState.GetData(state);
                int   numBaseComponents = typeData.NumComponents - typeData.NumChildComponents;

                for (int comp = 0; comp < numBaseComponents; ++comp)
                {
                    int compIdx       = GhostComponentIndex[baseOffset + comp].ComponentIndex;
                    int serializerIdx = GhostComponentIndex[baseOffset + comp].SerializerIndex;
#if ENABLE_UNITY_COLLECTIONS_CHECKS
                    if (compIdx >= ghostChunkComponentTypesLength)
                    {
                        throw new System.InvalidOperationException("Component index out of range");
                    }
#endif
                    if ((GhostComponentIndex[baseOffset + comp].SendMask & requiredSendMask) == 0)
                    {
                        continue;
                    }

                    var compSize = GhostComponentCollection[serializerIdx].ComponentType.IsBuffer
                        ? GhostSystemConstants.DynamicBufferComponentSnapshotSize
                        : GhostComponentCollection[serializerIdx].ComponentSize;
                    if (chunk.Has(ghostChunkComponentTypesPtr[compIdx]))
                    {
                        if (!GhostComponentCollection[serializerIdx].ComponentType.IsBuffer)
                        {
                            var compData = (byte *)chunk.GetDynamicComponentDataArrayReinterpret <byte>(ghostChunkComponentTypesPtr[compIdx], compSize).GetUnsafeReadOnlyPtr();
                            for (int ent = 0; ent < entities.Length; ++ent)
                            {
                                // If this entity did not predict anything there was no rollback and no need to debug it
                                if (!GhostPredictionSystemGroup.ShouldPredict(tick, predictedGhostComponents[ent]))
                                {
                                    continue;
                                }
                                if (entities[ent] == backupEntities[ent])
                                {
                                    int errorIndex = GhostComponentIndex[baseOffset + comp].PredictionErrorBaseIndex;

                                    var errors = new UnsafeList <float>(((float *)predictionErrors.GetUnsafePtr()) + errorIndex + ThreadIndex * numPredictionErrors, numPredictionErrors - errorIndex);
                                    GhostComponentCollection[serializerIdx].ReportPredictionErrors.Ptr.Invoke((System.IntPtr)(compData + compSize * ent), (System.IntPtr)(dataPtr + compSize * ent), ref errors);
                                }
                            }
                        }
                        else
                        {
                            //FIXME Buffers need to report error for the size and an aggregate for each element in the buffer
                        }
                    }

                    dataPtr = PredictionBackupState.GetNextData(dataPtr, compSize, chunk.Capacity);
                }
                if (typeData.NumChildComponents > 0)
                {
                    var linkedEntityGroupAccessor = chunk.GetBufferAccessor(linkedEntityGroupType);
                    for (int comp = numBaseComponents; comp < typeData.NumComponents; ++comp)
                    {
                        int compIdx       = GhostComponentIndex[baseOffset + comp].ComponentIndex;
                        int serializerIdx = GhostComponentIndex[baseOffset + comp].SerializerIndex;
#if ENABLE_UNITY_COLLECTIONS_CHECKS
                        if (compIdx >= ghostChunkComponentTypesLength)
                        {
                            throw new System.InvalidOperationException("Component index out of range");
                        }
#endif
                        if ((GhostComponentIndex[baseOffset + comp].SendMask & requiredSendMask) == 0)
                        {
                            continue;
                        }

                        var compSize = GhostComponentCollection[serializerIdx].ComponentType.IsBuffer
                            ? GhostSystemConstants.DynamicBufferComponentSnapshotSize
                            : GhostComponentCollection[serializerIdx].ComponentSize;
                        var entityIdx = GhostComponentIndex[baseOffset + comp].EntityIndex;

                        for (int ent = 0; ent < chunk.Count; ++ent)
                        {
                            // If this entity did not predict anything there was no rollback and no need to debug it
                            if (!GhostPredictionSystemGroup.ShouldPredict(tick, predictedGhostComponents[ent]))
                            {
                                continue;
                            }
                            if (entities[ent] != backupEntities[ent])
                            {
                                continue;
                            }
                            var linkedEntityGroup = linkedEntityGroupAccessor[ent];
                            if (childEntityLookup.TryGetValue(linkedEntityGroup[entityIdx].Value, out var childChunk) &&
                                childChunk.chunk.Has(ghostChunkComponentTypesPtr[compIdx]))
                            {
                                if (!GhostComponentCollection[serializerIdx].ComponentType.IsBuffer)
                                {
                                    var compData   = (byte *)childChunk.chunk.GetDynamicComponentDataArrayReinterpret <byte>(ghostChunkComponentTypesPtr[compIdx], compSize).GetUnsafeReadOnlyPtr();
                                    int errorIndex = GhostComponentIndex[baseOffset + comp].PredictionErrorBaseIndex;
                                    var errors     = new UnsafeList <float>(((float *)predictionErrors.GetUnsafePtr()) + errorIndex + ThreadIndex * numPredictionErrors, numPredictionErrors - errorIndex);
                                    GhostComponentCollection[serializerIdx].ReportPredictionErrors.Ptr.Invoke((System.IntPtr)(compData + compSize * childChunk.index), (System.IntPtr)(dataPtr + compSize * ent), ref errors);
                                }
                                else
                                {
                                    //FIXME Buffers need to report error for the size and an aggregate for each element in the buffer
                                }
                            }
                        }

                        dataPtr = PredictionBackupState.GetNextData(dataPtr, compSize, chunk.Capacity);
                    }
                }
            }