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
                    });
                }
            }
Ejemplo n.º 2
0
        protected unsafe override void OnUpdate()
        {
            m_GhostReceiveSystem.LastGhostMapWriter.Complete();

            var interpolationTargetTick = m_ClientSimulationSystemGroup.InterpolationTick;

            if (m_ClientSimulationSystemGroup.InterpolationTickFraction < 1)
            {
                --interpolationTargetTick;
            }
            //var predictionTargetTick = m_ClientSimulationSystemGroup.ServerTick;
            var prefabsEntity = GetSingletonEntity <GhostCollection>();
            var prefabs       = EntityManager.GetBuffer <GhostCollectionPrefab>(prefabsEntity).ToNativeArray(Allocator.Temp);

            var ghostSpawnEntity            = GetSingletonEntity <GhostSpawnQueueComponent>();
            var ghostSpawnBufferComponent   = EntityManager.GetBuffer <GhostSpawnBuffer>(ghostSpawnEntity);
            var snapshotDataBufferComponent = EntityManager.GetBuffer <SnapshotDataBuffer>(ghostSpawnEntity);
            var ghostSpawnBuffer            = ghostSpawnBufferComponent.ToNativeArray(Allocator.Temp);
            var snapshotDataBuffer          = snapshotDataBufferComponent.ToNativeArray(Allocator.Temp);

            ghostSpawnBufferComponent.ResizeUninitialized(0);
            snapshotDataBufferComponent.ResizeUninitialized(0);

            var spawnedGhosts            = new NativeList <SpawnedGhostMapping>(16, Allocator.Temp);
            var nonSpawnedGhosts         = new NativeList <NonSpawnedGhostMapping>(16, Allocator.Temp);
            var ghostCollectionSingleton = GetSingletonEntity <GhostCollection>();

            for (int i = 0; i < ghostSpawnBuffer.Length; ++i)
            {
                var    ghost               = ghostSpawnBuffer[i];
                Entity entity              = Entity.Null;
                byte * snapshotData        = null;
                var    ghostTypeCollection = EntityManager.GetBuffer <GhostCollectionPrefabSerializer>(ghostCollectionSingleton);
                var    snapshotSize        = ghostTypeCollection[ghost.GhostType].SnapshotSize;
                bool   hasBuffers          = ghostTypeCollection[ghost.GhostType].NumBuffers > 0;
                if (ghost.SpawnType == GhostSpawnBuffer.Type.Interpolated)
                {
                    // Add to m_DelayedSpawnQueue
                    entity = EntityManager.CreateEntity();
                    EntityManager.AddComponentData(entity, new GhostComponent {
                        ghostId = ghost.GhostID, ghostType = ghost.GhostType, spawnTick = ghost.ServerSpawnTick
                    });
                    var newBuffer = EntityManager.AddBuffer <SnapshotDataBuffer>(entity);
                    newBuffer.ResizeUninitialized(snapshotSize * GhostSystemConstants.SnapshotHistorySize);
                    snapshotData = (byte *)newBuffer.GetUnsafePtr();
                    //Add also the SnapshotDynamicDataBuffer if the entity has buffers to copy the dynamic contents
                    if (hasBuffers)
                    {
                        EntityManager.AddBuffer <SnapshotDynamicDataBuffer>(entity);
                    }
                    EntityManager.AddComponentData(entity, new SnapshotData {
                        SnapshotSize = snapshotSize, LatestIndex = 0
                    });
                    m_DelayedSpawnQueue.Enqueue(new GhostSpawnSystem.DelayedSpawnGhost {
                        ghostId = ghost.GhostID, ghostType = ghost.GhostType, clientSpawnTick = ghost.ClientSpawnTick, serverSpawnTick = ghost.ServerSpawnTick, oldEntity = entity
                    });
                    nonSpawnedGhosts.Add(new NonSpawnedGhostMapping {
                        ghostId = ghost.GhostID, entity = entity
                    });
                }
                else if (ghost.SpawnType == GhostSpawnBuffer.Type.Predicted)
                {
                    // TODO: this could allow some time for the prefab to load before giving an error
                    if (prefabs[ghost.GhostType].GhostPrefab == Entity.Null)
                    {
                        ReportMissingPrefab();
                        continue;
                    }
                    // Spawn directly
                    entity = ghost.PredictedSpawnEntity != Entity.Null ? ghost.PredictedSpawnEntity : EntityManager.Instantiate(prefabs[ghost.GhostType].GhostPrefab);
                    if (EntityManager.HasComponent <GhostPrefabMetaDataComponent>(prefabs[ghost.GhostType].GhostPrefab))
                    {
                        ref var toRemove = ref EntityManager.GetComponentData <GhostPrefabMetaDataComponent>(prefabs[ghost.GhostType].GhostPrefab).Value.Value.DisableOnPredictedClient;
                        //Need copy because removing component will invalidate the buffer pointer, since introduce structural changes
                        var linkedEntityGroup = EntityManager.GetBuffer <LinkedEntityGroup>(entity).ToNativeArray(Allocator.Temp);
                        for (int rm = 0; rm < toRemove.Length; ++rm)
                        {
                            var compType = ComponentType.ReadWrite(TypeManager.GetTypeIndexFromStableTypeHash(toRemove[rm].StableHash));
                            EntityManager.RemoveComponent(linkedEntityGroup[toRemove[rm].EntityIndex].Value, compType);
                        }
                    }
                    EntityManager.SetComponentData(entity, new GhostComponent {
                        ghostId = ghost.GhostID, ghostType = ghost.GhostType, spawnTick = ghost.ServerSpawnTick
                    });
                    var newBuffer = EntityManager.GetBuffer <SnapshotDataBuffer>(entity);
                    newBuffer.ResizeUninitialized(snapshotSize * GhostSystemConstants.SnapshotHistorySize);
                    snapshotData = (byte *)newBuffer.GetUnsafePtr();
                    EntityManager.SetComponentData(entity, new SnapshotData {
                        SnapshotSize = snapshotSize, LatestIndex = 0
                    });
                    spawnedGhosts.Add(new SpawnedGhostMapping {
                        ghost = new SpawnedGhost {
                            ghostId = ghost.GhostID, spawnTick = ghost.ServerSpawnTick
                        }, entity = entity
                    });
                }
                if (entity != Entity.Null)
                {
                    UnsafeUtility.MemClear(snapshotData, snapshotSize * GhostSystemConstants.SnapshotHistorySize);
                    UnsafeUtility.MemCpy(snapshotData, (byte *)snapshotDataBuffer.GetUnsafeReadOnlyPtr() + ghost.DataOffset, snapshotSize);
                    if (hasBuffers)
                    {
                        //Resize and copy the associated dynamic buffer snapshot data
                        var snapshotDynamicBuffer = EntityManager.GetBuffer <SnapshotDynamicDataBuffer>(entity);
                        var dynamicDataCapacity   = SnapshotDynamicBuffersHelper.CalculateBufferCapacity(ghost.DynamicDataSize, out var _);
                        snapshotDynamicBuffer.ResizeUninitialized((int)dynamicDataCapacity);
                        var dynamicSnapshotData = (byte *)snapshotDynamicBuffer.GetUnsafePtr();
                        if (dynamicSnapshotData == null)
                        {
                            throw new InvalidOperationException("snapshot dynamic data buffer not initialized but ghost has dynamic buffer contents");
                        }

                        // Update the dynamic data header (uint[GhostSystemConstants.SnapshotHistorySize)]) by writing the used size for the current slot
                        // (for new spawned entity is 0). Is un-necessary to initialize all the header slots to 0 since that information is only used
                        // for sake of delta compression and, because that depend on the acked tick, only initialized and relevant slots are accessed in general.
                        // For more information about the layout, see SnapshotData.cs.
                        ((uint *)dynamicSnapshotData)[0] = ghost.DynamicDataSize;
                        var headerSize = SnapshotDynamicBuffersHelper.GetHeaderSize();
                        UnsafeUtility.MemCpy(dynamicSnapshotData + headerSize, (byte *)snapshotDataBuffer.GetUnsafeReadOnlyPtr() + ghost.DataOffset + snapshotSize, ghost.DynamicDataSize);
                    }
                }
            }