/// <summary>
        /// Copies all entities from srcEntityManager and replaces all entities in this EntityManager
        /// </summary>
        /// <remarks>
        /// Gurantees that the chunk layout & order of the entities will match exactly, thus this method can be used for deterministic rollback.
        /// This feature is not complete and only supports a subset of the EntityManager features at the moment:
        /// * Currently it copies all SystemStateComponents (They should not be copied)
        /// * Currently does not support class based components
        /// </remarks>
        public void CopyAndReplaceEntitiesFrom(EntityManager srcEntityManager)
        {
#if ENABLE_UNITY_COLLECTIONS_CHECKS
            if (srcEntityManager == null || !srcEntityManager.IsCreated)
            {
                throw new ArgumentNullException(nameof(srcEntityManager));
            }
            if (!IsCreated)
            {
                throw new ArgumentException("This EntityManager has been destroyed");
            }
#endif

            srcEntityManager.CompleteAllJobs();
            CompleteAllJobs();

            using (var srcChunks = srcEntityManager.m_UniversalQueryWithChunks.CreateArchetypeChunkArray(Allocator.TempJob))
                using (var dstChunks = m_UniversalQueryWithChunks.CreateArchetypeChunkArray(Allocator.TempJob))
                {
                    using (var archetypeChunkChanges = ArchetypeChunkChangeUtility.GetArchetypeChunkChanges(srcChunks, dstChunks, Allocator.TempJob))
                    {
                        EntityManagerDifferUtility.CopyAndReplaceChunks(srcEntityManager, this, m_UniversalQueryWithChunks, archetypeChunkChanges);
                        Unity.Entities.EntityComponentStore.AssertSameEntities(srcEntityManager.EntityComponentStore, EntityComponentStore);
                    }
                }
        }
