public unsafe void Execute() { for (int chunk = 0; chunk < spawnChunks.Length; ++chunk) { var entities = spawnChunks[chunk].GetNativeArray(entityType); int ghostType = serializers.FindSerializer(spawnChunks[chunk].Archetype); var ghostState = (GhostSystemStateComponent *)UnsafeUtility.Malloc( UnsafeUtility.SizeOf <GhostSystemStateComponent>() * entities.Length, UnsafeUtility.AlignOf <GhostSystemStateComponent>(), Allocator.TempJob); for (var ent = 0; ent < entities.Length; ++ent) { int newId; if (!freeGhostIds.TryDequeue(out newId)) { newId = allocatedGhostIds[0]; allocatedGhostIds[0] = newId + 1; } ghostState[ent] = new GhostSystemStateComponent { ghostId = newId, ghostTypeIndex = ghostType, despawnTick = 0 }; // This runs after simulation. If an entity is created in the begin barrier and destroyed before this // runs there can be errors. To get around those we add the ghost system state component before everything // using the begin barrier, and set the value here commandBuffer.SetComponent(entities[ent], ghostState[ent]); } var pc = new PrioChunk { chunk = spawnChunks[chunk], ghostState = ghostState, priority = serializers.CalculateImportance(ghostType, spawnChunks[chunk]), // Age is always 1 for new chunks startIndex = 0, ghostType = ghostType }; serialSpawnChunks.Add(pc); } }
protected override unsafe void OnUpdate() { var preSpawnsAlreadyProcessed = EntityManager.CreateEntityQuery(ComponentType.ReadOnly <PreSpawnsInitialized>()) .CalculateEntityCount(); if (preSpawnsAlreadyProcessed > 0) { // Check if an uninitialized scene has appeared with no ghosts present inside, then just mark it as initialized and continue var prespawns = m_Prespawns.ToEntityArray(Allocator.TempJob); var newScenes = m_UninitializedScenes.ToEntityArray(Allocator.TempJob); for (int j = 0; j < newScenes.Length; ++j) { for (int i = 0; i < prespawns.Length; ++i) { var scenes = EntityManager.GetSharedComponentData <SceneSection>(prespawns[i]); var sceneSystem = World.GetExistingSystem <SceneSystem>(); var sceneEntity = sceneSystem.GetSceneEntity(scenes.SceneGUID); if (sceneEntity == newScenes[j]) { UnityEngine.Debug.LogError("[" + World.Name + "] Prespawned ghosts have already been initialized, this needs to happen for all subscenes at the same time."); return; } } EntityManager.AddComponent <PreSpawnsInitialized>(newScenes[j]); } newScenes.Dispose(); prespawns.Dispose(); return; } // Handle the chunk for an entity type, then handle each entity in the chunk (prespawned entities) var prespawnChunk = m_Prespawns.CreateArchetypeChunkArray(Allocator.TempJob); var entityType = GetEntityTypeHandle(); var preSpawnedIds = GetComponentDataFromEntity <PreSpawnedGhostId>(); var subsceneMap = new NativeMultiHashMap <ulong, Entity>(32, Allocator.Temp); var subscenePadding = new NativeHashMap <ulong, int>(32, Allocator.Temp); var ghostComponents = GetComponentDataFromEntity <GhostComponent>(); // Put all known subscene hashes tracked by the prespawned ghosts into a map for sorting for (int i = 0; i < prespawnChunk.Length; ++i) { var chunk = prespawnChunk[i]; var entities = chunk.GetNativeArray(entityType); for (int j = 0; j < entities.Length; ++j) { var entity = entities[j]; var subsceneHash = EntityManager.GetSharedComponentData <SubSceneGhostComponentHash>(entity).Value; subsceneMap.Add(subsceneHash, entity); } } var subsceneArray = subsceneMap.GetUniqueKeyArray(Allocator.Temp); // Figure out scene id padding or how many IDs were used by the previous scenes in the sorted list, continue // where it left off so each ghost in a scene starts of at the ID+1 of last ghost in the previous scene var scenePadding = 0; for (int i = 0; i < subsceneArray.Item2; ++i) { subscenePadding.Add(subsceneArray.Item1[i], scenePadding); scenePadding += subsceneMap.CountValuesForKey(subsceneArray.Item1[i]); } var PostUpdateCommands = new EntityCommandBuffer(Allocator.Temp); var serverSystems = World.GetExistingSystem <ServerSimulationSystemGroup>(); var ghostTypes = GetComponentDataFromEntity <GhostTypeComponent>(); var ghostPrefabBufferFromEntity = GetBufferFromEntity <GhostPrefabBuffer>(true); var prefabEntity = GetSingletonEntity <GhostPrefabCollectionComponent>(); var ghostReceiveSystem = World.GetExistingSystem <GhostReceiveSystem>(); var ghostSendSystem = World.GetExistingSystem <GhostSendSystem>(); var ghostCollectionSystem = World.GetExistingSystem <GhostCollectionSystem>(); DynamicBuffer <GhostPrefabBuffer> prefabList = ghostPrefabBufferFromEntity[prefabEntity]; int highestPrespawnId = -1; var spawnedGhosts = new NativeList <SpawnedGhostMapping>(1024, Allocator.Temp); for (int i = 0; i < prespawnChunk.Length; ++i) { var chunk = prespawnChunk[i]; var entities = chunk.GetNativeArray(entityType); for (int j = 0; j < entities.Length; ++j) { var entity = entities[j]; var ghostTypeComponent = ghostTypes[entity]; int ghostType; for (ghostType = 0; ghostType < prefabList.Length; ++ghostType) { if (ghostTypes[prefabList[ghostType].Value] == ghostTypeComponent) { break; } } if (ghostType >= prefabList.Length) { UnityEngine.Debug.LogError("Failed to look up ghost type for entity"); return; } // Check if this entity has already been handled if (ghostComponents[entity].ghostId != 0) { UnityEngine.Debug.LogWarning(entity + " already has ghostId=" + ghostComponents[entity].ghostId + " prespawn=" + preSpawnedIds[entity].Value); continue; } // Modfy the entity to its proper version if (EntityManager.HasComponent <GhostPrefabMetaDataComponent>(prefabList[ghostType].Value)) { ref var ghostMetaData = ref EntityManager.GetComponentData <GhostPrefabMetaDataComponent>(prefabList[ghostType].Value).Value.Value; if (serverSystems != null) { for (int rm = 0; rm < ghostMetaData.RemoveOnServer.Length; ++rm) { var rmCompType = ComponentType.ReadWrite(TypeManager.GetTypeIndexFromStableTypeHash(ghostMetaData.RemoveOnServer[rm])); PostUpdateCommands.RemoveComponent(entity, rmCompType); } } else { for (int rm = 0; rm < ghostMetaData.RemoveOnClient.Length; ++rm) { var rmCompType = ComponentType.ReadWrite(TypeManager.GetTypeIndexFromStableTypeHash(ghostMetaData.RemoveOnClient[rm])); PostUpdateCommands.RemoveComponent(entity, rmCompType); } // FIXME: should disable instead of removing once we have a way of doing that without structural changes if (ghostMetaData.DefaultMode == GhostPrefabMetaData.GhostMode.Predicted) { for (int rm = 0; rm < ghostMetaData.DisableOnPredictedClient.Length; ++rm) { var rmCompType = ComponentType.ReadWrite(TypeManager.GetTypeIndexFromStableTypeHash(ghostMetaData.DisableOnPredictedClient[rm])); PostUpdateCommands.RemoveComponent(entity, rmCompType); } } else if (ghostMetaData.DefaultMode == GhostPrefabMetaData.GhostMode.Interpolated) { for (int rm = 0; rm < ghostMetaData.DisableOnInterpolatedClient.Length; ++rm) { var rmCompType = ComponentType.ReadWrite(TypeManager.GetTypeIndexFromStableTypeHash(ghostMetaData.DisableOnInterpolatedClient[rm])); PostUpdateCommands.RemoveComponent(entity, rmCompType); } } } } var subsceneHash = EntityManager.GetSharedComponentData <SubSceneGhostComponentHash>(entity).Value; var newId = preSpawnedIds[entity].Value + subscenePadding[subsceneHash]; if (newId > highestPrespawnId) { highestPrespawnId = newId; } // If on a server we need to allocate the ghost ID for the pre-spawned entity so runtime spawns // will happen from the right start index if (serverSystems != null) { spawnedGhosts.Add(new SpawnedGhostMapping { ghost = new SpawnedGhost { ghostId = newId, spawnTick = 0 }, entity = entity }); var ghostSystemStateComponent = new GhostSystemStateComponent { ghostId = newId, despawnTick = 0, spawnTick = 0 }; PostUpdateCommands.AddComponent(entity, ghostSystemStateComponent); } else if (ghostReceiveSystem != null) { var snapshotSize = ghostCollectionSystem.m_GhostTypeCollection[ghostType].SnapshotSize; spawnedGhosts.Add(new SpawnedGhostMapping { ghost = new SpawnedGhost { ghostId = newId, spawnTick = 0 }, entity = entity }); var newBuffer = PostUpdateCommands.SetBuffer <SnapshotDataBuffer>(entity); newBuffer.ResizeUninitialized(snapshotSize * GhostSystemConstants.SnapshotHistorySize); UnsafeUtility.MemClear(newBuffer.GetUnsafePtr(), snapshotSize * GhostSystemConstants.SnapshotHistorySize); PostUpdateCommands.SetComponent(entity, new SnapshotData { SnapshotSize = snapshotSize, LatestIndex = 0 }); } // Pre-spawned uses spawnTick = 0, if there is a reference to a ghost and it has spawnTick 0 the ref is always resolved // This works because there despawns are high priority and we never create pre-spawned ghosts after connection var ghostComponent = new GhostComponent { ghostId = newId, ghostType = ghostType, spawnTick = 0 }; PostUpdateCommands.SetComponent(entity, ghostComponent); // Mark scene as processed, as whole scene will have been loaded when this entity appeared var sceneSection = EntityManager.GetSharedComponentData <SceneSection>(entity); var sceneSystem = World.GetExistingSystem <SceneSystem>(); var sceneEntity = sceneSystem.GetSceneEntity(sceneSection.SceneGUID); PostUpdateCommands.AddComponent <PreSpawnsInitialized>(sceneEntity); } }