Issue with GhostType definition of the Netcode sample PredictedSpawning

While implementing the PredictedSpawning feature in at test project to learn and gain experiences using ECS netcode I looked into the Netcode sample “PredictedSpawning”:

There I found a small issue in the struct “ClassificationSystem” regarding the “GhostCollectionPrefab” which contains all the ghost prefabs. The prefab supplied by the “GrenadeSpawner” authoring could not find a match in the stated collection. Therefore the line 14 of the OnUpdate-call got never executed which can be identified setting a breakpoint there which is never reached while playing the scene in the editor:

        [BurstCompile]
        public void OnUpdate(ref SystemState state)
        {
            if (m_GhostType == 0)
            {
                // Lookup the grenade prefab entity in the ghost prefab list, from there we can find the ghost type for this prefab
                var prefabEntity = SystemAPI.GetSingleton<GrenadeSpawner>().Grenade;
                var collectionEntity = SystemAPI.GetSingletonEntity<GhostCollection>();
                var ghostPrefabTypes = state.EntityManager.GetBuffer<GhostCollectionPrefab>(collectionEntity);
                for (int i = 0; i < ghostPrefabTypes.Length; ++i)
                {
                    if (ghostPrefabTypes[i].GhostPrefab == prefabEntity)
                        m_GhostType = ghostPrefabTypes[i].GhostType.GetHashCode();
                }
            }
            m_SnapshotDataLookupHelper.Update(ref state);
            m_PredictedGhostSpawnLookup.Update(ref state);
            m_GrenadeDataLookup.Update(ref state);
            var ghostCollection = SystemAPI.GetSingletonEntity<GhostCollection>();
            var classificationJob = new ClassificationJob
            {
                ghostMap = SystemAPI.GetSingleton<SpawnedGhostEntityMap>().Value,
                snapshotDataLookupHelper = m_SnapshotDataLookupHelper,
                ghostCollectionSingleton = ghostCollection,
                spawnListEntity = SystemAPI.GetSingletonEntity<PredictedGhostSpawnList>(),
                PredictedSpawnListLookup = m_PredictedGhostSpawnLookup,
                grenadeDataLookup = m_GrenadeDataLookup,
                ghostType = m_GhostType
            };
            state.Dependency = classificationJob.Schedule(state.Dependency);
        }

The code in the stuct “ClassificationJob” which depends on the “GhostType” may not work properly then. I only wanted to report the issue. The sample still works despite the issue. I tested with Unity 2022.2.14 and used the samples provided for version 1.0.0-pre.65 of ECS.

Thanks for reporting. Indeed is not correct, since the prefab that is registered with the GhostCollection is not the one in the spawner.

The ghostType is used only for avoiding a classification issue with predicted players. So it does not cause problem for the local player in general.
But indeed this should be fixed! Thanks!

1 Like

@CMarastoni
So, the samples have been updated with release of Unity 2022.3, so I checked the PredictedSpawn sample again. Line 14 of my intial post is indeed executed now, so the m_ghostType is set and provided to the classification job. But lets have a look at the Execute method of the ClassificationJob:

            public void Execute(DynamicBuffer<GhostSpawnBuffer> ghosts, DynamicBuffer<SnapshotDataBuffer> data)
            {
                var predictedSpawnList = PredictedSpawnListLookup[spawnListEntity];
                var snapshotDataLookup = snapshotDataLookupHelper.CreateSnapshotBufferLookup();
                for (int i = 0; i < ghosts.Length; ++i)
                {
                    var newGhostSpawn = ghosts[i];
                    if (newGhostSpawn.SpawnType != GhostSpawnBuffer.Type.Predicted || newGhostSpawn.HasClassifiedPredictedSpawn || newGhostSpawn.PredictedSpawnEntity != Entity.Null)
                        continue;

                    // Mark all the grenade spawns as classified even if not our own predicted spawns
                    // otherwise spawns from other players might be picked up by the default classification system when
                    // it runs when we happen to have a predicted spawn in the predictedSpawnList not yet classified here
                    if (newGhostSpawn.GhostType == ghostType)
                        newGhostSpawn.HasClassifiedPredictedSpawn = true;

                    // Find new ghost spawns (from ghost snapshot) which match the predict spawned ghost type handled by
                    // this classification system. Match the spawn ID data from the new spawn (by lookup it up in
                    // snapshot data) with the spawn IDs of ghosts in the predicted spawn list. When matched we replace
                    // the ghost entity of that new spawn with our predict spawned entity (so the spawn will not result
                    // in a new instantiation).
                    for (int j = 0; j < predictedSpawnList.Length; ++j)
                    {
                        if (newGhostSpawn.GhostType == predictedSpawnList[j].ghostType)
                        {
                            if (snapshotDataLookup.TryGetComponentDataFromSnapshotHistory(newGhostSpawn.GhostType, data, out GrenadeData grenadeData, i))
                            {
                                var spawnIdFromList = grenadeDataLookup[predictedSpawnList[j].entity].SpawnId;
                                if (grenadeData.SpawnId == spawnIdFromList)
                                {
                                    newGhostSpawn.PredictedSpawnEntity = predictedSpawnList[j].entity;
                                    predictedSpawnList[j] = predictedSpawnList[predictedSpawnList.Length - 1];
                                    predictedSpawnList.RemoveAt(predictedSpawnList.Length - 1);
                                    break;
                                }
                            }
                        }
                    }
                    ghosts[i] = newGhostSpawn;
                }
            }

I tested with launching a grenade and the line 15 in the Execute method above gets not called which uses the ghostType. You may correct me, but the ghostType member of the job contains a hashcode and the hashcode does not seem to match the newGhostSpawn.GhostType. The number ranges seem to be quite different. Thats what it looks in the Debugger when launching a grenade:


So it seems to me, that there is still a bug. I use a GhostClassificationSystem in my project which is based on the sample code and only want to ensure that it works as expected.

Edit: Created a bug report in IN-45105

The bug report for IN-45105 was confirmed two months ago, but unfortunately the stated issue tracker link is no more valid:

Can the devs please tell whats the status here.

Hey FaithlessOne! You’re right, it’s very odd that the ECSB-504 issue no longer exists, but the IN-45105 link does. I’ve pinged the relevant QA folk to see what’s going on there. As for status: We’re actually looking at this issue in this sprint, so it should be sorted relatively soon. Thanks for the report about the bug link itself (lol), and for your patience.

1 Like

Please see this forum thread for the quick fix: Classification ghost type in samples seems wrong

1 Like