Esempio n. 2
0
        /// <summary>
        /// Generates a detailed change set between <see cref="srcEntityManager"/> and <see cref="dstEntityManager"/>.
        /// All entities to be considered must have the <see cref="EntityGuid"/> component with a unique value.
        /// The resulting <see cref="EntityChanges"/> must be disposed when no longer needed.
        /// </summary>
        /// <remarks>
        /// For performance the <see cref="dstToSrcSequenceNumbers"/> is used to map chunks between the given worlds. If this is not provided, or is empty
        /// the tracker must evaluate all chunks, entities, and component data.
        ///
        /// When using the <see cref="EntityManagerDifferOptions.FastForwardShadowWorld"/> the destination world must be a direct ancestor to
        /// the source world, and must only be updated using this call or similar methods. There should be no direct changes to destination world.
        /// </remarks>
        internal static unsafe EntityChanges GetChanges(
            EntityManager srcEntityManager,
            EntityQuery srcEntityQuery,
            EntityManager dstEntityManager,
            EntityQuery dstEntityQuery,
            TypeInfoStream typeInfoStream,
            EntityManagerDifferOptions options,
            Allocator allocator)
        {
            var changes = new EntityChanges();

            srcEntityManager.CompleteAllJobs();
            dstEntityManager.CompleteAllJobs();

            if (options != EntityManagerDifferOptions.None)
            {
                var includeForwardChangeSet = (options & EntityManagerDifferOptions.IncludeForwardChangeSet) ==
                                              EntityManagerDifferOptions.IncludeForwardChangeSet;
                var includeReverseChangeSet = (options & EntityManagerDifferOptions.IncludeReverseChangeSet) ==
                                              EntityManagerDifferOptions.IncludeReverseChangeSet;
                var fastForwardShadowWorld = (options & EntityManagerDifferOptions.FastForwardShadowWorld) ==
                                             EntityManagerDifferOptions.FastForwardShadowWorld;
                var clearMissingReferences = (options & EntityManagerDifferOptions.ClearMissingReferences) ==
                                             EntityManagerDifferOptions.ClearMissingReferences;
                var validateUniqueEntityGuids = (options & EntityManagerDifferOptions.ValidateUniqueEntityGuid) ==
                                                EntityManagerDifferOptions.ValidateUniqueEntityGuid;

                // Query chunks that should be considered for change tracking.
                using (var srcChunks = srcEntityQuery.CreateArchetypeChunkArray(Allocator.TempJob))
                    using (var dstChunks = dstEntityQuery.CreateArchetypeChunkArray(Allocator.TempJob))
                    {
                        if (clearMissingReferences)
                        {
                            ArchetypeChunkChangeUtility.ClearMissingReferences(srcChunks,
                                                                               srcEntityManager.EntityComponentStore, srcEntityManager.GlobalSystemVersion,
                                                                               typeInfoStream);
                        }

                        // Compare the chunks and get a set of all changed chunks.
                        // @NOTE A modified chunk will appear as destroyed and then created.
                        using (var archetypeChunkChanges = ArchetypeChunkChangeUtility.GetArchetypeChunkChanges(srcChunks, dstChunks, Allocator.TempJob))
                        {
                            // If we have no chunk-level changes then there is no work to be done.
                            if (archetypeChunkChanges.HasChanges)
                            {
                                if (includeForwardChangeSet || includeReverseChangeSet)
                                {
                                    BuildComponentDataToEntityLookupTask <EntityGuid> buildSrcEntityGuidToEntityLookupTask =
                                        default;

                                    try
                                    {
                                        using (var buildSrcEntitiesTask =
                                                   new BuildEntityInChunkWithComponentTask <EntityGuid>(
                                                       archetypeChunkChanges.CreatedSrcChunks, Allocator.TempJob))
                                            using (var buildDstEntitiesTask =
                                                       new BuildEntityInChunkWithComponentTask <EntityGuid>(
                                                           archetypeChunkChanges.DestroyedDstChunks, Allocator.TempJob))
                                            {
                                                var handle = JobHandle.CombineDependencies(buildSrcEntitiesTask.Schedule(),
                                                                                           buildDstEntitiesTask.Schedule());

                                                if (validateUniqueEntityGuids)
                                                {
                                                    // Validation is expensive since we must run over the entire world. This is opt in.
                                                    buildSrcEntityGuidToEntityLookupTask =
                                                        new BuildComponentDataToEntityLookupTask <EntityGuid>(
                                                            srcEntityQuery.CalculateEntityCount(), Allocator.TempJob);
                                                    handle = JobHandle.CombineDependencies(handle,
                                                                                           buildSrcEntityGuidToEntityLookupTask.Schedule(srcChunks));
                                                }

                                                handle.Complete();

                                                if (validateUniqueEntityGuids &&
                                                    TryGetDuplicateComponents(
                                                        buildSrcEntityGuidToEntityLookupTask.GetComponentDataToEntityMap(),
                                                        out var duplicates))
                                                {
                                                    throw new DuplicateEntityGuidException(message:
                                                                                           $"Found {duplicates.Length} {nameof(EntityGuid)} components that are shared by more than one Entity, " +
                                                                                           $"see the {nameof(DuplicateEntityGuidException.DuplicateEntityGuids)} property for more information.")
                                                          {
                                                              DuplicateEntityGuids = duplicates
                                                          };
                                                }

                                                var srcState = new WorldState(
                                                    srcEntityManager,
                                                    buildSrcEntitiesTask.GetEntities()
                                                    );

                                                var dstState = new WorldState(
                                                    dstEntityManager,
                                                    buildDstEntitiesTask.GetEntities()
                                                    );

                                                BuildChangeSetTask buildForwardChangeSetTask = default;
                                                BuildChangeSetTask buildReverseChangeSetTask = default;

                                                try
                                                {
                                                    JobHandle buildForwardChangeSetJob = default;
                                                    JobHandle buildReverseChangeSetJob = default;

                                                    if (includeForwardChangeSet)
                                                    {
                                                        buildForwardChangeSetTask = new BuildChangeSetTask(dstState, srcState,
                                                                                                           typeInfoStream, Allocator.TempJob);
                                                        buildForwardChangeSetJob = buildForwardChangeSetTask.Schedule();
                                                    }

                                                    if (includeReverseChangeSet)
                                                    {
                                                        buildReverseChangeSetTask = new BuildChangeSetTask(srcState, dstState,
                                                                                                           typeInfoStream, Allocator.TempJob);
                                                        buildReverseChangeSetJob = buildReverseChangeSetTask.Schedule();
                                                    }

                                                    JobHandle.CombineDependencies(buildForwardChangeSetJob,
                                                                                  buildReverseChangeSetJob).Complete();

                                                    changes = new EntityChanges(
                                                        includeForwardChangeSet
                                                    ? buildForwardChangeSetTask.GetChangeSet(allocator)
                                                    : default,
                                                        includeReverseChangeSet
                                                    ? buildReverseChangeSetTask.GetChangeSet(allocator)
                                                    : default